Understanding RecyclerView setHasFixedSize

后端 未结 8 2054
闹比i
闹比i 2020-11-28 01:52

I\'m having some trouble understanding setHasFixedSize(). I know that it is used for optimization when the size of RecyclerView doesn\'t change, fr

8条回答
  •  我在风中等你
    2020-11-28 02:35

    Wen we set setHasFixedSize(true) on RecyclerView that means recycler's size is fixed and is not affected by the adapter contents. And in this case onLayout is not called on recycler when we update the adaptrer's data (but there is an exception).

    Let's go to the example:

    RecyclerView has a RecyclerViewDataObserver (find default implemntation in this file) with several methods, the main important is:

    void triggerUpdateProcessor() {
        if (POST_UPDATES_ON_ANIMATION && mHasFixedSize && mIsAttached) {
            ViewCompat.postOnAnimation(RecyclerView.this, mUpdateChildViewsRunnable);
        } else {
            mAdapterUpdateDuringMeasure = true;
            requestLayout();
        }
    }
    

    This method is called if we set setHasFixedSize(true) and update an adapter's data via: notifyItemRangeChanged, notifyItemRangeInserted, notifyItemRangeRemoved or notifyItemRangeMoved. In this case there is no calls to the recycler's onLayout, but there is calls to requestLayout for updating childs.

    But if we set setHasFixedSize(true) and update an adapter's data via notifyItemChanged then there is call to onChange of the recycler's default RecyclerViewDataObserver and no calls to triggerUpdateProcessor. In this case the recycler onLayout is called whenever we set setHasFixedSize true or 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();
        }
    }
    

    How to check by yourself:

    Create custom RecyclerView and override:

    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)
    }
    

    Set the recycler size to match_parent (in xml). Try to update adapter's data using replaceData and replaceOne with seting setHasFixedSize(true) and then false.

    // onLayout is called every time
    fun replaceAll(data: List) {
        dataSet.clear()
        dataSet.addAll(data)
        this.notifyDataSetChanged()
    }
    
    // onLayout is called only for setHasFixedSize(false)
    fun replaceOne(data: List) {
        dataSet.removeAt(0)
        dataSet.addAll(0, data[0])
        this.notifyItemChanged(0)
    }
    

    And check your log.

    My log:

    // 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
    

    Summarize:

    If we set setHasFixedSize(true) and update adapter's data with notifying an observer in some other way than calling notifyDataSetChanged, then you have some perfomance, because the is no calls to the recycler onLayout method.

提交回复
热议问题