Android - MapView contained within a Listview
Asked Answered
H

5

11

Currently I am trying to place a MapView within a ListView. Has anyone had any success with this? Is it even possible? Here is my code:

            ListView myList = (ListView) findViewById(android.R.id.list);
        List<Map<String, Object>> groupData = new ArrayList<Map<String, Object>>();

        Map<String, Object> curGroupMap = new HashMap<String, Object>();
        groupData.add(curGroupMap);
        curGroupMap.put("ICON", R.drawable.back_icon);
        curGroupMap.put("NAME","Go Back");
        curGroupMap.put("VALUE","By clicking here");

        Iterator it = data.entrySet().iterator();
        while (it.hasNext()) 
        {
            //Get the key name and value for it
            Map.Entry pair = (Map.Entry)it.next();
            String keyName = (String) pair.getKey();
            String value = pair.getValue().toString();

            if (value != null)
            {
                //Add the parents -- aka main categories
                curGroupMap = new HashMap<String, Object>();
                groupData.add(curGroupMap);

                //Push the correct Icon
                if (keyName.equalsIgnoreCase("Phone"))
                    curGroupMap.put("ICON", R.drawable.phone_icon);
                else if (keyName.equalsIgnoreCase("Housing"))
                    curGroupMap.put("ICON", R.drawable.house_icon);
                else if (keyName.equalsIgnoreCase("Website"))
                    curGroupMap.put("ICON", R.drawable.web_icon);
                else if (keyName.equalsIgnoreCase("Area Snapshot"))
                    curGroupMap.put("ICON", R.drawable.camera_icon);
                else if (keyName.equalsIgnoreCase("Overview"))
                    curGroupMap.put("ICON", R.drawable.overview_icon);  
                else if (keyName.equalsIgnoreCase("Location"))
                    curGroupMap.put("ICON", R.drawable.map_icon);
                else
                    curGroupMap.put("ICON", R.drawable.icon);

                //Pop on the Name and Value
                curGroupMap.put("NAME", keyName);
                curGroupMap.put("VALUE", value);
            }
        }

        curGroupMap = new HashMap<String, Object>();
        groupData.add(curGroupMap);
        curGroupMap.put("ICON", R.drawable.back_icon);
        curGroupMap.put("NAME","Go Back");
        curGroupMap.put("VALUE","By clicking here");

        //Set up adapter
        mAdapter = new SimpleAdapter(
                mContext,
                groupData,
                R.layout.exp_list_parent,
                new String[] { "ICON", "NAME", "VALUE" },
                new int[] { R.id.photoAlbumImg, R.id.rowText1, R.id.rowText2  }
        );

        myList.setAdapter(mAdapter); //Bind the adapter to the list 

Thanks in advance for your help!!

Hus answered 2/6, 2010 at 20:15 Comment(4)
Ok, I'll bite. Why do you need a MapView in a ListView?Saltus
Moreover, what problems are you experiencing?Gavrila
Hypothetical -- You list everything out for a specific business..I'd like to display a map of where it's at in the list. The problem i'm experiencing is that I don't know where to begin. I'd like to contain it within a ListView rather than having to start an entire new Intent (which kind of defeats the listview purpose).Hus
hey Ryan, have you done with showing mapview in listAdapter? I am also trying to implement such feature.Spitler
S
4

In that case you would add the MapView to the list just like you would any other view. Here's a quick tutorial on how to create a custom list adapter. But I have to caution you, a MapView is a pretty heavy view and if you try to get a bunch of them on the screen you're going to notice the app being sluggish! You may just add a button to the list item that takes the user to another page with more information including a map.

Saltus answered 2/6, 2010 at 22:46 Comment(3)
I think, the link is not for showing mapview in custom adapter... kindly guide me out to have a map in listadapterSpitler
I have SupportMapFragment , can i use inside listview row,? i have custom adapter but i am not getting how to use map inside getChildView() of BaseExpandableListAdapter. Simply we can do like ((TextView) convertView.findViewById(R.id.parent_txt_list_title)).setText(parent.getTitle()); but how we can do same thing for map. Can you please help meBerbera
I have added a map in a listview but facing zoom/pinch problem. because of parent scrolling.Holmium
G
56

To post an alternate solution to a rather old answer (over 2 years actually), but I thought this might help someone who might stumble on this post like I did.

NOTE: This might be useful for someone who simply needs to display the location in a "Map" but does not need to interact with it in the ListView. The actual Map can be displayed on say, a details page, after clicking on an item in the ListView

As already pointed out by @CaseyB, MapView is kind of a heavy view. To counter that aspect (and to make life a little but easier for me ;-) ), I chose to build an URL like you would for a static Google Map, using several parameters that are necessary for my application. You can get more options here: https://developers.google.com/maps/documentation/staticmaps/

First, when I am constructing the data for my ListView, I pass data such as the latitude and longitude to a string with some static variables taken from the link mentioned above. I get my co-ordinates from the Facebook API.

