web-dev-qa-db-fra.com

Le mode Google Maps Lite provoque des erreurs dans RecyclerView

J'ai un RecyclerView qui est une liste d'éléments à défilement vertical. Chaque élément de la liste contient une carte Google Maps V2 MapView en Mode simplifié . Je profite de cette nouvelle fonctionnalité qui renvoie des bitmaps au lieu d'une carte complète en remplacement du Google Static Maps API.

MapView nécessite que vous appeliez onCreate(), onResume(), onPause(), onDestroy() etc. depuis l'activité parent/Méthode correspondante du fragment. Où est l'endroit approprié pour les appeler à partir des RecyclerView.Adapter Et/ou RecyclerView.ViewHolder?

Comment puis-je nettoyer les MapViews recyclées afin que la mémoire ne coule pas, tout en gardant la liste libre?

Google dit que le mode Lite peut être utilisé dans les listes:

... option de carte en "mode léger", idéale pour les situations où vous souhaitez fournir un certain nombre de petites cartes, ou une carte si petite qu'une interaction significative n'est pas pratique, comme une vignette dans une liste.

ListItem.xml

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
                Android:layout_width="fill_parent"
                Android:layout_height="fill_parent">

    <com.google.Android.gms.maps.MapView
        Android:id="@+id/mapImageView"
        xmlns:map="http://schemas.Android.com/apk/res-auto"
        Android:layout_width="80dp"
        Android:layout_height="100dp"
        map:liteMode="true"
        map:mapType="normal"
        map:cameraZoom="15"/>

<!-- ... -->

</RelativeLayout>

RecyclerView.Adapter et ViewHolder

public class NearbyStopsAdapter extends RecyclerView.Adapter<NearbyStopsAdapter.ViewHolder> {

    private final Context mContext;

    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        MapView map;

        public ViewHolder(View view) {
            super(view);
            map = (MapView) view.findViewById(R.id.mapImageView);
            // Should this be created here?
            map.onCreate(null);
            map.onResume();
        }
    }

    public NearbyStopsAdapter(Context c) {
        this.mContext = c;
    }

    @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int position) {
        View itemView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item_nearby_stop, viewGroup, false);
        return new ViewHolder(itemView);
    }

    @Override public void onBindViewHolder(ViewHolder holder, int position) {
        //Call Async Map here?
        holder.map.getMapAsync(this);
    }

    @Override public void onViewRecycled(ViewHolder holder) {
        // Cleanup MapView here?
//        if (holder.map != null) {
//            holder.map.onPause();
//            holder.map.onDestroy();
//        }
    }

    @Override public void onViewAttachedToWindow(ViewHolder holder) {
        // Setup MapView here?
//            holder.map.onCreate(null);
//            holder.map.onResume();
    }

    @Override public void onViewDetachedFromWindow(ViewHolder holder) {
        // Cleanup MapView here?
//        if (holder.map != null) {
//            holder.map.onPause();
//            holder.map.onDestroy();
//        }
    }

    // ...
}

Logcat:

I/Google Maps Android API﹕ Google Play services package version: 659943
W/Google Maps Android API﹕ Map Loaded callback is not supported in Lite Mode
W/Google Maps Android API﹕ Buildings are not supported in Lite Mode
W/Google Maps Android API﹕ Indoor is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode
W/Google Maps Android API﹕ Toggling gestures is not supported in Lite Mode

Mise à jour: (8 juin 2018) Google a publié un exemple de code pour utiliser Lite Maps dans un ListView. Voir ici

42
Ryan R

Solution comme suit:

  1. Implémentez OnMapReadyCallback dans la classe ViewHolder.
  2. Dans onMapReady, appelez MapsInitializer.initialize, aux caractéristiques de la garantie peuvent être utilisées avant d'obtenir une carte.

