MapView in einem Fragment (Honeycomb)


81

Was ist der beste Weg, um ein Fragment mit einer MapView zu erstellen, nachdem das endgültige SDK mit Google Apis veröffentlicht wurde? MapView benötigt eine MapActivity, um richtig zu funktionieren.

Es funktioniert nicht, wenn die Aktivität, die die Fragmente verwaltet, von MapActivity erbt (schlechte Lösung, da dies gegen die Idee verstößt, dass Fragmente in sich geschlossen sind) und ein reguläres XML-basiertes Layout verwendet. Ich erhalte eine NullPointerException in MapActivity.setupMapView ():

E / AndroidRuntime (597): Auslöser: java.lang.NullPointerException
E / AndroidRuntime (597): unter com.google.android.maps.MapActivity.setupMapView (MapActivity.java:400)
E / AndroidRuntime (597): unter com.google.android.maps.MapView. (MapView.java:289)
E / AndroidRuntime (597): unter com.google.android.maps.MapView. (MapView.java:264)
E / AndroidRuntime (597): unter com.google.android.maps.MapView. (MapView.java:247)

Meine zweite Idee war, MapView programmgesteuert zu erstellen und die zugehörige Aktivität (über getActivity ()) als Kontext an den MapView-Konstruktor zu übergeben. Funktioniert nicht:

E / AndroidRuntime (834): Auslöser: java.lang.IllegalArgumentException: MapViews können nur in Instanzen von MapActivity erstellt werden.
E / AndroidRuntime (834): unter com.google.android.maps.MapView. (MapView.java:291)
E / AndroidRuntime (834): unter com.google.android.maps.MapView. (MapView.java:235)
E / AndroidRuntime (834): at de.foo.FinderMapFragment.onCreateView (FinderMapFragment.java:225)
E / AndroidRuntime (834): at android.app.FragmentManagerImpl.moveToState (FragmentManager.java:708)
E / AndroidRuntime (834): at android.app.FragmentManagerImpl.moveToState (FragmentManager.java:900)
E / AndroidRuntime (834): at android.app.FragmentManagerImpl.addFragment (FragmentManager.java:978)
E / AndroidRuntime (834): at android.app.Activity.onCreateView (Activity.java:4090)
E / AndroidRuntime (834): at android.view.LayoutInflater.createViewFromTag (LayoutInflater.java:664)

Eigentlich sollte es so etwas wie MapFragment geben, das sich um die Hintergrundthreads kümmert, die MapView benötigt, denke ich ... Also, was ist die aktuelle Best Practice, um dies zu tun?

Danke und Grüße aus Deutschland, Valentin


2
Ich habe eine Feature-Anfrage dafür gemeldet. Bitte starte es. code.google.com/p/android/issues/detail?id=15347
Jeremy Edwards

Antworten:


8

Ab dem 03.12.2012 hat Google die Google Maps Android API v2 veröffentlicht . Jetzt können Sie diese Probleme vergessen. https://developers.google.com/maps/documentation/android/

Beispiel für die Verwendung einer neuen API - https://developers.google.com/maps/documentation/android/start#add_a_map

Diese API funktioniert mindestens für Android API 8, verwenden Sie sie also;).

Jetzt können Sie einfach die Fragmentklasse "com.google.android.gms.maps.MapFragment" verwenden. Die Karte wird in Ihrer Aktivität angezeigt. Layoutbeispiel über den obigen Link:

<?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

Ich habe es geschafft, dieses Problem mithilfe von TabHost in Fragmenten zu beheben.

Hier ist die Idee (kurz):

  1. MainFragmentActivityerweitert FragmentActivity(aus Support-Bibliothek) und hat MapFragment.

  2. MyMapActivityerweitert MapActivityund enthält MapView.

  3. LocalActivityManagerFragment Gastgeber LocalActivityManager

  4. MapFragmenterstreckt sich LocalActivityManagerFragment.

  5. Und LocalActivityManagerenthält MyMapActivityAktivität darin.

Beispielimplementierung: https://github.com/inazaruk/map-fragment .


Geben Sie hier die Bildbeschreibung ein


4
Wenn Sie dann möchten, dass die MapActivity mit der übergeordneten Aktivität kommunizieren kann (was mit einem Fragment einfach ist), können Sie Activity.getParent ()
Dori

3
Sie müssen nicht unbedingt einen TabHost verwenden, sondern können auch einfach einen LocalActivityManager verwenden und das zurückgegebene Fenster an den Inhalt Ihres Fragments anhängen
ChristophK

4
Für den Fall, dass sich jemand anderes wundert, hier ist der Code: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
Seien Sie vorsichtig, der LocalActivityManager ist jetzt veraltet
louiscoquio

4
Es funktioniert gut für mich, Tabs und Fragmente zu verwenden. Für diejenigen, die nach der Erklärung fragen, würde ich lieber das Projekt und seinen Code sehen, es ist leicht zu verstehen. Und ja, LocalActivityManagerist veraltet, aber dies ist nur ein Hack, dieses Problem soll richtig gelöst werden, wenn sie beschließen, die entsprechende Unterstützung für Karten in Fragmente aufzunehmen.
mdelolmo

23

