5

Ich verwende eine GoogleMap (v2 api) innerhalb eines Fragments innerhalb eines ViewPagers. Das ursprüngliche Problem, das ich hatte, war, dass der ViewPager alle horizontalen Scroll-Gesten aufzeichnete, so dass die Karte nur gezogen werden konnte, wenn der Benutzer in vertikaler oder diagonaler Richtung startete. Alle horizontalen Ziehen wurde von der ViewPager erfasst. Ich fand, dass die Lösung darin besteht, eine abgeleitete ViewPager Klasse zu erstellen und die canScroll Methode zu überschreiben. Die Ansicht, die gezogen wird, wird an diese Methode übergeben, sodass Sie eine Entscheidung basierend auf dem Typ der Ansicht treffen können.Benutzerdefinierter ViewPager, damit das untergeordnete GoogleMap-Steuerelement horizontal scrollen kann

In meinem Fall ist mein Fragment ein SupportMapFragment (aus dem com.google.android.gms.maps Paket), die mir ein GoogleMap-Objekt gibt, mit dem ich interagieren kann. Die tatsächliche Steuerung, die ich in der ViewPager-Methode canScroll erhalte, ist jedoch vom Typ maps.j.b. Ich habe das Problem mit dem folgenden Code in meiner benutzerdefinierten ViewPager-Klasse behoben. Ich fühle mich jedoch nicht wohl bei der Suche nach diesem Klassennamen, wenn ich nichts darüber weiß. Was ist maps.j.b? Ist dies zur Laufzeit eine dynamisch erstellte Klasse? Ist es möglich, dass sich der Typ in einem zukünftigen Update des Frameworks ändert? Gibt es nur das View-Objekt, gibt es eine robustere Möglichkeit zu überprüfen, ob der Drag-Vorgang von einem Map-Steuerelement ausgelöst wurde?

@Override 
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { 
    if(v.getClass().getName().equals("maps.j.b")) { 
     return true; 
    } 
    return super.canScroll(v, checkV, dx, x, y); 
} 

Antwort

3

Ist das eine zur Laufzeit dynamisch erstellt Klasse?

Es ist wahrscheinlich die Ausgabe eines ProGuard umzubenennen.

Kann der Typ in einem zukünftigen Update des Frameworks geändert werden?

AFAIK, ja. Mir ist nicht bekannt, dass ProGuard garantiert, dass die Namen zwischen den Builds gleich bleiben.

Beachten Sie, dass dies vom Bibliotheksprojekt abhängig ist. Wenn Sie eine neue Edition des Bibliotheksprojekts übernehmen, kann sich der Klassenname ändern. Aber da dies an Ihre eigene App gebunden ist, könnten Sie im Notfall das Namensspiel behalten und den Namen nur korrigieren, wenn Sie eine neue Edition des Bibliotheksprojekts übernehmen.

Das gesagt, ich stimme zu, dass Übereinstimmung basierend auf Name icky ist.

Gibt es nur das View-Objekt, gibt es eine robustere Möglichkeit zu überprüfen, dass der Drag durch ein Map-Steuerelement ausgelöst wurde?

Nun, da dies kein MapView ist, haben wir keine Möglichkeit zu wissen, was genau ein maps.j.b ist. Wir wissen, dass es kein ist, da es nicht genannt wird, und Google diesen Namen allein gelassen hat (über -keep Richtlinien in der ProGuard Konfiguration vermutlich), also können wir es von unseren apps benutzen.

Sie könnten versuchen, Ihren eigenen MapFragment mit Ihrem eigenen MapView und sehen die Schaffung dort, wenn Sie einen MapView anstelle eines maps.j.b (das könnte einige interne Unterklasse von MapView von SupportMapFragment verwendet werden) erhalten. Wenn Sie die tatsächliche übergeben werden, können Sie direkte Gleichheitsüberprüfungen durchführen, um festzustellen, ob das übergebene Objekt MapFragment ist.

Oder Sie könnten sehen, wenn instanceof sagt, dass ein maps.j.b ein ist.

