Home » Android » Android Adapter multiple getView

Android Adapter multiple getView

Posted by: admin June 15, 2020 Leave a comment

Questions:

i have read about the issue of getView called multiple times and all the answers. However, i don’t find a solution for my problem.

I have a list where rows have two states: read or not. Well, i want to the items seen for first time have a different color and when i scroll the list, they change their color to “read state”.

In order to do this, in the method getView of my adapter i set a field isRead when the row for that item is painted. But the problem is the following: since the method getView is called multiple times the field is marked as read and when the list is shown in the screen it appears as if it had already been read.

Any idea to fix this problem?

Thanks

How to&Answers:

I assume you mean the issue of getView requesting the same view several times.

ListView does this because it needs to get measurements for the views for different reasons (scrollbar size, layout, etc)

This issue can usually be avoided by not using the “wrap_content” property on your listview.

Other than that, using getView to determine if a view has been displayed is simply a bad idea. ListView has many optimizations that mess with the order getView is called on for each row, so there is no way to know what will happen and your app will start showing odd behavior.

Try to avoid any relationship between the view and the data other than the concept of view as a display of that data.

Instead, have some worker thread or event listener in your listactivity watch the list for which items in the list have been displayed to the user, update the data, and call dataSetChanged on your adaptor.

Answer:

I had the same problem and I had no reference at all to “wrap_content” in the layouts attirbute. Although this is an old thread I couldn’t figured it out how to solve the issue. Thus, I mitigated it by adding a List in the Adapter that holds the positions already drawn, as shown in the code below. I think that it is not the right away of doing that, but it worked for me.

public class ImageAdapter extends BaseAdapter {
    private Context mContext;
    private List<Integer> usedPositions  = new ArrayList<Integer>();

    public ImageAdapter(Context c, List<String> imageUrls) {
        mContext = c;
        ...
    }

    ...

    // create a new ImageView for each item referenced by the Adapter
    public View getView(int position, View convertView, ViewGroup parent) {
        ImageView imageView;
        if (convertView == null) { 
            imageView = new ImageView(mContext);
            imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
            imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
            imageView.setPadding(8, 8, 8, 8);
        } else {
            imageView = (ImageView) convertView;
        }

        if (!usedPositions.contains(position)) {
            // Your code to fill the imageView object content
            usedPositions.add(position); // holds the used position
        }

        return imageView;
    }
}

Answer:

Not the way you want it to work. The reason that getView() is called multiple times is to allow the OS to measure the rows so it knows how to lay them out. You would need to have it marked as read when they click it or check a box or something.

Answer:

Here is a very simple way of avoiding the double call when you know nothing below this block of code will distort the layout.

private static List<String> usedPositions  = new ArrayList<String>();

@Override
public View getView(int position, View rowView, ViewGroup parent) {
    rowView = inflater.inflate(R.layout.download_listview_item, null);

    Log.d(LOG_TAG, "--> Position: " + position);

    if (!usedPositions.contains(String.valueOf(position))){
        usedPositions.add(String.valueOf(position));
        return rowView;
    }else{
        usedPositions.remove(String.valueOf(position));
    }