Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Retrieve values from list item when there are varying layouts in the list

Tags:

android

I have a listview containing different xml layouts, each with different views. I was wondering how I can get the data from the views inside the item that the user has clicked on, inside the onItemClick method.

My listview is made up of multiple layouts so I cannot know for sure which layout is being pressed when the onItemClick method is called. For example, one of the items in the layouts in the listview is:

// list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >

    <TextView
        android:id="@+id/lblSub"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:padding="6dp"
        android:layout_marginRight="4dp"
        android:text=""
        android:textColor="#FFFFFF"
        android:textSize="18sp" />

    <TextView
        android:id="@+id/lblMain"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:padding="6dp"
        android:layout_marginLeft="4dp"
        android:text=""
        android:textColor="#FFFFFF"
        android:textSize="18sp" />

</RelativeLayout>

Where as another layout in the listview could be:

// list_item_name.xml 
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <TextView
        android:id="@+id/lblName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginLeft="4dp"
        android:padding="4dp"
        android:text="TextView"
        android:textColor="#FFFFFF"
        android:textSize="28sp" />

</RelativeLayout>

So I am at a loss as to how to get the data for all these views for any layout within the listview.

My previous attempt was this:

public void onItemClick(AdapterView<?> listView, View view, int position, long id) {            
    Object obj = adapter.getItem(position);
    String value = obj.toString();
}

However value in this instance, returns a string containing all the values of all the views inside this item, for example: {caption=MobileNumber, value=07000123456} <<< I cannot do anything with this string unless I try and pick it apart using substring, which is unreliable at best considering the values may have been created by the user.

This is my adapter in use (credit to Jeff Sharkey, I did not create this adapter):

public class SeparatedListAdapter extends BaseAdapter {  

    public final Map<String,Adapter> sections = new LinkedHashMap<String,Adapter>();  
    public final ArrayAdapter<String> headers;  
    public final static int TYPE_SECTION_HEADER = 0;  

    public SeparatedListAdapter(Context context) {  
        headers = new ArrayAdapter<String>(context, R.layout.list_header);  
    }  

    public void addSection(String section, Adapter adapter) {  
        this.headers.add(section);  
        this.sections.put(section, adapter);  
    }
    public void addSectionNoHeader(String section, Adapter adapter) {
        this.sections.put(section, adapter);
    }

    public Object getItem(int position) {  
        for(Object section : this.sections.keySet()) {  
            Adapter adapter = sections.get(section);  
            int size = adapter.getCount() + 1;  

            // check if position inside this section   
            if(position == 0) return section;  
            if(position < size) return adapter.getItem(position - 1);  

            // otherwise jump into next section  
            position -= size;  
        }  
        return null;  
    }  

    public int getCount() {  
        // total together all sections, plus one for each section header  
        int total = 0;  
        for(Adapter adapter : this.sections.values())  
            total += adapter.getCount() + 1;  
        return total;  
    }  

    public int getViewTypeCount() {  
        // assume that headers count as one, then total all sections  
        int total = 1;  
        for(Adapter adapter : this.sections.values())  
            total += adapter.getViewTypeCount();  
        return total;  
    }  

    public int getItemViewType(int position) {  
        int type = 1;  
        for(Object section : this.sections.keySet()) {  
            Adapter adapter = sections.get(section);  
            int size = adapter.getCount() + 1;  

            // check if position inside this section   
            if(position == 0) return TYPE_SECTION_HEADER;  
            if(position < size) return type + adapter.getItemViewType(position - 1);  

            // otherwise jump into next section  
            position -= size;  
            type += adapter.getViewTypeCount();  
        }  
        return -1;  
    }  

    public boolean areAllItemsSelectable() {  
        return false;  
    }  

    public boolean isEnabled(int position) {  
        return (getItemViewType(position) != TYPE_SECTION_HEADER);  
    }  

    @Override  
    public View getView(int position, View convertView, ViewGroup parent) {  
        int sectionnum = 0;  
        for(Object section : this.sections.keySet()) {  
            Adapter adapter = sections.get(section);  
            int size = adapter.getCount() + 1;  

            // check if position inside this section   
            if(position == 0) return headers.getView(sectionnum, convertView, parent);  
            if(position < size) return adapter.getView(position - 1, convertView, parent);  

            // otherwise jump into next section  
            position -= size;  
            sectionnum++;  
        }  
        return null;  
    }  

