9

This post is related to this ViewHolder not working. On that post, I was following a tutorial on how to use ViewHolder on a ListView. What I want now is to have the last item on a ListView to have a different layout than the rest. Here's my code:

int lastpos = mList.size()-1;
          System.out.println("position: "+position+" mlist: "+lastpos);

          if(position==lastpos){
           view = vi.inflate(R.layout.list_item_record, null);
           holder.textView = (TextView)view.findViewById(R.id.record_view);
          }else{
           view = vi.inflate(R.layout.list_item_bn, null);
           holder.textView = (TextView)view.findViewById(R.id.tv_name);
          }
          view.setTag(holder);

I just get the ListView size minus 1 and have a condition that checks the if current position is equal to the last item. However, this code sets the last item's layout same as the rest. getView method full code:

private class CustomListAdapter extends ArrayAdapter { 
    private ArrayList mList;
    private Context mContext;

    public CustomListAdapter(Context context, int textViewResourceId,
            ArrayList list) {
        super(context, textViewResourceId, list);
        this.mList = list;
        this.mContext = context;
    }   

    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = convertView;

        final ToggleButton tb;

         if (view == null) {
          ViewHolder holder = new ViewHolder();
          LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

          int lastpos = mList.size()-1;
          System.out.println("position: "+position+" mlist: "+lastpos);

          if(position==lastpos){
           view = vi.inflate(R.layout.list_item_record, null);
           holder.textView = (TextView)view.findViewById(R.id.record_view);
          }else{
           view = vi.inflate(R.layout.list_item_bn, null);
           holder.textView = (TextView)view.findViewById(R.id.tv_name);
          }
          view.setTag(holder);
         }

         final ItemsDisplay listItem = (ItemsDisplay) mList.get(position);
            if (listItem != null) {
             ViewHolder holder1 = (ViewHolder)view.getTag();
             holder1.textView.setText(listItem.getTag());

             view.setOnClickListener(new OnClickListener() {
                    public void onClick(View arg0) { //--clickOnListItem
                     Toast.makeText(getBaseContext(),
                       "Button is "+position+" "+listItem.getTag(),
                       Toast.LENGTH_LONG).show();
                    }
                });

                tb  = (ToggleButton) view.findViewById(R.id.toggleButton);
                tb.setOnClickListener(new View.OnClickListener() {
              public void onClick(View v) {

               if(tb.isChecked()){
                Toast.makeText(getBaseContext(),
                           "Button is "+position+" checked:"+tb.isChecked()+" "+listItem.getTag(),
                           Toast.LENGTH_LONG).show();
               }else{
                Toast.makeText(getBaseContext(),
                  "Button is "+position+" checked:"+tb.isChecked()+" "+listItem.getTag(),
                           Toast.LENGTH_LONG).show();
               }
              }
                });
            }



        return view;  
    }

}

Could anybody help me with this?

Community
  • 1
  • 1
antonoVodka
  • 191
  • 1
  • 4
  • 13

2 Answers2

29

Implement the getItemViewType() and getViewTypeCount() for your adapter:

@Override
public int getViewTypeCount() {
   return 2; //return 2, you have two types that the getView() method will return, normal(0) and for the last row(1)
}

and:

@Override
public int getItemViewType(int position) {
    return (position == this.getCount() - 1) ? 1 : 0; //if we are at the last position then return 1, for any other position return 0
}

Then in the getView() method find out what type of view to inflate:

public View getView(final int position, View convertView, ViewGroup parent) {
        View view = convertView;
        int theType = getItemViewType(position); 
        if (view == null) {
          ViewHolder holder = new ViewHolder();
          LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          if (theType == 0) {
              // inflate the ordinary row
              view = vi.inflate(R.layout.list_item_bn, null);
              holder.textView = (TextView)view.findViewById(R.id.tv_name);      
          } else if (theType == 1){
             // inflate the row for the last position
              view = vi.inflate(R.layout.list_item_record, null);
              holder.textView = (TextView)view.findViewById(R.id.record_view);
          } 
          view.setTag(holder);
         }
 //other stuff here, keep in mind that you have a different layout for your last position so double check what are trying to initialize
}