Keiner von denen sind zuverlässig im Laufe der Zeit auch, wie denkbar, Google könnte Dinge ändern, so dass die ist etwas eingewickelt. Ich vermute jedoch, dass es stabiler ist als der generierte Klassenname.

maps.j.b erbt von SurfaceView. Daher ist eine Möglichkeit, halbzuverlässig zu erkennen, ob ein View, der in canScroll() übergeben wurde, zu testen ist, ob (v instanceof SurfaceView). Dies schlägt fehl, wenn Maps V2 später etwas anderes tut (z. B. TextureView auf API-Ebene 11+ erweitert) oder wenn Ihre ViewPager Seiten eine andere SurfaceView (z. B. eine VideoView) haben.

Ich habe an issue on this eingereicht, um zu versuchen, einige stabile Mittel zu bekommen, um diese Bestimmung der Maps V2 API hinzuzufügen.

+0

Sie haben höchstwahrscheinlich Recht damit, dass es sich um eine ProGuard-Ausgabe handelt, da die Felder der Laufzeitinstanz ebenfalls alle aus einem Zeichen bestehen. Der Typ, den ich zur Laufzeit bekomme, steht mir zur Kompilierzeit nicht zur Verfügung, also weiß ich nicht, ob ich mit instanceof etwas anfangen kann. Und wenn man bedenkt, dass der Typname, den ich heute verwende, von der Version der Bibliothek abhängt, die ich verwende, kann ich mir vorstellen, dass ich dies in einen Komponententest verpacken kann, um nach Änderungen zu suchen. Ich werde das Objekt und seine Klasse zur Laufzeit überprüfen und schauen, ob ich etwas Stabileres finden kann. Wie immer, danke für Ihre Hilfe und Input. – Rich

+0

@Rich: "Der Typ, den ich zur Laufzeit bekomme, steht mir zur Kompilierzeit nicht zur Verfügung" - nein, aber 'instanceof' funktioniert immer noch. Sie müssen nur feststellen, ob es sich um eine 'MapView' oder eine Unterklasse handelt, und 'instanceof' durchläuft die Vererbungskette. – CommonsWare

+0

Gibt es eine Möglichkeit für mich, die Vererbungskette zur Laufzeit zu betrachten (z. B. in dem Fenster "Ausdrücke" in Eclipse)?Außerdem dachte ich an einen noch einfacheren Ansatz, der funktioniert, da meine Karte den gesamten verfügbaren Platz ausfüllt. Da der Adapter, der den ViewPager einspeist, explizit angibt, welches Fragment in welche Position kommt, kann ich auch einfach nach der Position und dem vCheck-Booleschen Wert suchen, was bedeutet, dass ich mich am Blatt der Ansichtshierarchie befinde (keine Kinder mehr zu prüfen). Ich habe gerade diese Methode getestet und es funktioniert so weit. – Rich

1

Zunächst einmal, @Rich danke für die gemeinsame Nutzung Ihrer Lösung - es war etwas, das ich gesucht habe. Ich habe einen View Pager mit einem Fragment, das SupportMapfragment erweitert, das eines seiner Elemente ist. In meiner Lösung habe ich eine benutzerdefinierte XML-Layoutdatei, da ich dem Fragment einige zusätzliche Schaltflächen hinzufügen musste. Hier ist meine Lösung:

public class MapFragmentScrollOverrideViewPager extends ViewPager { 

public MapFragmentScrollOverrideViewPager(Context context) { 
    super(context); 
} 

public MapFragmentScrollOverrideViewPager(Context context, AttributeSet attrs) { 
    super(context, attrs); 
} 

@Override 
protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { 
    if(v instanceof MapHoldingRelativeLayout) { 
     return true; 
    } 
    return super.canScroll(v, checkV, dx, x, y); 
} } 

public class MapHoldingRelativeLayout extends RelativeLayout { 

public MapHoldingRelativeLayout(Context context) { 
    super(context); 
} 

public MapHoldingRelativeLayout(Context context, AttributeSet attrs) { 
    super(context, attrs); 
} 

public MapHoldingRelativeLayout(Context context, AttributeSet attrs, int defStyle) { 
    super(context, attrs, defStyle); 
} } 

