HashMap to ListView

55,676

Solution 1

Make simple adapter class:

MyAdapter.java

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import java.util.ArrayList;
import java.util.Map;

public class MyAdapter extends BaseAdapter {
    private final ArrayList mData;

    public MyAdapter(Map<String, String> map) {
        mData = new ArrayList();
        mData.addAll(map.entrySet());
    }

    @Override
    public int getCount() {
        return mData.size();
    }

    @Override
    public Map.Entry<String, String> getItem(int position) {
        return (Map.Entry) mData.get(position);
    }

    @Override
    public long getItemId(int position) {
        // TODO implement you own logic with ID
        return 0;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        final View result;

        if (convertView == null) {
            result = LayoutInflater.from(parent.getContext()).inflate(R.layout.my_adapter_item, parent, false);
        } else {
            result = convertView;
        }

        Map.Entry<String, String> item = getItem(position);

        // TODO replace findViewById by ViewHolder
        ((TextView) result.findViewById(android.R.id.text1)).setText(item.getKey());
        ((TextView) result.findViewById(android.R.id.text2)).setText(item.getValue());

        return result;
    }
}

layout/my_adapter_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        >

    <TextView
            android:id="@android:id/text1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />

    <TextView
            android:id="@android:id/text2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            />
</LinearLayout>

Your code:

public void showCinemas(HashMap<String, String> cinemas) {
    MyAdapter adapter = new MyAdapter(cinemas);
    list.setAdapter(adapter);
}

Solution 2

HashMap is made of 2 Collection (or better 1 Collection and 1 Set), so it is not really possible by extending ArrayAdapter; but you can easily get a Collection (or better a Set) of Map.Entry, and convert it to a List:

From:

Map<String, Object> map = new HashMap<String, Object>();

to:

List<Map.Entry<String, Object>> list = new ArrayList(map.entrySet());

So I use a derived ArrayAdapter like this one:

class HashMapArrayAdapter extends ArrayAdapter {

        private static class ViewHolder {
            TextView tV1;
            TextView tV2;
        }

        public HashMapArrayAdapter(Context context, int textViewResourceId, List<Map.Entry<String, Object>> objects) {
            super(context, textViewResourceId, objects);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

            ViewHolder viewHolder;

            if (convertView == null) {
                convertView = LayoutInflater.from(getContext()).inflate(android.R.layout.simple_list_item_2, parent, false);
                viewHolder = new ViewHolder();
                viewHolder.tV1 = (TextView) convertView.findViewById(android.R.id.text1);
                viewHolder.tV2 = (TextView) convertView.findViewById(android.R.id.text2);
                convertView.setTag(viewHolder);
            } else
                viewHolder = (ViewHolder) convertView.getTag();

            Map.Entry<String, Object> entry = (Map.Entry<String, Object>) this.getItem(position);

            viewHolder.tV1.setText(entry.getKey());
            viewHolder.tV2.setText(entry.getValue().toString());
            return convertView;
        }

And then to create the adapter:

 ArrayAdapter adapter = new HashMapArrayAdapter(this.getActivity(), android.R.layout.simple_list_item_2, new ArrayList(map.entrySet()));

Solution 3

This has some similarities to Zanna's answer above, but is somewhat cleaner, improved and more comprehensive. I think this is about as simple as it gets.

Adapter:

public class MapEntryListAdapter extends ArrayAdapter<Map.Entry<String, Object>>
{
    public MapEntryListAdapter (Context context, List<Map.Entry<String, Object>> entryList)
    {
        super (context, android.R.layout.simple_list_item_2, entryList);
    }