The example from the comments: http://pastebin.com/gn65240B (or https://gist.github.com/2641914 )

user
  • 86,916
  • 18
  • 197
  • 190
  • i tried this but still not working. i tried to print the value of position and theType. position gave me 0-5 only with theType=0. why is that? mList consist of 26 items. im just wondering why positions has 6 values only oppose to 26 items on my list. – antonoVodka Mar 13 '12 at 03:28
  • @user1263567 Have you scrolled the list down? I think it's normal to get only the 5-6 position, probably because these are the position that can be see on the screen, it would be a waste to call the `getView` method for positions that are not seen yet. If you scroll the list you should see the other positions as well. – user Mar 13 '12 at 09:35
  • i see. however, the last row is still the same as the rest. i tried to transfer the last row to the first row, to see if it works. it works but every 7th row is the same as the first row. only the 1st should be the only one to have a different layout than the rest. – antonoVodka Mar 14 '12 at 01:07
  • @user1263567 You're doing something wrong in the `getView` method and the rows get recycled. I've made a simple example here( http://pastebin.com/gn65240B ) so you can see a working example. – user Mar 14 '12 at 06:41
  • i tried to work on this but somehow i cant follow it especially when my layout doesnt deal with textview only. I noticed at your example that it is Listactivity. Will it matter? i implemented it as activity only and of the component of the activity is the listview. – antonoVodka Mar 28 '12 at 08:47
  • @user1263567 I just used `ListActivity` because I could setup the example faster(it doesn't matter what activity you use). The example it's pretty simple, you could build on that to implement your own row layout. – user Mar 28 '12 at 09:53
  • it works. i edited your example and its now working. thank you! – antonoVodka Mar 29 '12 at 04:06
  • What if I donot know where my another layout to be added. What I mean to say is what if If my first row has one layout and next two rows have different, again on from the fourth row and fifth row has some layout and next five rows have different layout. In this case I really do not know what is coming up. It depends on the true/false factor of my MyClass in my ArrayList that I am passing. Can you suggest what to do? – Debopam Mitra Jul 25 '12 at 07:38
  • @Debopam You should ask a question with more details. I doubt that your data is so unpredictable that you can't determine the row type. There isn't a limitation of the number of row types that you return from `getViewTypeCount`(you could return 30 for example). You should implement the logic to determine the row type in the `getItemViewType`(get the `MyClass` item for that position, see what type of row it requires and return the proper identifier for that row type). – user Jul 25 '12 at 07:51
  • @Luksprog: I have opened another question regarding this issue. See this [link](http://stackoverflow.com/questions/11645160/listview-rows-with-different-layout) – Debopam Mitra Jul 25 '12 at 07:59
  • @Luksprog Please, do you think you could give me some advice/ideas over here http://goo.gl/E37NSu – eddy Mar 01 '15 at 15:41
0

The problem is that once you've inflated the view it can be reused many times in any position. I'd suggest the following approach: you inflate all but last item as usual (including the view holder), but for the last item you hold the reference as a field of CustomListAdapter and return it every time the last item is requested:

private class CustomListAdapter extends ArrayAdapter { 
    ...
    private View mLastItem; 

    public View getView(final int position, View convertView, ViewGroup parent) {
        View view = convertView;
        ...
        int lastpos = mList.size()-1;

        if (view == null) {
            ViewHolder holder = new ViewHolder();
            LayoutInflater vi = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);

            if (position == lastpos) {
                view = vi.inflate(R.layout.list_item_record, null);
                holder.textView = (TextView)view.findViewById(R.id.record_view);
                mLastItem = view;
            }
            else {
                view = vi.inflate(R.layout.list_item_bn, null);
                holder.textView = (TextView)view.findViewById(R.id.tv_name);
            }
            view.setTag(holder);
        }

        if (position == lastpos) {
            ... // Update the last item here
            return mLastItem; 
        }
        ...
    }

}
a.ch.
  • 8,285
  • 5
  • 40
  • 53