5

I'm reading this tutorial http://android.amberfog.com/?p=296 . I'd like to create a Listview weith different types of rows. I understand how to create the adapter, but what about the xml layout? So I definire an xml layout like this one:

<ListView/>

<TextView android:id="@+id/id1" />

<TextView android:id="@+id/id2" />

<ImageView android:id="@+id/id3" />

<TextView android:id="@+id/id4" />

Will it be a problem (for performance) if maybe one row uses just some elements (only some textviews) of the layout and another row maybe uses other elements? I don't understand if mine is the right way to define the xml or if I have to create different layout for each type of row.

Thank you in advance

EDIT: now I'm having a null point exception.

java code from the adapter:

@Override

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

ViewHolder holder = null;
int type = getItemViewType(position);

if (convertView == null) {
    holder = new ViewHolder();

    convertView = mInflater.inflate(R.layout.listview_main, null);
    holder.textView_title = (TextView)convertView.findViewById(R.id.listview1);

    convertView.setTag(holder);
} else {
    holder = (ViewHolder)convertView.getTag();
}

**holder.textView_title.setText("aaaa");** //NULL POINT EXCEPTION HERE

return convertView;

}

class ViewHolder {
    public TextView textView_title;
}

xml 1:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:id="@+id/main_layout"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              android:gravity="left"
              android:layout_margin="0dp">

    <!-- android:background="#0094ff" -->

    <ListView
            android:id="@id/android:list"
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:fastScrollEnabled="true"
            android:scrollbarStyle="insideInset"
            android:textFilterEnabled="false"
            android:divider="@null"
            android:layout_margin="0dp"
            android:paddingTop="0dp"
            android:paddingBottom="0dp"
            android:paddingLeft="15dp"
            android:paddingRight="22dp"/>



</LinearLayout>

xml2

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:gravity="left"
    android:layout_margin="0dp">

    <TextView
        android:id="@+id/listview1"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:paddingTop="7dp"
        android:paddingBottom="0dp"
        android:paddingLeft="0dp"
        android:paddingRight="0dp"
        android:textSize="18sp"
        android:textColor="#000000"
        android:lines="1">
    </TextView>

</LinearLayout>
user1315621
  • 3,044
  • 9
  • 42
  • 86
  • different types of rows meaning? first row with different layout inflation second row different layout inflation for rows in listview? – Raghunandan Sep 18 '13 at 09:16
  • http://stackoverflow.com/questions/4777272/android-listview-with-different-layout-for-each-row – Devrim Sep 18 '13 at 09:17
  • Yes, you need to create different layouts for each type of row you want. Most simple and robust way of doing this. – umair.ali Sep 18 '13 at 09:19

4 Answers4

13

You need to override getViewItemType and getViewTypeCount. You will also need to have custom layouts.

getItemViewType(int position) - returns information which layout type you should use based on position.

You should have a look at the video in the link.

http://www.youtube.com/watch?v=wDBM6wVEO70

private static final int TYPE_ITEM1 = 0;
private static final int TYPE_ITEM2 = 1;
private static final int TYPE_ITEM3 = 2; 

Then

int type;
@Override
public int getItemViewType(int position) {

    if (position== 0){
        type = TYPE_ITEM1;
    } else if  (position == 1){
        type = TYPE_ITEM2;
    }
    else
    {
         type= TYPE_ITEM3 ;
    }
    return type;
}

 @Override
 public int getViewTypeCount() {
        return 3; 
 }
@Override  
public View getView(int position, View convertView, ViewGroup parent) {
View row = convertView;
LayoutInflater inflater = null;
int type = getItemViewType(position);
  // instead of if else you can use a case
   if (row  == null) {
    if (type == FIRST_TYPE) {
            //infalte layout of type1
      }
    if (type == SECOND_TYPE) {
            //infalte layout of type2
    }  else {
            //infalte layout of normaltype
 }
} 
Raghunandan
  • 132,755
  • 26
  • 225
  • 256
  • 1
    Trying with one type of row but having a null point exception! Code above :) – user1315621 Sep 18 '13 at 22:39
  • @user1315621 looks like your textview is not initialized. check it again – Raghunandan Sep 19 '13 at 03:54
  • 1
    @user1315621 check if you are inflating the layout that has the textview. If you get NPE at the line mentioned your textview initializatin has failed. Put a break point and debug – Raghunandan Sep 19 '13 at 04:01
  • Well I checked and it should be inizialized. However, following this tutorial http://www.debugrelease.com/2013/06/24/android-listview-tutorial-with-images-and-text/ , the textview is not in the same xml layout as the listview. How can I resolve? Thanks for your help – user1315621 Sep 19 '13 at 09:13
  • @user1315621 you need to have the textview in the same xl you inflate coz `findViewById` looks for a view with the id provided for the current inflated layout – Raghunandan Sep 19 '13 at 09:29
  • So how can I create different layout for rows? – user1315621 Sep 19 '13 at 10:07
  • @user1315621 have a different layout in layout folder. Customize the layout you want. Inflate that layout in `getView`. Use the inflated view object to initialize your views and update the same – Raghunandan Sep 19 '13 at 10:09
