Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Google Maps Lite Mode causes jank in RecyclerView

I have a RecyclerView which is a vertical scrolling list of items. Each list item contains a Google Maps V2 MapView in Lite Mode. I'm taking advantage of this new feature which returns bitmaps instead of a full-blown map as a replacement to the Google Static Maps API.

MapView requires that you call onCreate(), onResume(), onPause(), onDestroy() etc. from the parent Activity/Fragment's corresponding method. Where is the proper place to call these from the RecyclerView.Adapter and/or RecyclerView.ViewHolder?

How can I clean up recycled MapViews so that memory doesn't leak, while keeping the list jank free?

Google says Lite Mode can be used in lists:

... ‘lite mode’ map option, ideal for situations where you want to provide a number of smaller maps, or a map that is so small that meaningful interaction is impractical, such as a thumbnail in a list.

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

Update: (Jun 8, 2018) Google has released a code sample for using Lite Maps in a ListView. See here

like image 672
Ryan R Avatar asked Feb 19 '15 17:02

Ryan R


People also ask

How do I get out of Google Maps Lite mode?

The modification to the main Maps layout can be found in the bottom right-hand corner of your browser window, highlighted by a lightning icon. To turn Lite mode either on or off, simply flip this switch, and a prompt will confirm whether or not the process was successful.

What is Lite mode in Google Maps?

The Maps SDK for Android can serve a bitmap image of a map, offering limited interactivity to the user. This is called a lite mode map.


1 Answers

Solution as following:

  1. Implement OnMapReadyCallback in ViewHolder class.
  2. In onMapReady, call MapsInitializer.initialize, to gaurantee features can to be used before obtaining a map.

Use this class to initialize the Google Maps Android API if features need to be used before obtaining a map. It must be called because some classes such as BitmapDescriptorFactory and CameraUpdateFactory need to be initialized.

  1. Recycle map from 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(...);                    ... ...          }         }     }  
like image 100
Xcihnegn Avatar answered Sep 23 '22 09:09

Xcihnegn