    @Override  
    public long getItemId(int position) {  
        return position;  
    }  

}  

I'm looking for a way of extracting the values of all the views inside the item. But the fact that the item that has been clicked could be one of any layouts is confusing me. Any ideas?

Here is an example of how I am using this adapter in code:

SeparatedListAdapter adapter;
Customer cust;
ListView lvDetails;

public Map<String,?> createDoubleItem(String title, String caption) {  
        Map<String,String> item = new HashMap<String,String>();  
        item.put(ITEM_VALUE, title);  
        item.put(ITEM_CAPTION, caption);  
        return item;  
    }

    public Map<String,?> createSingleItem(String name) {  
        Map<String,String> item = new HashMap<String,String>();  
        item.put(ITEM_VALUE, name);
        return item;
    } 

@Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view_customer);

        Intent intent = getIntent();
        cust = Db.Functions.getCustomer(intent.getStringExtra("uuid"));
        lvDetails = (ListView) findViewById(R.id.custdetails);
        adapter = new SeparatedListAdapter(this);
        lvDetails.setOnItemClickListener(new OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> listView, View view, int position, long id) {
                Gen.popup("Item clicked");
            }
        });

        // Name
        List<Map<String,?>> name = new LinkedList<Map<String,?>>();
        if (cust.firstName == null && cust.lastName == null)
            name.add(createSingleItem("Unnamed Customer"));
        else
            name.add(createSingleItem(
                    ((cust.title != null) ? cust.title + " " : "") +
                    ((cust.firstName != null) ? cust.firstName + " " : "") +
                    ((cust.lastName != null) ? cust.lastName : "")));
        adapter.addSection("Name", new SimpleAdapter(this, name, R.layout.list_item_name,   
                new String[] { ITEM_VALUE },
                new int[] { R.id.lblName }));
        // Phones
        if ((cust.mobileNumber != null || cust.homeNumber != null || cust.workNumber != null) && SHOW_PHONES)
        {
            List<Map<String,?>> phone = new LinkedList<Map<String,?>>();
            if (cust.mobileNumber != null)
                phone.add(createDoubleItem(cust.mobileNumber, "Mobile"));
            if (cust.homeNumber != null)
                phone.add(createDoubleItem(cust.homeNumber, "Home"));
            if (cust.workNumber != null)
                phone.add(createDoubleItem(cust.workNumber, "Work"));
            adapter.addSection("Phone", new SimpleAdapter(this, phone, R.layout.list_item,   
                    new String[] { ITEM_VALUE, ITEM_CAPTION },
                    new int[] { R.id.lblSub, R.id.lblMain }));
        }  
        lvDetails.setAdapter(adapter);
        adapter.notifyDataSetChanged();
    }
}
like image 914
Mike Baxter Avatar asked Aug 08 '13 12:08

Mike Baxter


2 Answers

Here's an easy way - paste this into each of the xml Views:

android:onClick="someFunction"

Then inside this someFunction do:

public void someFunction(View v){

switch (v.getId()){
case R.id.lblSub:
  // This layout was clicked, do something about it
  break;

case R.id.lblMain:
  // This layout was clicked, do something about it
  break;

}
like image 194
g00dy Avatar answered Nov 14 '22 05:11

g00dy


My understanding here is that you have 3 types of items:

  • Section title (which are disabled thanks to the areAllItemsSelectable and isEnabled methods and therefore cannot be clicked)
  • Single items
  • Double items

You also have apparently 2 sections, Name and Phone.

Items from section 1 (adapter.getItemViewType(position) == 1) are single items, while section 2 (adapter.getItemViewType(position) == 2).

In both cases, your item object (adapter.getItem(position)) is declared a Map<String, ?> and is even defined as a Map<String, String>.

That means that you can cast item to a Map:

Map<String, String> itemAsMap = (Map<String, String>) item;

and then fetch your value from the Map, using the ITEM_VALUE and ITEM_CAPTION keys.

like image 44
njzk2 Avatar answered Nov 14 '22 03:11

njzk2