HorizontalScrollView: CustomAdapter with getView() doesn't reuse convertViews like ListView

无人久伴 提交于 2019-12-07 12:22:46

问题


In one of my past projects I implemented a "Time Picker Carousel". It is based on a HorizontalScrollView. The user can pick a time while scrolling this view. The time value is calculated from the X-Offset of the HorizontalScrollView.

I wanted to share this project at github, but while cleaning up the code I realized some bad performance issue.

The HorizontalScrollView is populated with a custom ArrayAdapter. The getView() uses a Holder for the convertView. I thought it might work as an adapter within a ListView, so only the visible items are rendered and reused if they will be destroyed. Instead, all items are rendered!!! (in my case 1008!) I add them myself (#1 in Code example), but even if I try to add less, the logic from remove (and recycle) old ones doesn't work, or did I miss something?

So my basic question is: What do I have to change to make my adapter behave like ListView?

  1. I found this so link and tried overriding remove function, but this is never called (somehow logic, because i am only adding)
  2. There's a good PagerAdapter example at github, but somehow I cannot translate this to ArrayAdapter<String>

Any thoughts, links are welcome!

And please don't suggest using ListView instead. We decided to use HorizontalScrollView because of callbacks and the fact we already have a ListView in the layout.

HorizontalScrollView

InfiniteTimeScrubberHorizontalView extends HorizontalScrollView{
...
public void setAdapter(Context context, TimeScrubberListAdapter mAdapter) {
    this.mAdapter = mAdapter;
    try {
        fillViewWithAdapter(mAdapter);
    } catch (ZeroChildException e) {
        e.printStackTrace();
    }
}

private void fillViewWithAdapter(TimeScrubberListAdapter mAdapter) {
    //...

    ViewGroup parent = (ViewGroup) getChildAt(0);
    parent.removeAllViews();
    for (int i = 0; i < mAdapter.getCount(); i++) {
        //#1: Here: ALL views are added
        parent.addView(mAdapter.getView(i, null, parent));   
    }
}

Adpater

public class TimeScrubberListAdapter extends ArrayAdapter<String> {

//...
private ArrayList<String> list;  //list from 0:00,0:30...23:00,23:30
final static int MAXIMUM_DAYS = 21

@Override
public int getCount() {
    return list.size() * MAXIMUM_DAYS;
}

@Override
public String getItem(int position) {
    return list.get(position % list.size());
}

@Override
public View getView(final int position, View convertView, ViewGroup parent) {
    RelativeLayout layout;

    if (convertView == null) {
        layout = (RelativeLayout) View.inflate(context, layoutId, null);
        holder = new Holder();
        holder.title = (TextView) layout.findViewById(R.id.epg_item_text);
        layout.setTag(holder);
    } else {
        layout = (RelativeLayout) convertView;
        view = layout;
        holder = (Holder) layout.getTag();
    }

    layout.setLayoutParams(mLP);        

    String timeValue = getItem(position);
    holder.title.setText(timeValue);            

    return layout;
}
//...
@Override
public void remove(String object) {
    //not called...some how logic, because i do not remove an item
    super.remove(object);    
}

回答1:


I think maybe you are confusing where the logic lies for rendering between View and Adapter. Using an Adapter does not cause view recycling behavior. It is the ListView itself, along with its parent, AbsListView, that implement the view recycling behavior. The Adapter is required by the ListView in order to properly populate the Views that are shown on screen as needed, but the logic for actually choosing which Views to display and when and how to recycle those views is not in the Adapter at all.

If you look at the source code for HorizontalScrollView and for ListView, you will see that they are dramatically different. They are not just the same thing in different orientations.

So, the long and short of it is that you will not be able to get the view recycling that you are looking for by using a HorizontalScrollView or even a simple descendent. If you want view recycling, you should check out the RecyclerView.




回答2:


ScrollView keeps all child views in memory, so performance issue is common while using them.

Why not use some open source HorizontalListView library. On searching I got these two libs out of which i have used first one in one of my projects and it worked smoothly without any memory issues.

  • Android-HorizontalListView

  • HorizontalVariableListView

These libs are horizontal implementation of ListView, which does recycling of items for you based on which views are visible on screen. Scrollview is bad choice for inifinite scrolling view.

UPDATE: Now we have RecyclerView from Android itself, which supports horizontal scrolling as well, it also enforces the use of Viewholder pattern



来源:https://stackoverflow.com/questions/26341587/horizontalscrollview-customadapter-with-getview-doesnt-reuse-convertviews-li

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!