Android RecyclerView创建并绑定数据集更改

时间:2016-05-16 12:46:04

标签: android android-recyclerview

我有一个非常大的应用程序,我几乎到处都使用了RecylclerViews, 我知道如何实现RecyclerViews,我没有遇到任何问题! 但是最近我在其中一个房车上遇到了一个非常糟糕的延迟,(让我向你保证我的bindview方法非常好,我加载了asynctasks中的所有图像......),这是第一次显示RV我得到一个ANR!

经过数小时的调试后,我发现每次数据集发生变化时(notifyDataset()),包括第一次RV填充,所有的视图都被创建和绑定(是的,就像onCreateView的200倍) )被称为,这与RecyclerView的理念正好相反!) 一旦创建并绑定了所有视图,应用程序就会开始正常运行,我的意思是不再需要onCreateView()和onBindView()调用所需的,没有任何延迟。

我知道找到原因真的很难,但我想也许有一个明显的设置或......导致SO用户的这个或过去的经验可以帮助我。

修改

显然原因是由于我的XML以及我如何使RV填充空闲区域,因为当我将其高度设置为指定值(例如400 dp)时,一切正常,但是如果我使用LinearLayout和layout_weight或与RelativeLayout一样(在align-top元素下面和在align-bottomed元素上面设置)会重现这个问题。

所以我可以说这个问题发生在需要动态计算RV的高度时我想它会创建所有它的孩子来计算它的高度,这显然是支持库中的一个错误(com.android) .support:recyclelerview-v7:当时23.4.0)

任何人都有解决方法吗? 或者也许可以告诉我我的错误?

解决方案

面向未来的googlers

似乎解决方案是在LayoutManager上使用setAutoMeasureEnabled(false);。 此方法已在支持库的23.2.0版本中引入,并且应该有助于将wrap_content用作RV项的布局参数, 无论如何我用这个方法克服了我的问题,并使用wrap_content为我的项目高度没有问题,

5 个答案:

答案 0 :(得分:1)

onBindViewHolder将被调用以显示视图。第一次设置适配器onBindViewHolder()时,将被调用200次。只会调用可见的项目数。 Fox示例,如果有4个项目可见,onBindViewHolder将被调用4次,因为滚动onBindViewHolder将被调用,具体取决于需要绘制的项目数。

如果某些数据正在更改并且您使用notifyDatasetChanged(),那么将重绘所有视图。如果要插入元素并想要刷新适配器,请使用类似

的内容
adapter.notifyItemInserted(position);

或删除

adapter.notifyItemRemoved(positionOfObject);
adapter.notifyItemRangeChanged(positionOfObject, arrayList.size());

效率更高

答案 1 :(得分:1)

Recycler View应仅针对创建时可见的行调用OnCreateViewHolder(...)和OnBindViewHolder(...)。如果您需要刷新列表中的行,当图像加载或类似的时候,您应该避免使用notifyDataSetChanged()。相反,您可以使用invalidateChild(...)方法之一。要找到合适的子项以使其无效,可以使用findViewHolderForAdapterPosition(int position)。另一种方法是使用适配器的notifyItemChanged(int position)方法。

答案 2 :(得分:1)

我遇到了类似的问题,我在ViewPager中使用了RecyclerView。将项目设置为适配器时,RecyclerView还将所有视图绑定两次。而且我在基本ViewPager类中发现了问题。我放置了一些代码行,以根据其子级动态计算ViewPager高度。当我删除该代码时,RecyclerView可以正常工作。该代码在下面;

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int mode = MeasureSpec.getMode(heightMeasureSpec);
    // Unspecified means that the ViewPager is in a ScrollView WRAP_CONTENT.
    // At Most means that the ViewPager is not in a ScrollView WRAP_CONTENT.
    if (mode == MeasureSpec.UNSPECIFIED || mode == MeasureSpec.AT_MOST) {
        // super has to be called in the beginning so the child views can be initialized.
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int height = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if (h > height) height = h;
        }
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    }
    // super has to be called again so the new specs are treated as exact measurements
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

答案 3 :(得分:0)

我遇到了类似的问题:onBindViewHolder要求所有数据输入,即使我希望只为可见数据调用它。

我发现这是由于我的视图持有人的大小由自动换行的内容定义的,而内容是我通过图像加载库异步加载的图像,因此当视图持有人最初绑定时,其大小为0 px,所有持有人都可以看到用户。并且仅在图像加载完成后才调整viewholdera的大小。但是到那时,观看者已经被束缚了。

答案 4 :(得分:0)

对于在android 5上运行的设备,我遇到了类似的问题,添加了列表中的所有元素及其创建问题。

当我更改它的高度时,我将其设置为wrap_content的高度,以使其可以正常工作。