MapView we fragmencie (plaster miodu)


81

teraz, gdy ostateczny pakiet SDK jest dostępny w Google API - jaki jest najlepszy sposób na utworzenie fragmentu z MapView? MapView potrzebuje MapActivity do prawidłowego działania.

Posiadanie działania zarządzającego fragmentami dziedziczone po MapActivity (złe rozwiązanie, ponieważ jest to sprzeczne z ideą, że fragmenty są samodzielne) i używanie zwykłego układu opartego na XML nie działa. Otrzymuję NullPointerException w MapActivity.setupMapView ():

E / AndroidRuntime (597): spowodowane przez: java.lang.NullPointerException
E / AndroidRuntime (597): w com.google.android.maps.MapActivity.setupMapView (MapActivity.java:400)
E / AndroidRuntime (597): w com.google.android.maps.MapView. (MapView.java:289)
E / AndroidRuntime (597): w com.google.android.maps.MapView. (MapView.java:264)
E / AndroidRuntime (597): w com.google.android.maps.MapView. (MapView.java:247)

Moim drugim pomysłem było programowe utworzenie MapView i przekazanie skojarzonego z nim działania (poprzez getActivity ()) jako Context do konstruktora MapView. Nie działa:

E / AndroidRuntime (834): spowodowane przez: java.lang.IllegalArgumentException: MapViews można tworzyć tylko wewnątrz instancji MapActivity.
E / AndroidRuntime (834): w com.google.android.maps.MapView. (MapView.java:291)
E / AndroidRuntime (834): w com.google.android.maps.MapView. (MapView.java:235)
E / AndroidRuntime (834): w de.foo.FinderMapFragment.onCreateView (FinderMapFragment.java:225)
E / AndroidRuntime (834): w android.app.FragmentManagerImpl.moveToState (FragmentManager.java:708)
E / AndroidRuntime (834): na android.app.FragmentManagerImpl.moveToState (FragmentManager.java:900)
E / AndroidRuntime (834): w android.app.FragmentManagerImpl.addFragment (FragmentManager.java:978)
E / AndroidRuntime (834): w android.app.Activity.onCreateView (Activity.java:4090)
E / AndroidRuntime (834): w android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:664)

Naprawdę powinno istnieć coś takiego jak MapFragment, które zajmie się wątkami w tle, których MapView potrzebuje, tak myślę ... Więc jaka jest obecnie najlepsza praktyka, aby to zrobić?

Dzięki i pozdrowienia z Niemiec Valentin


2
Zgłosiłem prośbę o dodanie funkcji. Proszę, zaznacz to. code.google.com/p/android/issues/detail?id=15347
— Jeremy Edwards

Odpowiedzi:


8

Od 03.12.2012 Google udostępnił Google Maps Android API v2 . Teraz możesz zapomnieć o tych problemach. https://developers.google.com/maps/documentation/android/

Przykład z użyciem nowego API - https://developers.google.com/maps/documentation/android/start#add_a_map

Ten interfejs API będzie działał przynajmniej na Android API 8, więc używaj go;).

Teraz możesz po prostu użyć klasy fragmentu „com.google.android.gms.maps.MapFragment”. Wyświetli mapę w Twojej aktywności. Przykład układu z linku powyżej:

<?xml version="1.0" encoding="utf-8"?>
<fragment xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/map"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    class="com.google.android.gms.maps.MapFragment"/>

87

Udało mi się rozwiązać ten problem, używając fragmentu TabHost.

Oto pomysł (krótko):

  1. MainFragmentActivityrozszerza FragmentActivity(z biblioteki wsparcia) i ma MapFragment.

  2. MyMapActivityrozszerza MapActivityi zawiera MapView.

  3. LocalActivityManagerFragment zastępy niebieskie LocalActivityManager

  4. MapFragmentrozciąga się LocalActivityManagerFragment.

  5. I LocalActivityManagerzawiera w sobie MyMapActivityaktywność.

Przykładowa implementacja: https://github.com/inazaruk/map-fragment .


wprowadź opis obrazu tutaj


4
Jeśli następnie chcesz, aby MapActivity mógł komunikować się z działaniem nadrzędnym (co jest łatwe w przypadku fragmentu), możesz użyć Activity.getParent ()
— Dori

3
Naprawdę nie musisz używać TabHost, możesz także po prostu użyć LocalActivityManager i dołączyć zwrócone okno do zawartości swojego fragmentu
— ChristophK,

4
Na wypadek, gdyby ktoś inny się zastanawiał, oto kod:Intent i = new Intent(getActivity().getParent(), MyMapActivity.class); Window w = localActivityManager.startActivity("tag", i); currentView=w.getDecorView(); currentView.setVisibility(View.VISIBLE); currentView.setFocusableInTouchMode(true); ((ViewGroup) currentView).setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS); this.contentView.addView(currentView);
— ChristophK

