了解RecyclerView setHasFixedSize

时间:2015-02-25 00:49:17

标签: android android-5.0-lollipop android-recyclerview

我在理解setHasFixedSize()时遇到了一些麻烦。我知道,当文档中RecyclerView的大小没有变化时,它会用于优化。

这意味着什么?在大多数常见情况下,ListView几乎总是具有固定大小。在什么情况下它不是固定的大小?这是否意味着它在屏幕上占据的实际房地产会随着内容而增长?

9 个答案:

答案 0 :(得分:90)

非常简化版的RecyclerView具有:

void onItemsInsertedOrRemoved() {
   if (hasFixedSize) layoutChildren();
   else requestLayout();
}

This link描述了为什么调用requestLayout可能会很昂贵。基本上每当插入,移动或移除项目时,RecyclerView的大小(宽度和高度)可能会发生变化,反过来,视图层次结构中任何其他视图的大小也可能会发生变化。如果经常添加或删除项目,这尤其麻烦。

当更改适配器的内容不会改变它的高度或宽度时,通过将setHasFixedSize设置为true来避免不必要的布局传递。

更新:JavaDoc已更新,以更好地描述该方法实际执行的操作。

  

如果可以知道,RecyclerView可以执行多项优化   提前RecyclerView的大小不受适配器的影响   内容。 RecyclerView仍然可以根据其他方式更改其大小   因素(例如其父级的大小)但这个大小计算不能   取决于其子项的大小或其适配器的内容(除了   适配器中的项目数)。

如果您使用RecyclerView   属于此类别,将其设置为{@code true}。它会允许   RecyclerView避免在其适配器时使整个布局无效   内容改变。

     

@param hasFixedSize如果适配器更改不能影响大小,则为true   RecyclerView。

答案 1 :(得分:18)

可以确认setHasFixedSize与RecyclerView本身有关,而不是与其相适应的每个项目的大小。

您现在可以在RecyclerView上使用android:layout_height="wrap_content",其中包括允许CollapsingToolbarLayout在RecyclerView为空时不应该崩溃。这仅适用于在RecylcerView上使用setHasFixedSize(false)的情况。

如果在RecyclerView上使用setHasFixedSize(true),则此行为可防止CollapsingToolbarLayout崩溃,即使RecyclerView确实为空,也不起作用。

如果setHasFixedSize与商品尺寸相关,则当RecyclerView没有商品时,它不应该有任何效果。

答案 2 :(得分:12)

ListView有一个类似的命名函数,我认为它确实反映了有关各个列表项高度大小的信息。 RecyclerView的文档非常清楚地表明它指的是RecyclerView本身的大小,而不是其项目的大小。

来自setHasFixedSize()方法上方的RecyclerView源注释:

 * RecyclerView can perform several optimizations if it can know in advance that changes in
 * adapter content cannot change the size of the RecyclerView itself.
 * If your use of RecyclerView falls into this category, set this to true.

答案 3 :(得分:5)

setHasFixedSize(true)表示RecyclerView具有固定宽度和高度的子项(项)。这样可以通过根据适配器确定整个列表的确切高度和宽度来更好地优化RecyclerView。

答案 4 :(得分:3)

我们在setHasFixedSize(true)上设置了RecyclerView,这意味着回收站的大小是固定的,不受适配器内容的影响。在这种情况下,当我们更新适配器的数据时,onLayout不会在回收站上调用(但是有例外)。

我们来看例子:

RecyclerView的{​​{1}}(find default implemntation in this file)具有几种方法,最重要的是:

RecyclerViewDataObserver

如果我们设置void triggerUpdateProcessor() { if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) { ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable); } else { mAdapterUpdateDuringMeasure = true; requestLayout(); } } 并通过setHasFixedSize(true)更新适配器的数据,则会调用此方法。在这种情况下,没有调用回收者的notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved,但是有调用onLayout来更新子代。