3
  • I have just solved this problem of creating different layouts for different rows.
    1. Define all the different layouts in their corresponding different xml files.
    2. Override getViewTypeCount() method and return number of defined layouts from this.
    3. Override getItemViewType() method and according to the relation between the 'position' of the element in the listview and the proposed xml layout define the condition and return the appropriate integer values.
    4. By calling the getItemViewType() method in the getView() method, we can get the number corresponding to layout, then inflate the convertView with the corresponding xml layout. And then by using findViewById(), you can get the values into objects of the TextView class defined in ViewHolder class and setText or whatever operation you want to use you can do further. As simple as it is. Suppose we have 4 types of layouts and 4 xml files named as element1, element2, element3, element4 and two common textview id as text1, text2.

@Override public int getItemViewType(int position) { if (position % 4 == 0) { return 0; } else if (position % 4 == 1) { return 1; } else if (position % 4 == 2) { return 2; } return 3; }

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

    ViewHolder holder = null;

    int type = getItemViewType(position);
    if (convertView == null) {
        switch (type) {
        case 0: {
            convertView = mInflater.inflate(R.layout.element1, null);
            break;
        }
        case 1: {
            convertView = mInflater.inflate(R.layout.element2, null);
            break;
        }
        case 2: {
            convertView = mInflater.inflate(R.layout.element3, null);
            break;
        }
        case 3: {
            convertView = mInflater.inflate(R.layout.element4, null);
            break;
        }
        }
        holder = new ViewHolder();
        holder.txt1 = (TextView) convertView.findViewById(R.id.text1);
        holder.txt2 = (TextView) convertView.findViewById(R.id.text2);
        convertView.setTag(holder);
    } else {
        holder = (ViewHolder) convertView.getTag();
    }
    String rowItem = null;
    rowItem = rowItems[position];
    holder.txt1.setText(rowItem);
    rowItem = rowItems[position+1];
    holder.txt1.setText(rowItem);
    return convertView;
}



private class ViewHolder {
    TextView txt1, txt2;
}
Apurva Sharma
  • 191
  • 2
  • 7
2

This might be a wrong method to do it. If you have only one component in the ListView then use simple adapter else use custom adapter with separate XML for the list row.

Sample code:

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

        ArrayList<HashMap<String, String>> alist = new ArrayList<HashMap<String, String>>();

        for (int i = 1; i < 20; i++) {
            HashMap<String, String> hmap = new HashMap<String, String>();
            hmap.put("date", "" + i + "/13");
            hmap.put("restaurant", "Restaurant" + i);
            hmap.put("distance", "" + (i * 100) + "kms");
            alist.add(hmap);

        }

        final CustomListAdapter adapter = new CustomListAdapter(this,
                R.layout.listitemhistory, alist);

        list.setAdapter(adapter);

    }

    private void initcomponents() {
        list = (ListView) findViewById(R.id.history_lst_list);

    }

    public void backButtonClick(View v) {
        finish();
    }

    class CustomListAdapter extends ArrayAdapter<HashMap<String, String>> {
        Context context;
        int textViewResourceId;
        ArrayList<HashMap<String, String>> alist;

        public CustomListAdapter(Context context, int textViewResourceId,
                ArrayList<HashMap<String, String>> alist) {
            super(context, textViewResourceId);
            this.context = context;
            this.alist = alist;
            this.textViewResourceId = textViewResourceId;

        }

        public int getCount() {

            return alist.size();
        }

        public View getView(int pos, View convertView, ViewGroup parent) {
            Holder holder = null;

                LayoutInflater inflater = ((Activity) context)
                        .getLayoutInflater();
                convertView = inflater.inflate(R.layout.listitemhistory,
                        parent, false);
                holder = new Holder();
                holder.date = (TextView) convertView
                        .findViewById(R.id.listitemhistory_txt_date);
                holder.restaurant = (TextView) convertView
                        .findViewById(R.id.listitemhistory_txt_restaurant);
                holder.distance = (TextView) convertView
                        .findViewById(R.id.listitemhistory_txt_distance);
                holder.lin_background = (LinearLayout) convertView
                        .findViewById(R.id.history_lin_background);
                convertView.setTag(holder);



            holder = (Holder) convertView.getTag();

            holder.date.setText(alist.get(pos).get("date"));
            holder.restaurant.setText(alist.get(pos).get("restaurant"));
            holder.distance.setText(alist.get(pos).get("distance"));

            return convertView;

        }

        class Holder {
            TextView date, restaurant, distance;
            LinearLayout lin_background;
        }
    }
Rishabh Srivastava
  • 3,683
  • 2
  • 30
  • 58
0

It wont be a problem. But I think you got the tutorial wrong.

Your need to define a layout for a row.

If you want your row to be able to contain a Text or an Image, you need to add both Views to the Layout.

Now in you adapter, where you fill your List you decide which item to set.

So lets say you have 2 Strings and an image.

So you set the text for the first two rows and add an image to the third. You dont have to predefine every row for itself, as i guess thats what you are trying to do.

Daniel Bo
  • 2,518
  • 1
  • 18
  • 29