8
uważaj, LocalActivityManager jest teraz przestarzały
— louiscoquio

4
U mnie działa dobrze, używając zakładek i fragmentów. Dla pytających o wyjaśnienie wolałbym zobaczyć projekt i jego kod, jest łatwy do zrozumienia. I tak, LocalActivityManagerjest przestarzały, ale to tylko hack, ten problem zostanie rozwiązany poprawnie, gdy zdecydują się uwzględnić odpowiednie wsparcie dla map w fragmentach.
— mdelolmo

23

Jak omówiono w Grupach dyskusyjnych Google , Peter Doyle stworzył własną bibliotekę kompatybilności obsługującą również Mapy Google. android-support-v4-googlemaps

Jednak jest też wada:

Obecnie jedną wadą jest to, że WSZYSTKIE klasy rozszerzające FragmentActivity to MapActivitys. Możliwe jest utworzenie oddzielnej klasy (np. FragmentMapActivity), ale wymaga to pewnego refaktoryzacji kodu FragmentActivity.


6
Oznacza to również, że Twoja aplikacja nie będzie aktualna z najnowszą biblioteką pomocy. Chociaż Android-support-v4-googlemaps był aktualizowany wiele razy, obecnie ma 2 wersje za nim. Zgadzam się z innymi, że ta zmodyfikowana biblioteka jest większym hackem niż użycie przestarzałego LocalActivityManager.
— Stan Kurdziel

1
Strona github.com/petedoyle/android-support-v4-googlemaps jest teraz aktualna (r10 od tej wiadomości). Przebudowałem też rzeczy, więc w przyszłości będzie bardzo łatwo być na bieżąco.
— Pete Doyle,

Mam jedno pytanie. Jeśli aplikacja jest już wdrożona i zainstalowana, jaka jest wada „braku aktualności z najnowszą biblioteką wsparcia”?
— hendrix

Podstawowym problemem związanym z tym podejściem, IMO, jest to, że nie będzie można wdrożyć aplikacji z opcjonalną obsługą Map. Ponieważ wszystkie FragmentActivities rozszerzają MapActivity, po prostu ulegnie awarii na urządzeniach bez interfejsów API Google (np. Kindle Fire).
— Paul Lammertsma

13

Tylko po to, żeby wyjaśnić odpowiedź. Wypróbowałem podejście sugerowane przez inazaruka i ChristophK. Właściwie możesz uruchomić dowolną aktywność we fragmencie - nie tylko mapy Google. Oto kod, który implementuje aktywność mapy google jako fragment dzięki inazaruk i ChristophK.

import com.actionbarsherlock.app.SherlockFragment;
import android.view.Window;

import android.app.LocalActivityManager;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

public class MapFragment extends SherlockFragment {
    private static final String KEY_STATE_BUNDLE = "localActivityManagerState";

    private LocalActivityManager mLocalActivityManager;

    protected LocalActivityManager getLocalActivityManager() {
        return mLocalActivityManager;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Bundle state = null;
        if (savedInstanceState != null) {
            state = savedInstanceState.getBundle(KEY_STATE_BUNDLE);
        }

        mLocalActivityManager = new LocalActivityManager(getActivity(), true);
        mLocalActivityManager.dispatchCreate(state);
    }

    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
            //This is where you specify you activity class
        Intent i = new Intent(getActivity(), GMapActivity.class); 
        Window w = mLocalActivityManager.startActivity("tag", i); 
        View currentView=w.getDecorView(); 
        currentView.setVisibility(View.VISIBLE); 
        currentView.setFocusableInTouchMode(true); 
        ((ViewGroup) currentView).setDescendantFocusability(ViewGroup.FOCUS_AFTER_DESCENDANTS);
        return currentView;
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putBundle(KEY_STATE_BUNDLE,
                mLocalActivityManager.saveInstanceState());
    }

    @Override
    public void onResume() {
        super.onResume();
        mLocalActivityManager.dispatchResume();
    }

    @Override
    public void onPause() {
        super.onPause();
        mLocalActivityManager.dispatchPause(getActivity().isFinishing());
    }

    @Override
    public void onStop() {
        super.onStop();
        mLocalActivityManager.dispatchStop();
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        mLocalActivityManager.dispatchDestroy(getActivity().isFinishing());
    }
}

2
Ale jedyne rozwiązanie, które pozwala uniknąć LocalActivityManager(jak omówiono tutaj: code.google.com/p/android/issues/detail?id=15347 ), polega na rozszerzeniu wszystkich FragmentActivities , co wydaje się jeszcze mniej optymalne.
— Estel

