user3823935
user3823935

Reputation: 891

Google Map loading issue in android listview

I am trying to show mapview on my listview. Map cannot load in listview. If i touch the map view, map will load. If i scroll the listview mapview goes to unloaded initial stage. My listview adapter and screenshots are given,

public class OfferListAdapter extends BaseAdapter {

String lat="",lon="";
String adId;
Context context;
MapView mapView;
GoogleMap map; 
Util g;
ArrayList<HashMap<String, String>> adList;
Bundle savedInstanceState;

public OfferListAdapter(final Context context, final ArrayList<HashMap<String, String>> addlist, final String type, final Bundle b) {
    // TODO Auto-generated constructor stub
    this.context=context;
    this.adList=addlist;
    this.listType=type;
    this.savedInstanceState=b;
}

@Override
public int getCount() {
    // TODO Auto-generated method stub
    return adList.size();
}

@Override
public Object getItem(int position) {
    // TODO Auto-generated method stub
    return null;
}

@Override
public long getItemId(int position) {
    // TODO Auto-generated method stub
    return 0;
}

private class ViewHolder
{
     ImageView adImg;
     TextView txt;
}

@SuppressLint("InflateParams")
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    // TODO Auto-generated method stub
    adId=null;
    g=Util.getInstance(context);
    final LayoutInflater mInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    final View view=mInflater.inflate(R.layout.offer_list_row, null);
    final ViewHolder holder;
    holder=new ViewHolder();
    mapView = (MapView) view.findViewById(R.id.map);
    mapView.onCreate(savedInstanceState);
    // Gets to GoogleMap from the MapView and does initialization stuff
    map = mapView.getMap();
    map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
    // Showing / hiding your current location
    map.setMyLocationEnabled(false);
    // Enable / Disable zooming controls
    map.getUiSettings().setZoomControlsEnabled(false);
    // Enable / Disable my location button
    map.getUiSettings().setMyLocationButtonEnabled(false);
    // Enable / Disable Compass icon
    map.getUiSettings().setCompassEnabled(false);
    // Enable / Disable Rotate gesture
    map.getUiSettings().setRotateGesturesEnabled(false);
    // Enable / Disable zooming functionality
    map.getUiSettings().setZoomGesturesEnabled(false);

    MapsInitializer.initialize(context);
    // Updates the location and zoom of the MapView
    double latitude = 12.965119900000000000;
    double longitude = 80.243980900000000000;
    MarkerOptions marker = new MarkerOptions().position(
            new LatLng(latitude, longitude))
            .title("Hello Maps");
    marker.icon(BitmapDescriptorFactory
            .defaultMarker(BitmapDescriptorFactory.HUE_RED));
    map.addMarker(marker);

    //Zoom Particular position
    CameraPosition cameraPosition = new CameraPosition.Builder()
    .target(new LatLng(latitude,
            longitude)).zoom(12).build();

    map.animateCamera(CameraUpdateFactory
            .newCameraPosition(cameraPosition));

    return view;
}

public void updateData(final ArrayList<HashMap<String, String>> addlist, final String type) {
    // TODO Auto-generated method stub
    this.adList=addlist;
    this.listType=type;
}

}

List of screenshots. 1)This screen will appears in 1st time loading,

enter image description here

2)This screen will appears after a click on mapview, enter image description here

3)This screen will appears after 2 click on mapview, enter image description here

4)This screen will appears after 10 to 15 click on mapview, enter image description here

Upvotes: 2

Views: 4681

Answers (4)

Vinil Chandran
Vinil Chandran

Reputation: 1561

Use static map API instead of Map fragment. You can implement it using web view. First of all replace the map fragment with a webview. the follow the below steps.

Create an html file static_map.html inside html folder in asset folder

its content may be like the below

<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Untitled Document</title>
<style>
    html,body{
        padding:0;
        margin:0;
    }
    .map{
        position: fixed;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    width: 100%;
    height: 100%;
    }
</style>
</head>

<body>
<img class="map" src="REPLACE_HERE" >
</body>
</html>

The programmatically create a static map URL and replace REPLACE_HERE string with it. The load it on the web view. This will reduce our effort very much.

