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
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.
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.
Solution as following:
OnMapReadyCallback
in ViewHolder
class.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.
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(...); ... ... } } }
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With