By Andy


2012-08-14 03:54:56 8 Comments

So I have this problem I had before, and naturally I asked for help on here. Luksprog's answer was great because I had no idea about how ListView and GridView optimized itself with recycling Views. So with his advice I was able to change how I added Views to my GridView. Problem is now I have something that does not make sense. This is my getView from my BaseAdapter:


public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.day_view_item, parent, false);
        }
        Log.d("DayViewActivity", "Position is: "+position);
        ((TextView)convertView.findViewById(R.id.day_hour_side)).setText(array[position]);
        LinearLayout layout = (LinearLayout)convertView.findViewById(R.id.day_event_layout);

        //layout.addView(new EventFrame(parent.getContext()));

        TextView create = new TextView(DayViewActivity.this);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 62, getResources().getDisplayMetrics()), 1.0f);
        params.topMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        params.bottomMargin = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, getResources().getDisplayMetrics());
        create.setLayoutParams(params);
        create.setBackgroundColor(Color.BLUE);
        create.setText("Test"); 
        //the following is my original LinearLayout.LayoutParams for correctly setting the TextView Height
        //new LinearLayout.LayoutParams(0, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60, getResources().getDisplayMetrics()), 1.0f)   
        if(position == 0) {
            Log.d("DayViewActivity", "This should only be running when position is 0. The position is: "+position);
            layout.addView(create);
        }

        return convertView;
    }

}

Problem is when I scroll, this happens, and not on position 0... Looks like position 6 and position 8, plus it puts two in position 8. Now I am still trying to get the hang of using ListView and GridView so I do not understand why this is happening. One of the main reasons I am making this question is to help others who probably don't know about ListView and GridView's recycling View, or the way this article puts it, ScrapView mechanism.

enter image description here

Later Edit

Adding link to a google IO talk that is basically all you need to understand how ListView works. Link was dead in on of the comments. So user3427079 was nice enough to update that link. Here it is for easy access.

3 comments

@Muhammad Babar 2013-01-01 07:24:30

Initially I was also unaware of listview recycling and the convertview usage mechanism, but after a whole days research I pretty much understand the mechanisms of the list view by referring to an image from android.amberfog enter image description here

When ever your listview is filled with an adapter it basically shows the number of Rows that the listview can show on screen and the number of rows doesnt get increase even you scroll through the list, this is the trick android use so that listview work more effcient and fast, Now the inside story of listview refering to image as you can see initially list view has 7 visible items, if you scroll up when item 1 is not any more visible getView() pass this view(item1) to recycler and you can use

System.out.println("getview:"+position+" "+convertView);

inside your

public View getView(final int position, View convertView, ViewGroup parent)
{
    System.out.println("getview:"+position+" "+convertView);
    ViewHolder holder;
    View row=convertView;
    if(row==null)
    {
        LayoutInflater inflater=((Activity)context).getLayoutInflater();
        row=inflater.inflate(layoutResourceId, parent,false);

        holder=new PakistaniDrama();
        holder.tvDramaName=(TextView)row.findViewById(R.id.dramaName);
        holder.cbCheck=(CheckBox)row.findViewById(R.id.checkBox);

        row.setTag(holder);

    }
    else
    {
        holder=(PakistaniDrama)row.getTag();
    }
            holder.tvDramaName.setText(dramaList.get(position).getDramaName());
    holder.cbCheck.setChecked(checks.get(position));
            return row;
    }

you will notice in your logcat initially convertview is null for all the visible rows, because initially there were no view(item) in recycler, so your getView() creates each new view for the visible items, but the moment you scroll up your item 1 will send to the Recycler with it state(for example the TextView text and as in mine case if checkbox is checked it will also be associated with the view and store in recycler). Now when you scroll up/down your listview is not going to create a new view it will use the view(convert view) which is already in your recycler, in your Logcat you will notice that convertView is not null, its because your new item 8 will be drawn using convertview, i.e., basically it take the item1 view from the recycler and inflater in place of item 8, and you can observe that as in mine code if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked, this is why listview is re using the same view not creating a new for you due to performance optimization.