The code is as follows

try {
    String url ="https://maps.googleapis.com/maps/api/staticmap?";
    url+="&zoom=13";
    url+="&size=600x300";
    url+="&maptype=roadmap";
    url+="&markers=color:green%7Clabel:G%7C"+latitude+", "+longitude;
    url+="&key="+ YOUR_GOOGLE_API_KEY;

    InputStream is = context.getAssets().open("html/static_map.html");
    int size = is.available();
    byte[] buffer = new byte[size];
    is.read(buffer);
    is.close();
    String str = new String(buffer);
    str = str.replace("REPLACE_HERE", url);
    wv_map.getSettings().setJavaScriptEnabled(true);
    wv_map.loadDataWithBaseURL("", str, "text/html", "UTF-8", "");
} catch (IOException e) {

} 

Need to enable and the static API in google console for this.

Upvotes: 0

ChinLoong
ChinLoong

Reputation: 1833

There is a great working example by google here googlemaps sample

It loads light weight maps in a list(see LiteListDemoActivity.java in example)

    /*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.example.mapdemo;

import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.MapsInitializer;
import com.google.android.gms.maps.OnMapReadyCallback;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;

import android.content.Context;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.ArrayAdapter;
import android.widget.TextView;

import java.util.HashSet;

/**
 * This shows to include a map in lite mode in a ListView.
 * Note the use of the view holder pattern with the
 * {@link com.google.android.gms.maps.OnMapReadyCallback}.
 */
public class LiteListDemoActivity extends AppCompatActivity {

    private ListFragment mList;

