I am having an unclear issue concerning the recycling of views in a getView method of a custom array adapter.

I understand that elements are reused, but how do I know exact what to implement in the first part of the if statement, and what in the second?

Right now I am having following code. I came to this question due to dropping the code in the second part of the statement which results in a list of the first 9 elements, which are repeated numberous times instead of all elements. I didn't really know what is causing this exactly...

@Override
    public View getView(int position, View convertView, ViewGroup parent) {
        View row = convertView;

        if (row == null) {
            LayoutInflater inflater = ((Activity) context).getLayoutInflater();
            row = inflater.inflate(layoutResourceId, parent, false);

            title = getItem(position).getTitle();
            size = calculateFileSize(position);

            txtTitle = (TextView) row.findViewById(R.id.txtTitle);
            tvFileSize = (TextView) row.findViewById(R.id.tvFileSize);

            txtTitle.setText(title);
            tvFileSize.setText(size);

        } else {

            title = getItem(position).getTitle();
            size = calculateFileSize(position);

            txtTitle = (TextView) row.findViewById(R.id.txtTitle);
            tvFileSize = (TextView) row.findViewById(R.id.tvFileSize);

            txtTitle.setText(title);
            tvFileSize.setText(size);
        }

        return row;
    } 
有帮助吗?

解决方案 2

I understand that elements are reused, but how do I know exact what to implement in the first part of the if statement, and what in the second?

The organization is quite simple once you get the hang of it:

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

    if (convertView == null) {
        /* This is where you initialize new rows, by:
         *  - Inflating the layout,
         *  - Instantiating the ViewHolder,
         *  - And defining any characteristics that are consistent for every row */
    } else {
        /* Fetch data already in the row layout, 
         *    primarily you only use this to get a copy of the ViewHolder */
    }

    /* Set the data that changes in each row, like `title` and `size`
     *    This is where you give rows there unique values. */

    return convertView;
}

For detailed explanations of how ListView's RecycleBin works and why ViewHolders are important watch Turbo Charge your UI, a Google I/O presentation by Android's lead ListView programmers.

其他提示

It's easy. The first time no row is created, so you have to inflate them. Afterwards, the Android os may decide to recycle the views that you already inflated and that are not visible anymore. Those are already inflated and passed into the convertView parameter, so all you have to do is to arrange it to show the new current item, for example placing the right values into the various text fields.

enter image description here

In short, in the first part you should perform the inflation AND fill the values, in the second if (if convertView != null) you should only overwrite the field because, given the view has been recycled, the textviews contain the values of the old item.

This post and this are good starting points

You want to create a ViewHolder class in your MainActivity. Something like

 static class ViewHolder
    {
        TextView tv1;
        TextView tv2;
    }

then in your getView, the first time you get your Views from your xml in the if and reuse them after that in the else

View rowView = convertView;
        if (rowView == null)
        {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            rowView = inflater.inflate(R.layout.layout_name_to_inflate, parent, false);
            holder = new ViewHolder();
            holder.tv1= (TextView) rowView.findViewById(R.id.textView1);
            holder.tv2 = (RadioGroup) rowView.findViewById(R.id.textView2);             
            rowView.setTag(holder);
        }
        else
        {
            holder = (ViewHolder) rowView.getTag();
        }

I would recommend that you use the View holder and convertview pattern to create your listView as it will be more efficient.Here is a good explanation of how it works with a re-use strategy. This will answer your question on how re-cycling works. If you want to refer to a code sample, I have it on GitHub.

Hope this helps.

The last part of the question I really couldn't grasp without a picture of the effect but for the first part "what to implement in the first part of the if statement, and what in the second" I think I've found the this implementation very common.

You would find the view references first and store them to a static class ViewHolder which then you attach to the tag of the new inflated view. As the listview recycles the views and a convertView is passed getView you get the ViewHolder from the convertView's tag so you don't have to find the references again (which greatly improves performance) and update the view data with that of your object at the position given.

Technically you don't care what position the view was since all you care for is the references to the views you need to update which are held within it's ViewHolder.

@Override
public View getView(int position, View convertView, ViewGroup container) {
    ViewHolder holder;
    Store store = getItem(position);
    if (convertView == null) {
        convertView = mLayoutInflater.inflate(R.layout.item_store, null);

        // create a holder to store references
        holder = new ViewHolder();

        // find references and store in holder
        ViewGroup logoPhoneLayout = (ViewGroup) convertView
                .findViewById(R.id.logophonelayout);
        ViewGroup addressLayout = (ViewGroup) convertView
                .findViewById(R.id.addresslayout);

        holder.image = (ImageView) logoPhoneLayout
                .findViewById(R.id.image1);
        holder.phone = (TextView) logoPhoneLayout
                .findViewById(R.id.textview1);
        holder.address = (TextView) addressLayout
                .findViewById(R.id.textview1);

        // store holder in views tag
        convertView.setTag(holder);
    } else {

        // Retrieve holder from view
        holder = (ViewHolder) convertView.getTag();
    }

    // fill in view with our store (at this position)
    holder.phone.setText(store.phone);
    holder.address.setText(store.getFullAddress());

    UrlImageViewHelper.setUrlDrawable(holder.image, store.storeLogoURL,
            R.drawable.no_image);

    return convertView;
}

private static class ViewHolder {
    ImageView image;
    TextView phone;
    TextView address;
}
许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top