Important things

1. Never set the layout_height and layout_width of your listview to wrap_content as getView() will force your adapter to get some child for measuring the height of the views to be drawn in list view and can cause some unexpected behaviour like returning convertview even the list is not scrolled.always use match_parent or fixed width/height.

2. If you want to use some Layout or view after your list view and question might came in your mind if i set the layout_height to fill_parent the view after list view will not show up as it goes down the screen, so its better to put your listview inside a layout.For example Linear Layout and set the height and width of that layout as of your requirement and make the height and width attribute of your listview to as of your layout(like if your layout width is 320 and height is 280) then your listview should have same height and width. This will tell getView() of exact height and width of views to be rendered, and getView() won't call again and again some random rows, and other problems like returning convert view even before scrolling won't happen, i have test this myself, unless my listview was inside the lineaLayout it was also having problems like repeating view call and convert view as, putting Listview inside LinearLayout worked like magic for me.(didn't know why)

01-01 14:49:36.606: I/System.out(13871): getview 0 null
01-01 14:49:36.636: I/System.out(13871): getview 0 [email protected]
01-01 14:49:36.636: I/System.out(13871): getview 1 [email protected]
01-01 14:49:36.646: I/System.out(13871): getview 2 [email protected]
01-01 14:49:36.646: I/System.out(13871): getview 3 [email protected]
01-01 14:49:36.656: I/System.out(13871): getview 4 [email protected]
01-01 14:49:36.666: I/System.out(13871): getview 5 [email protected]
01-01 14:49:36.666: I/System.out(13871): getview 0 [email protected]
01-01 14:49:36.696: I/System.out(13871): getview 0 [email protected]
01-01 14:49:36.706: I/System.out(13871): getview 1 null
01-01 14:49:36.736: I/System.out(13871): getview 2 null
01-01 14:49:36.756: I/System.out(13871): getview 3 null
01-01 14:49:36.776: I/System.out(13871): getview 4 null

But now its solved, I know, I'm not that good at explaining but as i put my whole day to understand so i thought other beginners like me can get help of my experience and i hope now you people will have a little bit understanding of ListView framework how it works, as it is really messy and tricky so beginners found too much problem understanding it

@2Dee 2014-02-14 09:02:36

"I know im not that good at explaining" >>> The +37 votes on this answer tells me you did an excellent job at explaining. Please do keep sharing your knowledge ! ;)

@Witalo Benicio 2014-03-05 15:32:00

Man, i understood the recycle method used by Android, but im getting this weird behaviour in my app. I have a Custom ArrayAdapter that has a getview method, the thing is that method is being called for different views if i scroll down and up, and this make this views load again (e.g. downloading an image). In my ListActivity class, i only call adapter = new Adapter... and list.setAdapter() , after i download and parse a json (im using AQuery library). In Facebook app and Instagram, you scroll up and down, and everything looks normal...why?

@Muhammad Babar 2014-03-05 16:52:33

@WitaloBenicio When ever you scroll up and down getview get called and most probably you are downloading images again. You should cache the images like other apps do. and before downloading any image check if its available in the cache then use it and if not then download it!

@Witalo Benicio 2014-03-05 18:01:16

Yeah yeah, in fact my problem, is that i was using AQuery library, and then passing it by reference to a asynchronous method. I fix it.

@Ehsan Sajjad 2014-03-31 18:17:44

Great answer raja ji +1 from me..:)

@Sripathi 2014-06-13 06:19:48

@MuhammadBabar Nice explanation..

@neeraj kirola 2014-08-02 07:28:47

thanks a lot..for this well explained answer...

@Kevin Cruijssen 2014-08-05 13:00:21

Thanks you for the Image. After having a lot of troubles with ListView when I started with Android, I already knew the recycling principle after struggling with it so much. Still, it took this picture and the sentence: if you had a checkbox and if you check it at position 0(let say item1 has also a checkbox and you checked it) so when you scroll down you will see item 8 checkbox is already checked,this is why listview is re using the same view not creating a new for you due to performance optimization. for me to realize my mistake. Best post about Android ListView recycling I came across!