    private MapAdapter mAdapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.lite_list_demo);

        // Set a custom list adapter for a list of locations
        mAdapter = new MapAdapter(this, LIST_LOCATIONS);
        mList = (ListFragment) getSupportFragmentManager().findFragmentById(R.id.list);
        mList.setListAdapter(mAdapter);

        // Set a RecyclerListener to clean up MapView from ListView
        AbsListView lv = mList.getListView();
        lv.setRecyclerListener(mRecycleListener);

    }

    /**
     * Adapter that displays a title and {@link com.google.android.gms.maps.MapView} for each item.
     * The layout is defined in <code>lite_list_demo_row.xml</code>. It contains a MapView
     * that is programatically initialised in
     * {@link #getView(int, android.view.View, android.view.ViewGroup)}
     */
    private class MapAdapter extends ArrayAdapter<NamedLocation> {

        private final HashSet<MapView> mMaps = new HashSet<MapView>();

        public MapAdapter(Context context, NamedLocation[] locations) {
            super(context, R.layout.lite_list_demo_row, R.id.lite_listrow_text, locations);
        }


        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            View row = convertView;
            ViewHolder holder;

            // Check if a view can be reused, otherwise inflate a layout and set up the view holder
            if (row == null) {
                // Inflate view from layout file
                row = getLayoutInflater().inflate(R.layout.lite_list_demo_row, null);

                // Set up holder and assign it to the View
                holder = new ViewHolder();
                holder.mapView = (MapView) row.findViewById(R.id.lite_listrow_map);
                holder.title = (TextView) row.findViewById(R.id.lite_listrow_text);
                // Set holder as tag for row for more efficient access.
                row.setTag(holder);

                // Initialise the MapView
                holder.initializeMapView();

                // Keep track of MapView
                mMaps.add(holder.mapView);
            } else {
                // View has already been initialised, get its holder
                holder = (ViewHolder) row.getTag();
            }

            // Get the NamedLocation for this item and attach it to the MapView
            NamedLocation item = getItem(position);
            holder.mapView.setTag(item);

            // Ensure the map has been initialised by the on map ready callback in ViewHolder.
            // If it is not ready yet, it will be initialised with the NamedLocation set as its tag
            // when the callback is received.
            if (holder.map != null) {
                // The map is already ready to be used
                setMapLocation(holder.map, item);
            }

            // Set the text label for this item
            holder.title.setText(item.name);

            return row;
        }

        /**
         * Retuns the set of all initialised {@link MapView} objects.
         *
         * @return All MapViews that have been initialised programmatically by this adapter
         */
        public HashSet<MapView> getMaps() {
            return mMaps;
        }
    }

    /**
     * Displays a {@link LiteListDemoActivity.NamedLocation} on a
     * {@link com.google.android.gms.maps.GoogleMap}.
     * Adds a marker and centers the camera on the NamedLocation with the normal map type.
     */
    private static void setMapLocation(GoogleMap map, NamedLocation data) {
        // Add a marker for this item and set the camera
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(data.location, 13f));
        map.addMarker(new MarkerOptions().position(data.location));

        // Set the map type back to normal.
        map.setMapType(GoogleMap.MAP_TYPE_NORMAL);
    }

    /**
     * Holder for Views used in the {@link LiteListDemoActivity.MapAdapter}.
     * Once the  the <code>map</code> field is set, otherwise it is null.
     * When the {@link #onMapReady(com.google.android.gms.maps.GoogleMap)} callback is received and
     * the {@link com.google.android.gms.maps.GoogleMap} is ready, it stored in the {@link #map}
     * field. The map is then initialised with the NamedLocation that is stored as the tag of the
     * MapView. This ensures that the map is initialised with the latest data that it should
     * display.
     */
    class ViewHolder implements OnMapReadyCallback {

        MapView mapView;

        TextView title;

        GoogleMap map;

        @Override
        public void onMapReady(GoogleMap googleMap) {
            MapsInitializer.initialize(getApplicationContext());
            map = googleMap;
            NamedLocation data = (NamedLocation) mapView.getTag();
            if (data != null) {
                setMapLocation(map, data);
            }
        }

        /**
         * Initialises the MapView by calling its lifecycle methods.
         */
        public void initializeMapView() {
            if (mapView != null) {
                // Initialise the MapView
                mapView.onCreate(null);
                // Set the map ready callback to receive the GoogleMap object
                mapView.getMapAsync(this);
            }
        }

    }

    /**
     * RecycleListener that completely clears the {@link com.google.android.gms.maps.GoogleMap}
     * attached to a row in the ListView.
     * Sets the map type to {@link com.google.android.gms.maps.GoogleMap#MAP_TYPE_NONE} and clears
     * the map.
     */
    private AbsListView.RecyclerListener mRecycleListener = new AbsListView.RecyclerListener() {

        @Override
        public void onMovedToScrapHeap(View view) {
            ViewHolder holder = (ViewHolder) view.getTag();
            if (holder != null && holder.map != null) {
                // Clear the map and free up resources by changing the map type to none
                holder.map.clear();
                holder.map.setMapType(GoogleMap.MAP_TYPE_NONE);
            }

        }
    };

    /**
     * Location represented by a position ({@link com.google.android.gms.maps.model.LatLng} and a
     * name ({@link java.lang.String}).
     */
    private static class NamedLocation {

        public final String name;

        public final LatLng location;

        NamedLocation(String name, LatLng location) {
            this.name = name;
            this.location = location;
        }
    }

    /**
     * A list of locations to show in this ListView.
     */
    private static final NamedLocation[] LIST_LOCATIONS = new NamedLocation[]{
            new NamedLocation("Cape Town", new LatLng(-33.920455, 18.466941)),
            new NamedLocation("Beijing", new LatLng(39.937795, 116.387224)),
            new NamedLocation("Bern", new LatLng(46.948020, 7.448206)),
            new NamedLocation("Breda", new LatLng(51.589256, 4.774396)),
            new NamedLocation("Brussels", new LatLng(50.854509, 4.376678)),
            new NamedLocation("Copenhagen", new LatLng(55.679423, 12.577114)),
            new NamedLocation("Hannover", new LatLng(52.372026, 9.735672)),
            new NamedLocation("Helsinki", new LatLng(60.169653, 24.939480)),
            new NamedLocation("Hong Kong", new LatLng(22.325862, 114.165532)),
            new NamedLocation("Istanbul", new LatLng(41.034435, 28.977556)),
            new NamedLocation("Johannesburg", new LatLng(-26.202886, 28.039753)),
            new NamedLocation("Lisbon", new LatLng(38.707163, -9.135517)),
            new NamedLocation("London", new LatLng(51.500208, -0.126729)),
            new NamedLocation("Madrid", new LatLng(40.420006, -3.709924)),
            new NamedLocation("Mexico City", new LatLng(19.427050, -99.127571)),
            new NamedLocation("Moscow", new LatLng(55.750449, 37.621136)),
            new NamedLocation("New York", new LatLng(40.750580, -73.993584)),
            new NamedLocation("Oslo", new LatLng(59.910761, 10.749092)),
            new NamedLocation("Paris", new LatLng(48.859972, 2.340260)),
            new NamedLocation("Prague", new LatLng(50.087811, 14.420460)),
            new NamedLocation("Rio de Janeiro", new LatLng(-22.90187, -43.232437)),
            new NamedLocation("Rome", new LatLng(41.889998, 12.500162)),
            new NamedLocation("Sao Paolo", new LatLng(-22.863878, -43.244097)),
            new NamedLocation("Seoul", new LatLng(37.560908, 126.987705)),
            new NamedLocation("Stockholm", new LatLng(59.330650, 18.067360)),
            new NamedLocation("Sydney", new LatLng(-33.873651, 151.2068896)),
            new NamedLocation("Taipei", new LatLng(25.022112, 121.478019)),
            new NamedLocation("Tokyo", new LatLng(35.670267, 139.769955)),
            new NamedLocation("Tulsa Oklahoma", new LatLng(36.149777, -95.993398)),
            new NamedLocation("Vaduz", new LatLng(47.141076, 9.521482)),
            new NamedLocation("Vienna", new LatLng(48.209206, 16.372778)),
            new NamedLocation("Warsaw", new LatLng(52.235474, 21.004057)),
            new NamedLocation("Wellington", new LatLng(-41.286480, 174.776217)),
            new NamedLocation("Winnipeg", new LatLng(49.875832, -97.150726))
    };

}