    @NonNull @Override
    public View getView (int position, View convertView, ViewGroup parent)
    {
        View currentItemView = convertView != null ? convertView :
                       LayoutInflater.from (getContext ()).inflate (
                               android.R.layout.simple_list_item_2, parent, false);

        Map.Entry<String, Object> currentEntry = this.getItem (position);

        TextView textViewKey = currentItemView.findViewById (android.R.id.text1);
        TextView textViewValue = currentItemView.findViewById (android.R.id.text2);

        textViewKey.setText (currentEntry.getKey ());
        textViewValue.setText (currentEntry.getValue ().toString ());

        return currentItemView;
    }
}

MainActivity - fields:

private Map<String, Object> mMapItems;                      // original data source of all items
private List<Map.Entry<String, Object>> mListOfMapEntries;  // list of entries from mMapItems
private MapEntryListAdapter mMapEntryListAdapter;

MainActivity - onCreate method: (relevant portion)

    mMapItems = new LinkedHashMap<> ();

    mMapItems.put ("Test Key", "Test Value");                       // put in sample item #1
    mMapItems.put ("Sample Key", "Sample Value");                   // put in sample item #2

    mListOfMapEntries = new ArrayList<> (mMapItems.entrySet ());    // create the list

    mMapEntryListAdapter = new MapEntryListAdapter (this, mListOfMapEntries);

    ListView listView = findViewById (R.id.list_view);
    listView.setAdapter (mMapEntryListAdapter);

Note: By design, the data source of this adapter does not duplicate the incoming entryList but rather points to the same actual list. This enables you to easily modify the Adapter's data in MainActivity or wherever else you have the reference to an Adapter of this object. You would then need to call the adapter's notifyDataSetChanged() method to let it know that the list has changed so that it updates the ListView to reflect these changes.

If the contents of your Map object changes later on, you could do the following to bring those updates into your list and then into your ListView:

    mListOfMapEntries.clear ();
    mListOfMapEntries.addAll (mMapItems.entrySet ());
    mMapEntryListAdapter.notifyDataSetChanged ();

This clears the list of all existing items then adds the items from the Map to the list and then, very importantly, tells the adapter to update the ListView.

(Note: Do NOT create a new List object here (as opposed to clearing and adding shown here) because then you will not anymore be modifying the Adapter's data source which will still be pointing to the original list.)

Share:
55,676

Related videos on Youtube

Suleiman
Author by

Suleiman

Updated on January 21, 2020

Comments

  • Suleiman
    Suleiman over 4 years

    I have HashMap, how can I to put it in ListView? Which adapter need to use?

        public void showCinemas(HashMap<String, String> cinemas)
    {
        ...//What?
        list.setAdapter(adapter);
    }
    
  • Oleksii K.
    Oleksii K. over 10 years
    He need to show key-value pair in ListView, so ArrayList of HashMap will not help
  • Phantômaxx
    Phantômaxx over 10 years
    If you set data=null; before adding data to listdata, you are always adding null to listdata. Am I wrong?
  • RichieHH
    RichieHH almost 10 years
    What do you mean "only array will suffice"? Nonsense. See full answer below. In this case I maintain my own hashmap but no arrays required.
  • Josh
    Josh over 9 years
    Great!! I am pulling a list of TV channels from a json: Image, name and number. The images load using the fetched URL with a self contained image downloader class. If you may need it tell me, Id be glad to share.
  • cafebabe1991
    cafebabe1991 about 8 years
    Shouldn't it be values() instead of entrySet ?
  • Madhan
    Madhan over 7 years
    How to handle OnItemClickListener for this?
  • Carlo Matulessy
    Carlo Matulessy almost 7 years
    I would suggest for future reference to change your entrySet() to values() as already suggested by @cafebabe1991 ;) Took me sometimes to notice this issue and read the comments below
  • Sam
    Sam over 5 years
    I think it would be simpler to override ArrayAdapter rather than BaseAdapter. This way, you wouldn't have to override the methods that ArrayAdapter already includes, like getItem() and getCount().
  • Qadir Hussain
    Qadir Hussain about 4 years
    Upvote for mentioning the LinkedHashMap. HashMap has some auto sorting problem.
  • Amr SubZero
    Amr SubZero over 3 years
    The only thing that worked after long journey of trying! Thanks.