@Piyush Kukadiya 2014-10-10 06:18:17

Can you tell me one thing if i want to change height of list row then why it only works by setting height dynamically(convertView.setLayoutParams(new AbsListView.LayoutParams(AbsListView.LayoutParams.FILL_PAREN‌​T,80));) and does not work by defining height of row in xml statically ?

@Muhammad Babar 2014-10-10 06:47:13

It should work. Can you show me your list_row xml?

@Pratik Butani AndroidDev 2015-05-08 06:48:44

@MuhammadBabar I have problem like when i click on edittext inside of listview, the soft input keyboard changes numeric to alphabetic automatically. why its happens?

@Muhammad Babar 2015-05-08 09:54:42

@PratikButani this is strange. Can you show me your list item xml? and any related code that might be helpful.

@Pratik Butani AndroidDev 2015-05-08 10:54:22

@MuhammadBabar Yes, I have posted Question: stackoverflow.com/q/30122027/1318946

@Shambhu 2017-12-20 11:53:26

@MuhammadBabar awesome explaination man! It saves my time. Just want to know, does the RecyclerView also work on the same mechanism? In my question stackoverflow.com/questions/47897770/… , i know the task is quiet impossible with listView. Should I try with recyclerView?

@Muhammad Babar 2017-12-26 05:31:41

@Shambhu thanks, please check my comment there.

@Ananth Raj Singh 2019-01-13 18:28:47

Excellent answer, but try to use smaller sentences i.e. bit more full stops. That will increase readability and understandability. :)

@Krishna 2015-12-19 13:24:35

Using the Holder pattern you can achieve what you want:

You can find description of this pattern here:

Recycling of list view happens when you scroll down the screen and above list view items are hidden . They are reused to show new list view items.

@Ahmed Adel Ismail 2014-04-14 10:56:11

Take care, in the Holder pattern, if you set the postion in your Holder object, you should set it every time , for example :

@Override
public final View getView(int position, View view, ViewGroup parent) {
    Holder holder = null;
    if (view == null) {
        LayoutInflater inflater = (LayoutInflater) App.getContext()
                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = inflater.inflate(getContainerView(), parent, false);
        holder = getHolder(position, view, parent);
        holder.setTag(tag);
        view.setTag(holder);
    } else {
        holder = (Holder) view.getTag();
    }
    holder.position = position;
    draw(holder);
    return holder.getView();
}

this is an example from an abstract class, where

getHolder(position, view, parent);

does all the setting operations for the

ImageViews, TextViews, etc..

@Tarun 2015-12-30 09:01:33

u my friend are a rock star.....way to go man

@ArtiomLK 2017-10-01 23:06:34

Didn't notice that all this time I was doing. parent.setTag(holder); instead of the correct way which is, view.setTag(holder);

Related Questions

Sponsored Content

61 Answered Questions

[SOLVED] How do I center text horizontally and vertically in a TextView?

  • 2009-01-11 00:27:55
  • pupeno
  • 1117780 View
  • 1825 Score
  • 61 Answer
  • Tags:   android textview

51 Answered Questions

[SOLVED] How do I fix android.os.NetworkOnMainThreadException?

27 Answered Questions

77 Answered Questions

2 Answered Questions

3 Answered Questions

4 Answered Questions

[SOLVED] Parameters of getView method - android

  • 2013-06-21 13:35:56
  • JavaForAndroid
  • 2653 View
  • -1 Score
  • 4 Answer
  • Tags:   android parameters

1 Answered Questions

[SOLVED] Finding child views from parent view created programmatically in Android

  • 2012-03-03 23:26:27
  • contrapsych
  • 6095 View
  • 3 Score
  • 1 Answer
  • Tags:   android view

0 Answered Questions

Android calendar event use gridview listview

  • 2013-01-10 08:13:22
  • user1902932
  • 860 View
  • 0 Score
  • 0 Answer
  • Tags:   android gridview

2 Answered Questions

[SOLVED] Recycling views in a listview, worth it?

Sponsored Content