Utilisez cette classe pour initialiser Google Maps Android API si des fonctionnalités doivent être utilisées avant d'obtenir une carte. Elle doit être appelée car certaines classes telles que BitmapDescriptorFactory et CameraUpdateFactory doivent être initialisées.

  1. Recyclez la carte à partir de onViewRecycled.


    public class NearbyStopsAdapter extends RecyclerView.Adapter<NearbyStopsAdapter.ViewHolder> {


       @Override 
       public void onBindViewHolder(ViewHolder holder, int position)  
       {
          //get 'location' by 'position' from data list
          //get GoogleMap
          GoogleMap thisMap = holder.gMap;
          //then move map to 'location'
          if(thisMap != null) 
             //move map to the 'location' 
             thisMap.moveCamera(...);          
       }


       //Recycling GoogleMap for list item
       @Override 
       public void onViewRecycled(ViewHolder holder) 
       {
          // Cleanup MapView here?
          if (holder.gMap != null) 
          {
              holder.gMap.clear();
              holder.gMap.setMapType(GoogleMap.MAP_TYPE_NONE);
          }
       }



       public class ViewHolder extends RecyclerView.ViewHolder implements OnMapReadyCallback { 

           GoogleMap gMap; 
           MapView map;
            ... ... 

           public ViewHolder(View view) {
              super(view);
              map = (MapView) view.findViewById(R.id.mapImageView);

              if (map != null) 
              {
                 map.onCreate(null);
                 map.onResume();
                 map.getMapAsync(this);
              }

          }


          @Override
          public void onMapReady(GoogleMap googleMap) {
              //initialize the Google Maps Android API if features need to be used before obtaining a map 
              MapsInitializer.initialize(getApplicationContext());
              gMap = googleMap;

              //you can move map here to item specific 'location'
              int pos = getPosition();
              //get 'location' by 'pos' from data list  
              //then move to 'location'
              gMap.moveCamera(...);

                  ... ...
         }

       }
    } 
27
Xcihnegn

Google dit:

Lors de l'utilisation de l'API en mode entièrement interactif, les utilisateurs de la classe MapView doivent transmettre toutes les méthodes du cycle de vie des activités aux méthodes correspondantes de la classe MapView. Des exemples de méthodes de cycle de vie incluent onCreate (), onDestroy (), onResume () et onPause ().

Lorsque vous utilisez la classe MapView en mode léger, le transfert des événements du cycle de vie est facultatif, sauf dans les situations suivantes:

Il est obligatoire d'appeler onCreate (), sinon aucune carte n'apparaîtra. Si vous souhaitez afficher le point Ma position sur votre carte en mode léger et utiliser la source de localisation par défaut, vous devrez appeler onResume () et onPause (), car la source de localisation ne se mettra à jour qu'entre ces appels. Si vous utilisez votre propre source de localisation, il n'est pas nécessaire d'appeler ces deux méthodes.

Ainsi, en mode léger, vous n'avez pas à vous soucier de onDestroy (), onResume () et onPause ()

5
bjarocki
3
Chathura Wijesinghe

Google map fournit Lite Mode

Le SDK Maps pour Android peut servir une image bitmap d'une carte, offrant une interactivité limitée à l'utilisateur. Cela s'appelle une carte en mode léger.

Mode Lite

Suivez le LiteListDemoActivity: Displaying maps efficiently in ListViews using lite mode exemple.

2
Kasim Rangwala

J'ai supprimé cette méthode de remplacement car chaque fois que cela donne une carte vide lors des tests et cela fonctionne parfaitement dans ma recyclerView.

@Override 
public void onViewRecycled(ViewHolder holder) 
{
  // Cleanup MapView here?
  if (holder.gMap != null) 
  {
      holder.gMap.clear();
      holder.gMap.setMapType(GoogleMap.MAP_TYPE_NONE);
  }
}

Vous pouvez l'essayer si le code ci-dessus ne fonctionne pas également dans votre cas.

0
Suman Bakhati

Vous devez avoir une classe de détenteur de vue distincte. La classe de l'adaptateur RecyclerView aura juste onCreateViewHolder () et onBindViewHolder ().

Votre fichier de mise en page doit ressembler à ceci:

<RelativeLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
    xmlns:tools="http://schemas.Android.com/tools"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    Android:paddingLeft="@dimen/activity_horizontal_margin"
    Android:paddingRight="@dimen/activity_horizontal_margin"
    Android:paddingTop="@dimen/activity_vertical_margin"
    Android:paddingBottom="@dimen/activity_vertical_margin"
    tools:context=".MyActivity">

    <view
    <com.google.Android.gms.maps.MapView
    Android:id="@+id/mapImageView"
    xmlns:map="http://schemas.Android.com/apk/res-auto"
    Android:layout_width="80dp"
    Android:layout_height="100dp"
    map:liteMode="true"
    map:mapType="normal"
    map:cameraZoom="15" />

</RelativeLayout>

Et onCreate (), onDestroy () sera appelé dans la classe Activity comme d'habitude.

Veuillez suivre ceci tutoriel pour obtenir un aperçu complet.

0
AniV