LocalActivityManager jest bardziej przestarzały, ponieważ ActivityGroup jest przestarzały, nadal jest przydatny w tej sytuacji i być może Google zastanowi się dwa razy nad zastąpieniem go w przyszłych wersjach, jeśli ludzie będą go używać do rozwiązywania swoich niedociągnięć.
— straya

4

Wspaniałe wiadomości na ten temat od Google. Wydają dziś nowy interfejs API Map Google z mapami obiektów i MapFragment.

With this new API, adding a map to your Activity is as simple as:

<fragment
  android:id="@+id/map"
  android:layout_width="match_parent"
  android:layout_height="match_parent"
  class="com.google.android.gms.maps.MapFragment" />

Nie dokładnie. Spróbuj użyć tego w FragmentPagerAdapter.
— Thomas Dignan

Jeśli mówisz o czarnym widoku, jest mały hack, aby to naprawić stackoverflow.com/questions/13837697/ ...
— Marcelo

dzięki. Skończyło się na tym, że zobaczyłem to później, ale teraz używam pierwszej wersji api mapy z polaris.
— Thomas Dignan

2

Interfejs API Map Google nie jest częścią AOSP. Dopóki żaden pracownik Google nie odpowie, trudno będzie stwierdzić, czy w przyszłości pojawi się MapFragment.

Możliwą ograniczoną alternatywą jest użycie WebViewFragmenti nadużywanie go w celu załadowania niestandardowego maps.google.comadresu URL.


1
Mamy nadzieję, że API Map zostanie wkrótce zaktualizowane o fragmenty i rysunki wektorowe!
— Nathan Schwermann

2

Hm szkoda, że ​​Google jeszcze nie odpowiedział. FWIW, jeśli naprawdę musisz to zrobić, nie znalazłem innego sposobu niż:

Niech działanie zarządzania kartami dziedziczy z MapActivity, utwórz tam MapView programowo, niech mapfragment.xml zawiera ViewGroup i dodaj MapView do ViewGroup używając

((ViewGroup) getFragmentManager (). FindFragmentById (R.id.finder_map_fragment) .getView ()). AddView (mapView) ;;

Jest to oczywiście sprzeczne z ideą, że fragmenty mają być samowystarczalne, ale ...


Dzięki, rozszerzenie działania zarządzania fragmentem MapActivity działało idealnie.
— Pete Doyle,

2

Oto wersja MonoDroid ( Mono dla Androida ) bardzo uproszczonej MapFragment :

public class MapFragment : Fragment
{
    // FOLLOW http://stackoverflow.com/questions/5109336/mapview-in-a-fragment-honeycomb
    private static  String KEY_STATE_BUNDLE = "localActivityManagerState";

    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        Bundle state = null;
        if (savedInstanceState != null) {
            state = savedInstanceState.GetBundle(KEY_STATE_BUNDLE);
        }
        mLocalActivityManager = new LocalActivityManager(Activity, true);
        mLocalActivityManager.DispatchCreate(state);
    }

    public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        //This is where you specify you activity class
        Intent i = new Intent(Activity, typeof(SteamLocationMapActivity)); 
        Window w = mLocalActivityManager.StartActivity("tag", i); 
        View currentView=w.DecorView; 
        currentView.Visibility = ViewStates.Visible; 
        currentView.FocusableInTouchMode = true; 
        ((ViewGroup) currentView).DescendantFocusability = DescendantFocusability.AfterDescendants;
        return currentView;
    }

    private LocalActivityManager mLocalActivityManager;
    protected LocalActivityManager GetLocalActivityManager() {
        return mLocalActivityManager;
    }   


    public override void OnSaveInstanceState(Bundle outState)
    {
        base.OnSaveInstanceState(outState);
        outState.PutBundle(KEY_STATE_BUNDLE,mLocalActivityManager.SaveInstanceState());
    }

    public override void OnResume()
    {
        base.OnResume();
        mLocalActivityManager.DispatchResume();

    }

    public override void OnPause()
    {
        base.OnPause();
        mLocalActivityManager.DispatchPause(Activity.IsFinishing);
    }

    public override void OnStop()
    {
        base.OnStop();
        mLocalActivityManager.DispatchStop();
    }
}



1

Czy mogę uzyskać rozwiązanie:

  1. utwórz klasę TempFragmentActivity rozszerza MapActivity
  2. w TempFragmentActivity znajduje się obiekt MapView (jak normalnie zdefiniowano w xml)
  3. usuń ten obiekt MapView z rodzica (LinearLayout) (unieważniony później wyjątek)
  4. trzymaj ten obiekt MapView gdzieś (np. statyczny element członkowski TempFragmentActivity)
  5. w swoim fragmencie dodaj ten obiekt MapView przy użyciu kodu (nie definiuj w xml) do jakiegoś LinearLayout

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.