5

Sto utilizzando un GoogleMap (v2 api) all'interno di un frammento all'interno di un ViewPager. Il problema originale che stavo facendo era che il ViewPager stava acquisendo tutti i movimenti di scorrimento orizzontali in modo che la mappa potesse essere trascinata solo se l'utente iniziava in direzione verticale o diagonale. Tutto il trascinamento orizzontale è stato catturato dallo ViewPager. Ho trovato che la soluzione è creare una classe derivata ViewPager e sovrascrivere il metodo canScroll. La vista trascinata viene passata a questo metodo, quindi puoi prendere una decisione in base al tipo di visualizzazione.Personalizza ViewPager per consentire al controllo GoogleMap figlio di scorrere orizzontalmente

Nel mio caso, il mio frammento è un SupportMapFragment (dal pacchetto com.google.android.gms.maps) che mi fornisce un oggetto GoogleMap con cui interagire. Tuttavia, il controllo effettivo che ricevo nel metodo canScroll di ViewPager è di tipo maps.j.b. Ho risolto il problema con il seguente codice all'interno della mia classe ViewPager personalizzata. Tuttavia, non mi sento a mio agio nel controllare questo nome di classe quando non ne so nulla. Che cos'è maps.j.b? È una classe creata dinamicamente in fase di runtime? È possibile che il tipo cambi in un futuro aggiornamento del framework? Dato solo l'oggetto View, c'è un modo più robusto per verificare che il trascinamento sia stato avviato da un controllo mappa?

@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); 
} 

risposta

3

Si tratta di una classe creata dinamicamente a runtime?

È probabilmente l'output di un rinominare ProGuard.

È possibile che il tipo cambi in un aggiornamento futuro del framework?

AFAIK, sì. Non sono a conoscenza del fatto che ProGuard garantisce che i nomi rimarranno gli stessi tra le build.

Si noti che questo dipenderà dal progetto di libreria. Quando prendi una nuova edizione del progetto della biblioteca, il nome della classe potrebbe cambiare. Ma dal momento che è legato alla tua app, in un colpo solo, puoi attaccare al gioco del nome e devi solo aggiustare il nome quando prendi una nuova edizione del progetto della biblioteca.

Detto questo, sono d'accordo che l'abbinamento basato sul nome sia icky.

Dato solo l'oggetto Visualizza, esiste un metodo più affidabile per verificare che il trascinamento sia stato avviato da un controllo mappa?

Ebbene, dal momento che questo non è un MapView, non abbiamo modo di sapere che cosa esattamente un maps.j.b è. Sappiamo che non è un MapView, poiché non è denominato MapView e Google ha lasciato questo nome da solo (tramite le direttive -keep nella configurazione di ProGuard, presumibilmente) in modo che possiamo utilizzarlo dalle nostre app.

Si potrebbe provare a creare il proprio MapFragment con il proprio MapView e vedere se ci si ottiene un MapView invece di una maps.j.b (che potrebbe essere una sottoclasse interna MapView usata da SupportMapFragment). Se si supera il valore effettivo di MapView, è possibile eseguire controlli di uguaglianza diretta per verificare se l'oggetto passato è lo MapView MapFragment.

Oppure, è possibile vedere se instanceof indica che uno maps.j.b è un MapView.

Né di quelli sono garantiti affidabili nel tempo, sia, come presumibilmente Google potrebbe cambiare questioni in modo che il MapView è avvolto in qualcosa. Tuttavia, suppongo che sia più probabile che sia stabile rispetto al nome della classe generata.

maps.j.b eredita da SurfaceView. Quindi, un modo per rilevare in modo semi-affidabile o meno uno è passata allo canScroll() per verificare se (v instanceof SurfaceView). Ciò non riesce se Maps V2 fa qualcos'altro dopo (ad esempio, estende TextureView su livello API 11+) o se le tue pagine ViewPager hanno un altro SurfaceView (ad esempio, VideoView).

Ho archiviato an issue on this per cercare di ottenere alcuni metodi stabili per aggiungere questa determinazione all'API di Maps V2.

+0

È probabile che si tratti di un output ProGuard poiché i campi di istanza di runtime sono anche tutti nomi di singoli caratteri. Il tipo che ottengo in fase di esecuzione non è disponibile per me in fase di compilazione, quindi non so se sarò in grado di fare qualsiasi cosa con instanceof. E considerando che il nome del tipo che sto usando oggi dipende dalla versione della libreria che sto usando, immagino di poterlo inserire in un test unitario per verificare le modifiche. Continuerò a controllare l'oggetto ed è classe in fase di esecuzione e vedere se riesco a trovare qualcosa di più stabile. Come sempre, grazie per il vostro aiuto e input. – Rich

+0

@Rich: "Il tipo che ottengo al runtime non è disponibile per me in fase di compilazione" - no, ma 'instanceof' funzionerà ancora. Hai solo bisogno di determinare se è un 'MapView' o qualche sottoclasse, e' instanceof' cammina la catena di ereditarietà. – CommonsWare

+0

Esiste un modo per esaminare la catena di ereditarietà in fase di esecuzione (ad esempio, in qualcosa come la finestra Espressioni in Eclipse)?Inoltre, ho pensato ad un approccio ancora più semplice che funzioni poiché la mia mappa riempie tutto lo spazio disponibile. Poiché l'adattatore che alimenta ViewPager dichiara esplicitamente quale frammento va in quale posizione, posso anche controllare la posizione e il vCheck booleano che dovrebbe significare che sono nella foglia della gerarchia della vista (non ci sono più bambini da controllare). Ho appena testato quel metodo e funziona così lontano. – Rich

1

Prima di tutto, @Rich grazie per aver condiviso la tua soluzione - era qualcosa che stavo cercando. Ho un View Pager con un frammento che estende SupportMapfragment che è uno dei suoi elementi. Nella mia soluzione ho un file di layout XML personalizzato poiché avevo bisogno di aggiungere alcuni pulsanti extra al frammento. Ecco la mia soluzione:

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); 
} } 

file di layout Frammento:

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

Il frammento in sé:

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)); 
    } 
} 

Poiché il layout della SupportMapFragment originale è dentro di mia abitudine RelativeLyout è possibile utilizzare l'operatore instanceof e non fare riferimento direttamente alla classe maps.jb ...

2

Spero che questo aiuti un anno dopo ... Nel mio caso ho risolto questo problema ignorando l'oggetto View. Quello che puoi fare è notificare a CustomViewPager in modo che la pagina corrente contenga una mappa.

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); 
    } 
} 

Nel mio caso containsMap() basta controllare la pagina corrente corrisponde ad una pagina indice predefinita:

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

ma potrebbe essere più elaborato: aggiungere un metodo chiamato setCanScrollIndex (int index, canScroll booleano) per mantenere un riferimento a quelle pagine che contengono una mappa. Chiamalo dalla tua attività quando appropriato.

+1

Questo è quello che ho finito facendo - controllando la posizione di getCurrentItem contro un indice predefinito in cui sarebbe la mappa. Mi piace la tua idea di esporre meglio le funzionalità in quanto è un design più riutilizzabile. – Rich