Fragment Layout-Datei:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout 
    android:layout_width="match_parent" 
    android:layout_height="match_parent" 
    xmlns:android="http://schemas.android.com/apk/res/android"> 

    <Button 
     android:id="@+id/switchNearbyOffersButton" 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:text="XXXX" 
     /> 

    <xxx.view.MapHoldingRelativeLayout 
     android:id="@+id/mapLayout" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:layout_below="@id/switchNearbyOffersButton" 
     /> 

</RelativeLayout> 

Das Fragment selbst:

public final class NearbyOffersFragment extends SupportMapFragment { 

    public static NearbyOffersFragment newInstance(String content) { 
     NearbyOffersFragment fragment = new NearbyOffersFragment(); 
     return fragment; 
    } 

    private GoogleMap googleMap; 

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

    private void setUpMapIfNeeded() { 
     if (googleMap == null) { 
      googleMap = getMap(); 
      if (googleMap != null) { 
       setUpMap(); 
      } 
     } 
    } 

    @Override 
    public View onCreateView(LayoutInflater inflater, ViewGroup container, 
      Bundle savedInstanceState) { 

     final View rootView = inflater.inflate(R.layout.fragment_nearby_offers, 
       container, false); 
     final View mapFragmentLayout = super.onCreateView(inflater, container, 
       savedInstanceState); 
     MapHoldingRelativeLayout mapLayout = (MapHoldingRelativeLayout) rootView 
       .findViewById(R.id.mapLayout); 
     mapLayout.addView(mapFragmentLayout); 
     return rootView; 
    } 
    @Override 
    public void onResume() { 
     super.onResume(); 
     setUpMapIfNeeded(); 
    } 

    @Override 
    public void onSaveInstanceState(Bundle outState) { 
     super.onSaveInstanceState(outState); 
    } 

    private void setUpMap() { 
     googleMap.setMapType(GoogleMap.MAP_TYPE_HYBRID); 
     googleMap.getUiSettings().setZoomControlsEnabled(true); 
     googleMap.getUiSettings().setAllGesturesEnabled(true); 
     CameraPosition cameraPosition = new CameraPosition.Builder() 
       .target(new LatLng(41.474856, -88.056928)) 
       .zoom(14.5f) 
       .build(); 
     googleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cameraPosition)); 
    } 
} 

seit der ursprünglichen SupportMapFragment Layout Innenseite meiner Gewohnheit ist RelativeLyout Sie den instanceof-Operator verwenden können und verweisen Sie nicht direkt auf die Klasse maps.jb ...

2

Hoffe das hilft ein Jahr später ... In meinem Fall habe ich dieses Problem gelöst, indem ich das View-Objekt ignoriert habe. Sie können den CustomViewPager benachrichtigen, sodass die aktuelle Seite eine Karte enthält.

public class CustomViewPager extends ViewPager { 
    @Override 
    protected boolean canScroll(View v, boolean checkV, int dx, int x, int y) { 
     if (this.containsMap()) { 
      return true; 
     } 
     return super.canScroll(v, checkV, dx, x, y); 
    } 
} 

In meinem Fall containsMap() überprüfen nur die aktuelle Seite zu einer vordefinierten Seitenindex entspricht:

public boolean containsMap(){ 
    return getCurrentItem() == MAP_PAGE; 
} 

Aber es könnte sein, aufwendigere: Fügen Sie eine Methode setCanScrollIndex (int index, boolean canScroll genannt) um einen Verweis auf jene Seiten zu behalten, die eine Karte enthalten. Nennen Sie es bei Bedarf aus Ihrer Aktivität.

+1

Das ist, was ich getan habe - die Position von getCurrentItem gegen einen vordefinierten Index zu überprüfen, wo die Karte sein würde. Ich mag Ihre Idee, die Funktionalität besser darzustellen, da es sich um ein wiederverwendbares Design handelt. – Rich