24

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);
}
Dave Newton
  • 158,873
  • 26
  • 254
  • 302
Suleiman
  • 1,003
  • 2
  • 15
  • 29

4 Answers4

56

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);
}
Oleksii K.
  • 5,359
  • 6
  • 44
  • 72
  • 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. – Josh Oct 11 '14 at 20:09
  • 5
    Shouldn't it be values() instead of entrySet ? – cafebabe1991 May 21 '16 at 19:12
  • 1
    How to handle OnItemClickListener for this? – Madhan Feb 06 '17 at 10:29
  • 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 – Carlo Matulessy Sep 07 '17 at 17:28
  • 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(). – Sam Jan 21 '19 at 03:38
6

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()));
Zanna
  • 676
  • 9
  • 13
2

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.)

Sam
  • 1,009
  • 10
  • 13
0

Its fair simple:

for the list items you are going to create, for eg. say you have to put Student's records in list items like name and address

private HashMap<String,Object> prepareListViewItems(Student[] student)
{
 ArrayList<HashMap<String,Object>> listdata = new ArrayList<HashMap<String,Object>>();

for(int index=0;index<student.size();index++)
{
    HashMap<String,Object> data = new HashMap<String,Object>();
    data.put("roll", student[indxe].roll);
    data.put("address", student[indxe].address);
    data=null;
    listdata.add(data);

}

return data;
}

private void setListAdapter(Student[] students)
{
    TestListAdapter adapter = new TestListAdapter(prepareListViewItems(students))
    list.setAdapter(adapter);
}

But, when you are creating a custom adapter, there is no need to create hashmap, only array will suffice for your purpose.

Bette Devine
  • 1,196
  • 1
  • 9
  • 23
  • He need to show key-value pair in ```ListView```, so ```ArrayList``` of ```HashMap``` will not help – Oleksii K. Oct 20 '13 at 05:29
  • If you set `data=null;` **before** adding data to listdata, you are always adding null to listdata. Am I wrong? – Phantômaxx Mar 18 '14 at 14:00
  • 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. – RichieHH Aug 31 '14 at 16:59