但是,如果我们设置requestLayout并通过setHasFixedSize(true)更新适配器的数据,则会调用回收商默认notifyItemChanged中的onChange,而不会调用{{1} }。在这种情况下,只要我们设置RecyclerViewDataObserver triggerUpdateProcessoronLayout,就会调用回收器setHasFixedSize

true

如何亲自检查:

创建自定义false并覆盖:

// no calls to triggerUpdateProcessor
@Override
public void onChanged() {
    assertNotInLayoutOrScroll(null);
     mState.mStructureChanged = true;

     processDataSetCompletelyChanged(true);
     if (!mAdapterHelper.hasPendingUpdates()) {
         requestLayout();
     }
}

// calls to triggerUpdateProcessor
@Override
public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {
    assertNotInLayoutOrScroll(null);
    if (mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {
        triggerUpdateProcessor();
    }
}

将回收站大小设置为RecyclerView(以xml为单位)。尝试先将override fun requestLayout() { Log.d("CustomRecycler", "requestLayout is called") super.requestLayout() } override fun invalidate() { Log.d("CustomRecycler", "invalidate is called") super.invalidate() } override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) { Log.d("CustomRecycler", "onLayout is called") super.onLayout(changed, l, t, r, b) } match_parent设置为replaceDatareplaceOne,然后再使用setHasFixedSize(true)来更新适配器的数据。

false

并检查您的日志。

我的日志:

// onLayout is called every time
fun replaceAll(data: List<String>) {
    dataSet.clear()
    dataSet.addAll(data)
    this.notifyDataSetChanged()
}

// onLayout is called only for setHasFixedSize(false)
fun replaceOne(data: List<String>) {
    dataSet.removeAt(0)
    dataSet.addAll(0, data[0])
    this.notifyItemChanged(0)
}

总结:

如果我们设置// for replaceAll D/CustomRecycler: requestLayout is called D/CustomRecycler: onMeasure is called D/CustomRecycler: onMeasure is called D/CustomRecycler: onLayout D/CustomRecycler: requestLayout is called D/CustomRecycler: requestLayout is called D/CustomRecycler: onDraw is called // for replaceOne D/CustomRecycler: requestLayout is called D/CustomRecycler: onDraw is called D/CustomRecycler: requestLayout is called D/CustomRecycler: onDraw is called 并通过调用观察者而不是调用setHasFixedSize(true)来通知观察者来更新适配器的数据,那么您会有一些表现,因为对回收者notifyDataSetChanged的调用没有方法。

答案 5 :(得分:0)

它会影响recyclerview的动画,如果它是false ..插入和删除动画将不会显示。因此,如果您为recyclerview添加了动画,请确保它是true

答案 6 :(得分:0)

如果RecyclerView的大小(RecyclerView本身)

...不取决于适配器的内容:

mRecyclerView.setHasFixedSize(true);

...取决于适配器的内容:

mRecyclerView.setHasFixedSize(false);

答案 7 :(得分:0)

如果我们有一个RecyclerView作为{strong>高度/宽度的match_parent,则应该添加setHasFixedSize(true),因为RecyclerView本身的大小不会更改在其中插入或删除项目。

如果我们有一个wrap_content height / width 的RecyclerView,则

setHasFixedSize 应该为false,因为适配器插入的每个元素都可以更改{ {1}}取决于插入/删除的项目,因此,Recycler的大小在每次添加/删除项目时都会有所不同。

更清楚一点,如果我们使用

Recycler

我们可以使用<android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="match_parent"/>

my_recycler_view.setHasFixedSize(true)

我们应该使用<android.support.v7.widget.RecyclerView android:id="@+id/my_recycler_view" android:scrollbars="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"/> ,如果我们还使用my_recycler_view.setHasFixedSize(false)作为宽度

,则适用

答案 8 :(得分:-2)

我不知道我是否正确,但是我的前辈曾经说过,如果我将<slot>用于嵌套滚动视图中的回收器视图,则ui会像回收者视图将一次性加载所有项目(而不是像通常那样动态加载)

如果发现此错误,请读者纠正我的意见。