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

Valentin
źródło
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"/>
Paul Annekov
źródło
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

inazaruk
źródło
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.

TomTasche
źródło
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());
    }
}
zmicer
źródło
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" />
Marcelo
źródło
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.

Octavian A. Damiean
źródło
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 ...

Valentin
źródło
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();
    }
}
Ian Vink
źródło
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
Joe Zhang
źródło
0

Napisałem małą bibliotekę, łącząc rozwiązania oparte na LocalActivityManager z problemem MapFragment (zawiera również przykładową aplikację pokazującą różne sytuacje użycia):

https://github.com/coreform/android-tandemactivities

straya
źródło