Wie bei Google Groups erläutert , hat Peter Doyle eine benutzerdefinierte Kompatibilitätsbibliothek erstellt, die auch Google Maps unterstützt. android-support-v4-googlemaps

Es gibt jedoch auch einen Nachteil:

Derzeit besteht ein Nachteil darin, dass ALLE Klassen, die FragmentActivity erweitern, MapActivitys sind. Es ist möglich, eine separate Klasse (z. B. FragmentMapActivity) zu erstellen, es ist jedoch eine Umgestaltung des FragmentActivity-Codes erforderlich.


6
Dies bedeutet auch, dass Ihre App nicht mit der neuesten Support-Bibliothek auf dem neuesten Stand ist. Während die android-support-v4-googlemaps mehrmals aktualisiert wurden, liegen derzeit 2 Versionen dahinter. Ich stimme anderen zu, dass diese modifizierte Bibliothek ein größerer Hack ist als die Verwendung des veralteten LocalActivityManager.
Stan Kurdziel

1
github.com/petedoyle/android-support-v4-googlemaps ist jetzt auf dem neuesten Stand (r10 ab dieser Nachricht). Ich habe auch Dinge überarbeitet, damit es in Zukunft sehr einfach ist, auf dem neuesten Stand zu bleiben.
Pete Doyle

Ich habe eine Frage. Wenn die Anwendung bereits bereitgestellt und installiert ist, was ist der Nachteil, wenn Sie nicht mit der neuesten Support-Bibliothek auf dem neuesten Stand sind?
Hendrix

Das Hauptproblem bei diesem Ansatz, IMO, besteht darin, dass Sie Ihre App nicht mit optionaler Kartenunterstützung bereitstellen können. Da alle FragmentActivities MapActivity erweitern, stürzt es einfach auf Geräten ohne Google-APIs (z. B. Kindle Fire) ab.
Paul Lammertsma

13

Nur um die Antwort zu klären. Ich habe den von inazaruk und ChristophK vorgeschlagenen Ansatz ausprobiert. Tatsächlich können Sie jede Aktivität in einem Fragment ausführen - nicht nur Google Maps. Hier ist der Code, der dank inazaruk und ChristophK die Google Map-Aktivität als Fragment implementiert.

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
Die einzige Lösung, die dies vermeidet LocalActivityManager(wie hier erläutert : code.google.com/p/android/issues/detail?id=15347 ), besteht darin, alle zu erweitern FragmentActivities , was noch weniger optimal erscheint.
Estel

LocalActivityManager ist veralteter, da ActivityGroup veraltet ist, was in dieser Situation immer noch nützlich ist. Möglicherweise wird Google zweimal darüber nachdenken, es in zukünftigen Versionen zu verdrängen, wenn Benutzer es verwenden, um ihre Mängel zu beheben.
Straya

4

Tolle Neuigkeiten von Google dazu. Sie veröffentlichen heute eine neue Google Maps-API mit Indoor-Karten und 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" />

Nicht genau. Versuchen Sie das in einem FragmentPagerAdapter.
Thomas Dignan

Wenn Sie über die schwarze Ansicht sprechen, gibt es einen kleinen Hack, um sie zu beheben stackoverflow.com/questions/13837697/…
Marcelo

Vielen Dank. Ich habe das später gesehen, aber jetzt verwende ich die erste Karten-API-Version mit Polaris.
Thomas Dignan

2

Die Google Maps-API ist nicht Teil des AOSP. Solange kein Googler antwortet, ist es kaum möglich zu sagen, ob es in Zukunft ein MapFragment geben wird.

Eine mögliche eingeschränkte Alternative besteht darin, a zu verwenden WebViewFragmentund es zu missbrauchen, um eine benutzerdefinierte maps.google.comURL zu laden .


1
Wir hoffen, dass die Map-API bald mit Fragmenten und der Vektorzeichnung aktualisiert wird!
Nathan Schwermann

2

Schade, dass Google noch nicht geantwortet hat. FWIW, wenn Sie dies wirklich tun müssen, habe ich keinen anderen Weg gefunden als:

Lassen Sie die Registerkarte "Aktivität verwalten" von MapActivity erben, erstellen Sie die MapView dort programmgesteuert, lassen Sie die mapfragment.xml eine ViewGroup enthalten und fügen Sie die MapView der ViewGroup hinzu

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

Dies widerspricht eindeutig der Vorstellung, dass Fragmente in sich geschlossen sein sollen, aber ...


Vielen Dank, dass die Erweiterung der Fragmentverwaltungsaktivität MapActivity für mich perfekt funktioniert hat.
Pete Doyle

2

Hier ist eine MonoDroid- Version ( Mono für Android ) eines sehr vereinfachten MapFragments :

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

Darf ich die Lösung bekommen:

  1. Klasse erstellen TempFragmentActivity erweitert MapActivity
  2. In TempFragmentActivity befindet sich ein MapView-Objekt (wie in XML definiert).
  3. Entfernen Sie dieses übergeordnete MapView-Objektformular (LinearLayout) (spätere Ausnahme ungültig machen).
  4. Bewahren Sie dieses MapView-Objekt irgendwo auf (z. B. statisches Mitglied von TempFragmentActivity).
  5. Fügen Sie in Ihrem Fragment dieses MapView-Objekt mithilfe von Code (nicht in XML definiert) in ein LinearLayout ein

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.