Upvotes: 0

Tao-Nhan Nguyen
Tao-Nhan Nguyen

Reputation: 5926

I came across the same problem, and using your code and other stack topics I came up with this way of doing it

mapView.onCreate(savedInstanceState);
mapView.onResume();
mapView.getMapAsync(
        new OnMapReadyCallback() {
    @Override
    public void onMapReady(GoogleMap googlemap) {
        final GoogleMap map = googlemap;
        map.setMapType(GoogleMap.MAP_TYPE_TERRAIN);

        MapsInitializer.initialize(context);

        map.setMyLocationEnabled(true);
        map.setMapType(GoogleMap.MAP_TYPE_TERRAIN);

        LatLng sydney = new LatLng(-33.873, 151.206);
        String markerTitle = "Sydney";
        String snippetTitle = "Lovely city";

        if (markerTitle != null && snippetTitle != null) {
            map.addMarker(new MarkerOptions()
                    .title(markerTitle)
                    .snippet(snippetTitle)
                    .position(sydney));
        }

        map.setMyLocationEnabled(true);
        map.moveCamera(CameraUpdateFactory.newLatLngZoom(sydney2, 13));

    }
}
);

1) You need to call onResume(); on mapView so it loads properly 2) Use .getMapAsync, getMap is deprecated

You will be able to zoom in with a double tap :)

Upvotes: 4

AniV
AniV

Reputation: 4037

Direct use of the mapView in a listView is a heavy operation. This is going to cause a delay and result in sluggish User Experience of your app. To avoid this behavior you have an alternative to use Static maps. This will enable Lazy Loading of maps in your list view and therby user wont have to tap map now and then.

I am providing a small example below that use this method. First create a list view from the data (Either API or DB). Then pass data such as latitude and longitude to a string with some static variables defined as follows.

String getMapURL = "http://maps.googleapis.com/maps/api/staticmap?zoom=12&size=360x180&markers=size:mid|color:green|"  
+ JOLocation.getString("latitude") 
+ "," 
+ JOLocation.getString("longitude") 
+ "&sensor=false";

The above constructed URL, when used in a browser, returns a .PNG file. Then, in my adapter for the activity, This method results in the lazy loading of the maps in the app where your marker displaying the coordinates are fetched at runtime, avoiding heavy work in the code.

Hope this would help!!!

Upvotes: 5

Related Questions