The code I use to construct the link:

String getMapURL = "http://maps.googleapis.com/maps/api/staticmap?zoom=18&size=560x240&markers=size:mid|color:red|"  
+ 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, I use @Fedor's Lazy Loading to display the image generated from the URL contructed earlier to display in the custom ListView. You can of course choose your own method to display this Map (Map's image actually).

An example of the final result.

enter image description here

Currently, I have about 30 odd Checkin Maps (I use it in conjunction with the Facebook SDK) in this ListView, but users can have 100's of them and there have been absolutely no reports of it slowing down.

I suspect, this might not help the OP considering the time that has passed since the question, but hope it helps other users landing on this page in the future.

Godmother answered 7/10, 2012 at 7:28 Comment(6)
Wow, brilliant answer!Candler
Couldn't think of a better way. Great job.Fredi
I am working on same issue. I want to show actual route path(not straight line) between two location in map in list view. anyone know how to achieve this?Geodynamics
Is this still the best way to do this? The Android Google Maps SDK added a "Lite Mode" recently, but this way seems to be more lightweight. Is there a way to be able to add longer labels to the markers? It appears that it's restricted to 1 character now.Selfheal
@JohnOleynik: Honestly speaking, I haven't so far felt the need to change the use of the Static Map. That being said, I did read the Lite Mode in the new v2 of the SDK and it too is a bitmap of a location. And it is interactive. This seems to have a working example of Lite Mode used in a ListView. I haven't tested it or even seens it's code. But looks promising. Give it a try. :-)Godmother
This is an awesome answer. Could have never thought about it. Kudos to you man. To show PNG in the list and show an activity with the map after the map is chosen, for me it's the best way. Convenient and Best! Thanks!!!Consignee
U
6

First off, I'm don't think displaying multiple MapViews at once will work. MapActivity documents that only one is supported per process:

"Only one MapActivity is supported per process. Multiple MapActivities running simultaneously are likely to interfere in unexpected and undesired ways."

(http://code.google.com/android/add-ons/google-apis/reference/index.html)

It doesn't explicitly say you can't have multiple MapViews within a MapActivity but I think they'll interfere as well, regardless of what kind of parent ViewGroup they're in.

Second, you might consider using the static maps API to get a simple image for inclusion in the ListView -- a fully-fledged MapView may be unnecessarily heavyweight in any case:

http://code.google.com/apis/maps/documentation/staticmaps/

The one issue you may potentially face is that the Static Maps API limits usage by "user", which probably means by IP (it doesn't require an API key), and mobile networks can be problematic with IP usage limiting. I'm not sure exactly how that'll play out.

Undercharge answered 2/6, 2010 at 23:51 Comment(2)
Hey Steve -- I'm not trying to display multiple mapviews..only one. Also the goal is to have the user interact with the map so a static map will not work unfortunately. Thank you for your suggestion though :)Hus
Ah, I see. In that case, you can either use a custom list adapter, as CaseyB noted, or perhaps just use a vertical LinearLayout within a ScrollView, if the items in your list are largely heterogenous.Undercharge
S
4

In that case you would add the MapView to the list just like you would any other view. Here's a quick tutorial on how to create a custom list adapter. But I have to caution you, a MapView is a pretty heavy view and if you try to get a bunch of them on the screen you're going to notice the app being sluggish! You may just add a button to the list item that takes the user to another page with more information including a map.

Saltus answered 2/6, 2010 at 22:46 Comment(3)
I think, the link is not for showing mapview in custom adapter... kindly guide me out to have a map in listadapterSpitler
I have SupportMapFragment , can i use inside listview row,? i have custom adapter but i am not getting how to use map inside getChildView() of BaseExpandableListAdapter. Simply we can do like ((TextView) convertView.findViewById(R.id.parent_txt_list_title)).setText(parent.getTitle()); but how we can do same thing for map. Can you please help meBerbera
I have added a map in a listview but facing zoom/pinch problem. because of parent scrolling.Holmium
L
4

It's possible, from the GoogleMapSample code itself:

/**
 * 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;
        }
    }
}

Full code available at: https://github.com/googlemaps/android-samples/blob/master/ApiDemos/app/src/main/java/com/example/mapdemo/LiteListDemoActivity.java

Lists answered 16/4, 2016 at 19:57 Comment(2)
Link is dead updated link github.com/googlemaps/android-samples/blob/master/ApiDemos/java/…Haines
Updated link: github.com/googlemaps/android-samples/blob/master/ApiDemos/…Selfdenial
M
-1

i faced the same problem today - turns out that you have to create the MapView inside your MapActivity otherwise you'll get an error like Unable to Inflate View com.google.maps.MapView or so... Than pass this MapView to your ListAdapter and spit it out when needed. I had to place the MapView inside a RelativeLayout to adjust the Height and Width as i want (for some reason MapView doesn't behave the "normal" viewish way). You can ask me for Details if you like :)

Miffy answered 29/10, 2012 at 22:21 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.