日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > Android >内容正文

Android

android流程化步骤样式,Android RecyclerView 解析之绘制流程篇

發布時間:2024/9/19 Android 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 android流程化步骤样式,Android RecyclerView 解析之绘制流程篇 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

前言: 當前市場上有很多成熟的RecyclerView分析文章,但那始終是其他人總結出來的,還得自己動手分析,才知道自己理解了有多少,當然這個也算是加深對RecyclerView對理解吧;

官方簡介:A flexible view for providing a limited window into a large data set.

一種靈活的視圖,在有限的窗口,展示大量的數據集;

在開始之前,為了加深理解,我們需要帶著疑問進行閱讀;

(1),RecyclerView是怎么加載數據的?

(2),RecyclerView是怎么將View繪制到頁面上的?

(3),RecyclerView是怎么復用item的?

1.1 總體結構

RecyclerView主體架構.png

由上圖可知,RecyclerView主要由這幾部分組成;那他們的關系是啥呢? 具體是如何關聯的呢?且聽完細細道來!

數據層面:首頁RecyclerView需要將數據和view綁定起來,是通過Adapter加載ViewHolder來實現綁定數據的;

布局層面:RecyclerView的Item的布局是通過LayoutManager來進行布局的;

復用層面:LayoutManger從Recycler獲取item來進行復用;

總結:

1,Adapter:將數據轉化為RecyclerView可以識別的數據;

2,ViewHolder:將數據和item綁定起來;

3,LayoutManager:通過計算將Item布局到頁面中;

4,Recycler:復用機制,統一管理Item,用于復用;

5,ItemDecoration:繪制item的樣式;

1.2 具體流程:

1.2.1 RecyclerView 初始化流程

首先,先來看看RecyclerView 的初始化流程,先舉個簡單的例子;

//獲取RecyclerView 控件

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);

//創建adapter

MyAdapter adapter = new MyAdapter(list);

//創建LayoutManager

LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext());

//設置LayoutManager

recyclerView.setLayoutManager(linearLayoutManager);

//設置Adapter

recyclerView.setAdapter(adapter);

1,我們先來看看RecyclerView 的構造方法做了啥?

public RecyclerView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

//創建觀察者

this.mObserver = new RecyclerView.RecyclerViewDataObserver();

//創建回收器

this.mRecycler = new RecyclerView.Recycler();

//創建布局信息保存類

this.mViewInfoStore = new ViewInfoStore();

this.mUpdateChildViewsRunnable = new Runnable() {

public void run() {

if (RecyclerView.this.mFirstLayoutComplete && !RecyclerView.this.isLayoutRequested()) {

if (!RecyclerView.this.mIsAttached) {

RecyclerView.this.requestLayout();

} else if (RecyclerView.this.mLayoutFrozen) {

RecyclerView.this.mLayoutWasDefered = true;

} else {

RecyclerView.this.consumePendingUpdateOperations();

}

}

}

};

...

if (attrs != null) {

TypedArray a = context.obtainStyledAttributes(attrs, CLIP_TO_PADDING_ATTR, defStyle, 0);

this.mClipToPadding = a.getBoolean(0, true);

a.recycle();

} else {

this.mClipToPadding = true;

}

...

this.mAccessibilityManager = (AccessibilityManager)this.getContext().getSystemService("accessibility");

this.setAccessibilityDelegateCompat(new RecyclerViewAccessibilityDelegate(this));

boolean nestedScrollingEnabled = true;

if (attrs != null) {

int defStyleRes = 0;

TypedArray a = context.obtainStyledAttributes(attrs, styleable.RecyclerView, defStyle, defStyleRes);

//從布局文件獲取Layoutmanger的名稱

String layoutManagerName = a.getString(styleable.RecyclerView_layoutManager);

int descendantFocusability = a.getInt(styleable.RecyclerView_android_descendantFocusability, -1);

if (descendantFocusability == -1) {

this.setDescendantFocusability(262144);

}

this.mEnableFastScroller = a.getBoolean(styleable.RecyclerView_fastScrollEnabled, false);

//通過layoutManger的名稱進行反射創建layoutManager,并設置給RecycleView

this.createLayoutManager(context, layoutManagerName, attrs, defStyle, defStyleRes);

...

} else {

this.setDescendantFocusability(262144);

}

//設置是否支持嵌套滾動,默認為true

this.setNestedScrollingEnabled(nestedScrollingEnabled);

}

從構造方法可以看出,里面做了一大堆初始化的操作,最主要看一下這個創建layoutManager的方法createLayoutManager();

根據布局屬性進行反射來創建layoutManager;

private void createLayoutManager(Context context, String className, AttributeSet attrs, int defStyleAttr, int defStyleRes) {

if (className != null) {

className = className.trim();

if (!className.isEmpty()) {

className = this.getFullClassName(context, className);

try {

ClassLoader classLoader;

if (this.isInEditMode()) {

classLoader = this.getClass().getClassLoader();

} else {

classLoader = context.getClassLoader();

}

Class extends RecyclerView.LayoutManager> layoutManagerClass = classLoader.loadClass(className).asSubclass(RecyclerView.LayoutManager.class);

Object[] constructorArgs = null;

Constructor constructor;

try {

//通過反射創建布局構造器

constructor = layoutManagerClass.getConstructor(LAYOUT_MANAGER_CONSTRUCTOR_SIGNATURE);

constructorArgs = new Object[]{context, attrs, defStyleAttr, defStyleRes};

} catch (NoSuchMethodException var13) {

try {

constructor = layoutManagerClass.getConstructor();

} catch (NoSuchMethodException var12) {

var12.initCause(var13);

throw new IllegalStateException(attrs.getPositionDescription() + ": Error creating LayoutManager " + className, var12);

}

}

constructor.setAccessible(true);

//將創建出來的LayoutManger設置給RecycleView

this.setLayoutManager((RecyclerView.LayoutManager)constructor.newInstance(constructorArgs));

} catch (ClassNotFoundException var14) {

throw new IllegalStateException(attrs.getPositionDescription() + ": Unable to find LayoutManager " + className, var14);

} catch (InvocationTargetException var15) {

throw new IllegalStateException(attrs.getPositionDescription() + ": Could not instantiate the LayoutManager: " + className, var15);

} catch (InstantiationException var16) {

throw new IllegalStateException(attrs.getPositionDescription() + ": Could not instantiate the LayoutManager: " + className, var16);

} catch (IllegalAccessException var17) {

throw new IllegalStateException(attrs.getPositionDescription() + ": Cannot access non-public constructor " + className, var17);

} catch (ClassCastException var18) {

throw new IllegalStateException(attrs.getPositionDescription() + ": Class is not a LayoutManager " + className, var18);

}

}

}

}

再看一下setLayoutManager()這個方法里面做了啥操作?

public void setLayoutManager(@Nullable RecyclerView.LayoutManager layout) {

if (layout != this.mLayout) {

//停止當前的滾動操作

this.stopScroll();

if (this.mLayout != null) {

//判斷當前的layoutManager如果為空,則將該layoutManager的狀態進行初始化;

if (this.mItemAnimator != null) {

this.mItemAnimator.endAnimations();

}

this.mLayout.removeAndRecycleAllViews(this.mRecycler);

this.mLayout.removeAndRecycleScrapInt(this.mRecycler);

this.mRecycler.clear();

if (this.mIsAttached) {

this.mLayout.dispatchDetachedFromWindow(this, this.mRecycler);

}

this.mLayout.setRecyclerView((RecyclerView)null);

this.mLayout = null;

} else {

this.mRecycler.clear();

}

this.mChildHelper.removeAllViewsUnfiltered();

//將當前的layoutManager賦值給成員變量

this.mLayout = layout;

if (layout != null) {

if (layout.mRecyclerView != null) {

throw new IllegalArgumentException("LayoutManager " + layout + " is already attached to a RecyclerView:" + layout.mRecyclerView.exceptionLabel());

}

//將當前的RecyclerView賦值給layoutManager

this.mLayout.setRecyclerView(this);

if (this.mIsAttached) {

this.mLayout.dispatchAttachedToWindow(this);

}

}

//更新一下RecyclerView的緩存

this.mRecycler.updateViewCacheSize();

//觸發重新布局

this.requestLayout();

}

}

總結:看完RecyclerView的構造方法,里面主要是做了一些初始化的操作,并創建了layoutManager設置給RecyclerView(如果布局屬性有設置的話);

2,看完了RecyclerView的setLayoutManager()的流程,我們繼續接著分析,看一下setAdapter()具體做了啥?

public void setAdapter(@Nullable RecyclerView.Adapter adapter) {

this.setLayoutFrozen(false);

//主要模塊

this.setAdapterInternal(adapter, false, true);

this.processDataSetCompletelyChanged(false);

this.requestLayout();

}

跟進源碼,我們主要分析setAdapterInternal()這個方法,讓我們看看這個源碼里面做了什么操作;

private void setAdapterInternal(@Nullable RecyclerView.Adapter adapter, boolean compatibleWithPrevious, boolean removeAndRecycleViews) {

if (this.mAdapter != null) {

//解注冊之前的數據觀察者

this.mAdapter.unregisterAdapterDataObserver(this.mObserver);

this.mAdapter.onDetachedFromRecyclerView(this);

}

if (!compatibleWithPrevious || removeAndRecycleViews) {

//進行初始化操作,初始化layoutManger,初始化mRecycler

this.removeAndRecycleViews();

}

this.mAdapterHelper.reset();

RecyclerView.Adapter oldAdapter = this.mAdapter;

//將adapter賦值給當前成員變量

this.mAdapter = adapter;

if (adapter != null) {

//adapter注冊數據觀察者,用于監聽數據的增刪改查

adapter.registerAdapterDataObserver(this.mObserver);

adapter.onAttachedToRecyclerView(this);

}

if (this.mLayout != null) {

this.mLayout.onAdapterChanged(oldAdapter, this.mAdapter);

}

this.mRecycler.onAdapterChanged(oldAdapter, this.mAdapter, compatibleWithPrevious);

this.mState.mStructureChanged = true;

}

這個方法里面主要是給adapter注冊數據監聽,用于數據的增刪改查的刷新,并做一些初始化的操作;

我們再看一下這個觀察者里面主要做了什么操作,具體的實現是在RecyclerViewDataObserver 這個類里面;

private class RecyclerViewDataObserver extends RecyclerView.AdapterDataObserver {

RecyclerViewDataObserver() {

}

public void onChanged() {

RecyclerView.this.assertNotInLayoutOrScroll((String)null);

RecyclerView.this.mState.mStructureChanged = true;

RecyclerView.this.processDataSetCompletelyChanged(true);

if (!RecyclerView.this.mAdapterHelper.hasPendingUpdates()) {

RecyclerView.this.requestLayout();

}

}

public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {

RecyclerView.this.assertNotInLayoutOrScroll((String)null);

if (RecyclerView.this.mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {

this.triggerUpdateProcessor();

}

}

public void onItemRangeInserted(int positionStart, int itemCount) {

RecyclerView.this.assertNotInLayoutOrScroll((String)null);

if (RecyclerView.this.mAdapterHelper.onItemRangeInserted(positionStart, itemCount)) {

this.triggerUpdateProcessor();

}

}

public void onItemRangeRemoved(int positionStart, int itemCount) {

RecyclerView.this.assertNotInLayoutOrScroll((String)null);

if (RecyclerView.this.mAdapterHelper.onItemRangeRemoved(positionStart, itemCount)) {

this.triggerUpdateProcessor();

}

}

public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {

RecyclerView.this.assertNotInLayoutOrScroll((String)null);

if (RecyclerView.this.mAdapterHelper.onItemRangeMoved(fromPosition, toPosition, itemCount)) {

this.triggerUpdateProcessor();

}

}

void triggerUpdateProcessor() {

if (RecyclerView.POST_UPDATES_ON_ANIMATION && RecyclerView.this.mHasFixedSize && RecyclerView.this.mIsAttached) {

ViewCompat.postOnAnimation(RecyclerView.this, RecyclerView.this.mUpdateChildViewsRunnable);

} else {

RecyclerView.this.mAdapterUpdateDuringMeasure = true;

RecyclerView.this.requestLayout();

}

}

}

看到了我們很熟悉的方法,即adapter刷新數據所調用的方法;我們主要分析其中一個方法即可,讓我們來看一下onItemRangeChanged()這個方法;

這里面主要分為兩步:

public void onItemRangeChanged(int positionStart, int itemCount, Object payload) {

RecyclerView.this.assertNotInLayoutOrScroll((String)null);

//這里通過AdapterHelper將傳進來的信息保存起來

if (RecyclerView.this.mAdapterHelper.onItemRangeChanged(positionStart, itemCount, payload)) {

//重新布局

this.triggerUpdateProcessor();

}

}

(1)通過AdapterHelper將傳進來的信息保存起來;

boolean onItemRangeChanged(int positionStart, int itemCount, Object payload) {

if (itemCount < 1) {

return false;

} else {

this.mPendingUpdates.add(this.obtainUpdateOp(4, positionStart, itemCount, payload));

this.mExistingUpdateTypes |= 4;

return this.mPendingUpdates.size() == 1;

}

}

(2)通過triggerUpdateProcessor()方法觸發RecyclerView重新布局;

void triggerUpdateProcessor() {

if (RecyclerView.POST_UPDATES_ON_ANIMATION && RecyclerView.this.mHasFixedSize && RecyclerView.this.mIsAttached) {

//當前有動畫正在執行的時候會走這里

ViewCompat.postOnAnimation(RecyclerView.this, RecyclerView.this.mUpdateChildViewsRunnable);

} else {

//觸發重新布局

RecyclerView.this.mAdapterUpdateDuringMeasure = true;

RecyclerView.this.requestLayout();

}

}

1,RecyclerView的主要繪制流程;

2,復用機制;

2. 工作流程

2.1 主體關系

首先我們來看一下各個模塊的關系;

關系圖.png

通過上圖大體可以看出這幾個模塊的關系:

(1)RecyclerView通過LayoutManager來進行布局操作;

(2)LayoutManager從Recycler里面獲取復用的item來進行布局;

(3)Recycler管理著ViewHolder的創建與復用;

(4)Adapter將數據和ViewHolder綁定起來,并和RecyclerView注冊觀察者;

(5)RecyclerView通過ItemDecoration進行item樣式的繪制;

接下來通過源碼來細細剖析,看看具體是怎么實現的;

那么我們接著上面分析的setAdapter()方法繼續分析,在setAdapter()方法里,最后調用來requestLayout(),來觸發RecyclerView 的繪制流程;

這個requestLayout()這個方法最終會調用到ViewRootImp里面的requestLayout()方法;

public void requestLayout() {

if (!mHandlingLayoutInLayoutRequest) {

checkThread();

mLayoutRequested = true;

//觸發繪制流程

scheduleTraversals();

}

}

在ViewRootImp里調用requestLayout()方法進行繪制,我們主要看scheduleTraversals()方法,里面最終會調用到performTraversals()方法,源碼如下;

void scheduleTraversals() {

if (!mTraversalScheduled) {

mTraversalScheduled = true;

mTraversalBarrier = mHandler.getLooper().getQueue().postSyncBarrier();

mChoreographer.postCallback(

Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null);

if (!mUnbufferedInputDispatch) {

scheduleConsumeBatchedInput();

}

notifyRendererOfFramePending();

pokeDrawLockIfNeeded();

}

}

...

final class TraversalRunnable implements Runnable {

@Override

public void run() {

doTraversal();

}

}

final TraversalRunnable mTraversalRunnable = new TraversalRunnable();

...

void doTraversal() {

if (mTraversalScheduled) {

mTraversalScheduled = false;

mHandler.getLooper().getQueue().removeSyncBarrier(mTraversalBarrier);

if (mProfile) {

Debug.startMethodTracing("ViewAncestor");

}

performTraversals();

if (mProfile) {

Debug.stopMethodTracing();

mProfile = false;

}

}

}

...

performTraversals()這個方法里面執行了三大步驟,測量(measure),布局(layout),繪制(draw),完成的view的工作流程,將頁面繪制出來;

{

// cache mView since it is used so much below...

final View host = mView;

...

if (!mStopped || mReportNextDraw) {

//執行view的測量流程

performMeasure(childWidthMeasureSpec, childHeightMeasureSpec);

} else {

...

}

...

if (didLayout) {

//執行view的布局流程

performLayout(lp, mWidth, mHeight);

...

}

...

if (!cancelDraw && !newSurface) {

...

//執行view的繪制流程

performDraw();

} else {

...

}

}

從上面整理的方法來看,繪制流程主要是這performMeasure(),performLayout(),performDraw();最終會觸發RecyclerView的onMeasure(),onLayout(),onDraw()方法,具體源碼這里就不過多分析了,感興趣的可以看一下View的繪制流程;

讓我們一個個來進行分析,先看看RecyclerView的onMeasure()方法里面做了什么?

onMeasure()分析:

protected void onMeasure(int widthSpec, int heightSpec) {

if (mLayout == null) {

//1.判斷當前的LayoutManger是否為空,為空則走RecyclerView默認測量的方法 ;

defaultOnMeasure(widthSpec, heightSpec);

return;

}

//2.LayoutManger開啟自動測量時走這里處理邏輯;

if (mLayout.mAutoMeasure) {

final int widthMode = MeasureSpec.getMode(widthSpec);

final int heightMode = MeasureSpec.getMode(heightSpec);

final boolean skipMeasure = widthMode == MeasureSpec.EXACTLY

&& heightMode == MeasureSpec.EXACTLY;

mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

if (skipMeasure || mAdapter == null) {

return;

}

if (mState.mLayoutStep == State.STEP_START) {

dispatchLayoutStep1();

}

// set dimensions in 2nd step. Pre-layout should happen with old dimensions for

// consistency

mLayout.setMeasureSpecs(widthSpec, heightSpec);

mState.mIsMeasuring = true;

dispatchLayoutStep2();

// now we can get the width and height from the children.

mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

// if RecyclerView has non-exact width and height and if there is at least one child

// which also has non-exact width & height, we have to re-measure.

if (mLayout.shouldMeasureTwice()) {

mLayout.setMeasureSpecs(

MeasureSpec.makeMeasureSpec(getMeasuredWidth(), MeasureSpec.EXACTLY),

MeasureSpec.makeMeasureSpec(getMeasuredHeight(), MeasureSpec.EXACTLY));

mState.mIsMeasuring = true;

dispatchLayoutStep2();

// now we can get the width and height from the children.

mLayout.setMeasuredDimensionFromChildren(widthSpec, heightSpec);

}

} else {

//3.LayoutManger沒有開啟自動測量時走這里處理邏輯;

if (mHasFixedSize) {

mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

return;

}

// custom onMeasure

if (mAdapterUpdateDuringMeasure) {

eatRequestLayout();

onEnterLayoutOrScroll();

processAdapterUpdatesAndSetAnimationFlags();

onExitLayoutOrScroll();

if (mState.mRunPredictiveAnimations) {

mState.mInPreLayout = true;

} else {

// consume remaining updates to provide a consistent state with the layout pass.

mAdapterHelper.consumeUpdatesInOnePass();

mState.mInPreLayout = false;

}

mAdapterUpdateDuringMeasure = false;

resumeRequestLayout(false);

}

if (mAdapter != null) {

mState.mItemCount = mAdapter.getItemCount();

} else {

mState.mItemCount = 0;

}

eatRequestLayout();

mLayout.onMeasure(mRecycler, mState, widthSpec, heightSpec);

resumeRequestLayout(false);

mState.mInPreLayout = false; // clear

}

}

這里面主要分三種情況,而我們大部分情況都是走第三步,通過查看官方的LayoutManger的源碼得知,LinearLayoutManager和StaggeredGridLayoutManager都開啟了自動測試,而GridLayoutManager繼承自LinearLayoutManager;所以,官方的LayoutManager都開啟了自動測量,這里我們只需要關注第二步的邏輯;

從上面源碼可以看出,RecyclerView通過LayoutManger里的onMeasure()來進行測量操作;

通過State這個類來進行布局和測試狀態的記錄,這里的mLayoutStep 包括STEP_START、STEP_LAYOUT 、 STEP_ANIMATIONS三個狀態;

從源碼分析,此時測量完畢之后,判斷當前狀態為開始的時候(STEP_START),調用了dispatchLayoutStep1()進行了一系列的操作,這個方法執行完了之后,會將mLayoutStep 賦值為STEP_LAYOUT;后面就執行了dispatchLayoutStep2(),在這個方法里將mLayoutStep 賦值為STEP_ANIMATIONS;

這里我們可以理解為,RecyclerView在測量完畢之后,就開始進行布局了,分別執行了dispatchLayoutStep1()和dispatchLayoutStep2()方法;到此onMeasure()分析完了;

讓我們繼續接著往下看,此時RecyclerView的onMeasure()已經執行完了,接下來會執行onLayout()方法,讓我們看看這個方法里面做了啥?

onLayout()分析:

先看一下源碼

protected void onLayout(boolean changed, int l, int t, int r, int b) {

TraceCompat.beginSection("RV OnLayout");

//執行布局操作

this.dispatchLayout();

TraceCompat.endSection();

this.mFirstLayoutComplete = true;

}

主要看dispatchLayout()這個方法

void dispatchLayout() {

if (mAdapter == null) {

Log.e(TAG, "No adapter attached; skipping layout");

// leave the state in START

return;

}

if (mLayout == null) {

Log.e(TAG, "No layout manager attached; skipping layout");

// leave the state in START

return;

}

mState.mIsMeasuring = false;

if (mState.mLayoutStep == State.STEP_START) {

dispatchLayoutStep1();

mLayout.setExactMeasureSpecsFrom(this);

dispatchLayoutStep2();

} else if (mAdapterHelper.hasUpdates() || mLayout.getWidth() != getWidth()

|| mLayout.getHeight() != getHeight()) {

// First 2 steps are done in onMeasure but looks like we have to run again due to

// changed size.

mLayout.setExactMeasureSpecsFrom(this);

dispatchLayoutStep2();

} else {

// always make sure we sync them (to ensure mode is exact)

mLayout.setExactMeasureSpecsFrom(this);

}

dispatchLayoutStep3();

}

通過上面源碼可以看出,之前在onMeasure()里的這個dispatchLayoutStep2()方法里面已經把mLayoutStep 賦值為STEP_ANIMATIONS,那么這里就會走最后一個方法dispatchLayoutStep3();如果沒有執行STEP_START方法,那么就會依次執行dispatchLayoutStep1(),dispatchLayoutStep2(),dispatchLayoutStep3()這幾個布局方法;讓我們來一個個分析;

dispatchLayoutStep1():

private void dispatchLayoutStep1() {

mState.assertLayoutStep(State.STEP_START);

mState.mIsMeasuring = false;

eatRequestLayout();

mViewInfoStore.clear();

onEnterLayoutOrScroll();

processAdapterUpdatesAndSetAnimationFlags();

saveFocusInfo();

mState.mTrackOldChangeHolders = mState.mRunSimpleAnimations && mItemsChanged;

mItemsAddedOrRemoved = mItemsChanged = false;

mState.mInPreLayout = mState.mRunPredictiveAnimations;

mState.mItemCount = mAdapter.getItemCount();

findMinMaxChildLayoutPositions(mMinMaxLayoutPositions);

if (mState.mRunSimpleAnimations) {

// Step 0: Find out where all non-removed items are, pre-layout

int count = mChildHelper.getChildCount();

for (int i = 0; i < count; ++i) {

final ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));

if (holder.shouldIgnore() || (holder.isInvalid() && !mAdapter.hasStableIds())) {

continue;

}

final ItemHolderInfo animationInfo = mItemAnimator

.recordPreLayoutInformation(mState, holder,

ItemAnimator.buildAdapterChangeFlagsForAnimations(holder),

holder.getUnmodifiedPayloads());

mViewInfoStore.addToPreLayout(holder, animationInfo);

if (mState.mTrackOldChangeHolders && holder.isUpdated() && !holder.isRemoved()

&& !holder.shouldIgnore() && !holder.isInvalid()) {

long key = getChangedHolderKey(holder);

// This is NOT the only place where a ViewHolder is added to old change holders

// list. There is another case where:

// * A VH is currently hidden but not deleted

// * The hidden item is changed in the adapter

// * Layout manager decides to layout the item in the pre-Layout pass (step1)

// When this case is detected, RV will un-hide that view and add to the old

// change holders list.

mViewInfoStore.addToOldChangeHolders(key, holder);

}

}

}

if (mState.mRunPredictiveAnimations) {

// Step 1: run prelayout: This will use the old positions of items. The layout manager

// is expected to layout everything, even removed items (though not to add removed

// items back to the container). This gives the pre-layout position of APPEARING views

// which come into existence as part of the real layout.

// Save old positions so that LayoutManager can run its mapping logic.

saveOldPositions();

final boolean didStructureChange = mState.mStructureChanged;

mState.mStructureChanged = false;

// temporarily disable flag because we are asking for previous layout

mLayout.onLayoutChildren(mRecycler, mState);

mState.mStructureChanged = didStructureChange;

for (int i = 0; i < mChildHelper.getChildCount(); ++i) {

final View child = mChildHelper.getChildAt(i);

final ViewHolder viewHolder = getChildViewHolderInt(child);

if (viewHolder.shouldIgnore()) {

continue;

}

if (!mViewInfoStore.isInPreLayout(viewHolder)) {

int flags = ItemAnimator.buildAdapterChangeFlagsForAnimations(viewHolder);

boolean wasHidden = viewHolder

.hasAnyOfTheFlags(ViewHolder.FLAG_BOUNCED_FROM_HIDDEN_LIST);

if (!wasHidden) {

flags |= ItemAnimator.FLAG_APPEARED_IN_PRE_LAYOUT;

}

final ItemHolderInfo animationInfo = mItemAnimator.recordPreLayoutInformation(

mState, viewHolder, flags, viewHolder.getUnmodifiedPayloads());

if (wasHidden) {

recordAnimationInfoIfBouncedHiddenView(viewHolder, animationInfo);

} else {

mViewInfoStore.addToAppearedInPreLayoutHolders(viewHolder, animationInfo);

}

}

}

// we don't process disappearing list because they may re-appear in post layout pass.

clearOldPositions();

} else {

clearOldPositions();

}

onExitLayoutOrScroll();

resumeRequestLayout(false);

mState.mLayoutStep = State.STEP_LAYOUT;

}

這個方法主要做了ViewHolder信息的保存,里面通過遍歷當前的子View,根據子view的位置信息創建ItemHolderInfo,并添加到 ViewInfoStore這個類里面進行保存;

看一下ItemHolderInfo這個類;

public static class ItemHolderInfo {

public int left;

public int top;

public int right;

public int bottom;

public ItemHolderInfo() {

}

...

public ItemHolderInfo setFrom(@NonNull RecyclerView.ViewHolder holder,

@AdapterChanges int flags) {

final View view = holder.itemView;

this.left = view.getLeft();

this.top = view.getTop();

this.right = view.getRight();

this.bottom = view.getBottom();

return this;

}

}

class ViewInfoStore {

private static final boolean DEBUG = false;

/**

* View data records for pre-layout

*/

@VisibleForTesting

final ArrayMap mLayoutHolderMap = new ArrayMap<>();

@VisibleForTesting

final LongSparseArray mOldChangedHolders = new LongSparseArray<>();

/**

* Clears the state and all existing tracking data

*/

void clear() {

mLayoutHolderMap.clear();

mOldChangedHolders.clear();

}

/**

* Adds the item information to the prelayout tracking

* @param holder The ViewHolder whose information is being saved

* @param info The information to save

*/

void addToPreLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) {

InfoRecord record = mLayoutHolderMap.get(holder);

if (record == null) {

record = InfoRecord.obtain();

mLayoutHolderMap.put(holder, record);

}

record.preInfo = info;

record.flags |= FLAG_PRE;

}

}

通過源碼可以看出,在dispatchLayoutStep1()方法里會先遍歷子view,并創建ItemHolderInfo,然后再通過ViewInfoStore的addToPreLayout()的這個方法將ItemHolderInfo賦值給InfoRecord,再保存到mLayoutHolderMap這個集合里面;

下面我們再來分析一下dispatchLayoutStep2()這個方法里面做來啥?

dispatchLayoutStep2():

private void dispatchLayoutStep2() {

private void dispatchLayoutStep2() {

startInterceptRequestLayout();

onEnterLayoutOrScroll();

mState.assertLayoutStep(State.STEP_LAYOUT | State.STEP_ANIMATIONS);

mAdapterHelper.consumeUpdatesInOnePass();

mState.mItemCount = mAdapter.getItemCount();

mState.mDeletedInvisibleItemCountSincePreviousLayout = 0;

// Step 2: Run layout

mState.mInPreLayout = false;

// 開始真正的去布局

mLayout.onLayoutChildren(mRecycler, mState);

mState.mStructureChanged = false;

mPendingSavedState = null;

// onLayoutChildren may have caused client code to disable item animations; re-check

mState.mRunSimpleAnimations = mState.mRunSimpleAnimations && mItemAnimator != null;

mState.mLayoutStep = State.STEP_ANIMATIONS;

onExitLayoutOrScroll();

stopInterceptRequestLayout(false);

}

}

通過上面的源碼可以看出,dispatchLayoutStep2()里面就開始真正的去布局了,通過onLayoutChildre()方法進行布局,具體的實現都在LayoutManager的子類里面;我們常用的LayoutManager基本上是LinearLayoutManager,那么這里我們具體來分析一下這個類里面是怎么實現的;

先看一下源碼:

public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {

...

final View focused = getFocusedChild();

if (!mAnchorInfo.mValid || mPendingScrollPosition != RecyclerView.NO_POSITION

|| mPendingSavedState != null) {

mAnchorInfo.reset();

// 獲取布局的錨點

updateAnchorInfoForLayout(recycler, state, mAnchorInfo);

mAnchorInfo.mValid = true;

} else if (focused != null && (mOrientationHelper.getDecoratedStart(focused)

>= mOrientationHelper.getEndAfterPadding()

|| mOrientationHelper.getDecoratedEnd(focused)

<= mOrientationHelper.getStartAfterPadding())) {

...

// 更新錨點信息

mAnchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));

}

//判斷是否是從后往前開始布局

if (mAnchorInfo.mLayoutFromEnd) {

...

//布局操作

fill(recycler, mLayoutState, state, false);

...

} else {

...

// fill towards end

fill(recycler, mLayoutState, state, false);

// fill towards start

fill(recycler, mLayoutState, state, false);

...

}

...

}

這里把代碼簡化了,我們只需要關注幾個重點的方法;這里的布局操作是,通過尋找布局的錨點(mAnchorInfo),判斷是從后往前布局還是從前往后布局,然后調用fill()方法進行布局;

尋找布局的錨點是通過updateAnchorInfoForLayout(recycler, state, mAnchorInfo)這個方法

private void updateAnchorInfoForLayout(RecyclerView.Recycler recycler, RecyclerView.State state,

AnchorInfo anchorInfo) {

...

if (updateAnchorFromChildren(recycler, state, anchorInfo)) {

if (DEBUG) {

Log.d(TAG, "updated anchor info from existing children");

}

return;

}

...

}

這里我們只需要關注updateAnchorFromChildren這個方法,跟進去看一下具體做了什么;

private boolean updateAnchorFromChildren(RecyclerView.Recycler recycler,

RecyclerView.State state, AnchorInfo anchorInfo) {

if (getChildCount() == 0) {

return false;

}

final View focused = getFocusedChild();

if (focused != null && anchorInfo.isViewValidAsAnchor(focused, state)) {

anchorInfo.assignFromViewAndKeepVisibleRect(focused, getPosition(focused));

return true;

}

if (mLastStackFromEnd != mStackFromEnd) {

return false;

}

View referenceChild = anchorInfo.mLayoutFromEnd

? findReferenceChildClosestToEnd(recycler, state)

: findReferenceChildClosestToStart(recycler, state);

if (referenceChild != null) {

anchorInfo.assignFromView(referenceChild, getPosition(referenceChild));

...

}

return true;

}

return false;

}

從這里的源碼可以看出,先通過getFocusedChild()去獲取focused 這個view,當獲取到了的時候將其標記為錨點,如果獲取不到那么就通過findReferenceChildClosestToEnd和findReferenceChildClosestToStart去尋找合適的view,并將其標記為錨點;

讓我們回到onLayoutChildren這個方法,當獲取到錨點的時候,調用fill方法開始填充頁面,根據fill方法看看具體做了什么?

int fill(RecyclerView.Recycler recycler, LayoutState layoutState,

RecyclerView.State state, boolean stopOnFocusable) {

...

if (layoutState.mScrollingOffset != LayoutState.SCROLLING_OFFSET_NaN) {

//回收沒有用到的view

recycleByLayoutState(recycler, layoutState);

}

...

while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {

layoutChunk(recycler, state, layoutState, layoutChunkResult);

...

}

}

這里通過recycleByLayoutState方法先將沒有用到view進行回收,然后再通過while循環調用layoutChunk方法進行布局;

看一下layoutChunk方法具體做了什么操作?

void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,

LayoutState layoutState, LayoutChunkResult result) {

View view = layoutState.next(recycler);

...

RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams();

if (layoutState.mScrapList == null) {

if (mShouldReverseLayout == (layoutState.mLayoutDirection

== LayoutState.LAYOUT_START)) {

addView(view);

} else {

addView(view, 0);

}

} else {

...

}

...

layoutDecoratedWithMargins(view, left, top, right, bottom);

...

}

到這里就是最終布局的地方了,先通過recycler獲取要布局的view,再通過addView方法將view添加到RecyclerView里去,然后根據參數調用layoutDecoratedWithMargins方法進行布局;

public void layoutDecoratedWithMargins(@NonNull View child, int left, int top, int right,

int bottom) {

final LayoutParams lp = (LayoutParams) child.getLayoutParams();

final Rect insets = lp.mDecorInsets;

child.layout(left + insets.left + lp.leftMargin, top + insets.top + lp.topMargin,

right - insets.right - lp.rightMargin,

bottom - insets.bottom - lp.bottomMargin);

}

這里最終調用了view的layout方法進行布局;到這里dispatchLayoutStep2()就分析完了,讓我們繼續接著看dispatchLayoutStep3()第三步里面做了啥;

dispatchLayoutStep3():

private void dispatchLayoutStep3() {

mState.assertLayoutStep(State.STEP_ANIMATIONS);

...

mState.mLayoutStep = State.STEP_START;

if (mState.mRunSimpleAnimations) {

...

for (int i = mChildHelper.getChildCount() - 1; i >= 0; i--) {

ViewHolder holder = getChildViewHolderInt(mChildHelper.getChildAt(i));

if (holder.shouldIgnore()) {

continue;

}

long key = getChangedHolderKey(holder);

final ItemHolderInfo animationInfo = mItemAnimator

.recordPostLayoutInformation(mState, holder);

ViewHolder oldChangeViewHolder = mViewInfoStore.getFromOldChangeHolders(key);

...

if (oldDisappearing && oldChangeViewHolder == holder) {

// run disappear animation instead of change

mViewInfoStore.addToPostLayout(holder, animationInfo);

} else {

...

mViewInfoStore.addToPostLayout(holder, animationInfo);

...

}

} else {

mViewInfoStore.addToPostLayout(holder, animationInfo);

}

}

// Step 4: Process view info lists and trigger animations

//觸發動畫

mViewInfoStore.process(mViewInfoProcessCallback);

}

...

}

這個方法里面只需要關注addToPostLayout這個方法就行,這里和第一步類似,也是通過遍歷viewholder信息來創建ItemHolderInfo,并保存到mViewInfoStore里去;

看一下addToPostLayout這個方法做了啥?

void addToPostLayout(RecyclerView.ViewHolder holder, RecyclerView.ItemAnimator.ItemHolderInfo info) {

InfoRecord record = mLayoutHolderMap.get(holder);

if (record == null) {

record = InfoRecord.obtain();

mLayoutHolderMap.put(holder, record);

}

record.postInfo = info;

也是通過將ItemHolderInfo信息轉化為InfoRecord類,然后保存到集合里去(mLayoutHolderMap);

到此,RecyclerView的onLayout流程就已經走完了;那么接下來就要開始分析onDraw的流程了;

onDraw()分析

先看一下源碼;

public void draw(Canvas c) {

super.draw(c);

...

for (int i = 0; i < count; i++) {

mItemDecorations.get(i).onDrawOver(c, this, mState);

}

...

}

public void onDraw(Canvas c) {

super.onDraw(c);

final int count = mItemDecorations.size();

for (int i = 0; i < count; i++) {

mItemDecorations.get(i).onDraw(c, this, mState);

}

}

很簡單,就幾行,mItemDecorations這個集合里面存的是ItemDecoration,也就是說,RecyclerView的onDraw是用來繪制ItemDecoration的;而itemView的繪制是在ViewGroup里面;

至此,RecyclerView的onMeasure,onLayout,onDraw,流程就已經分析完畢了;

總結:

RecyclerView的布局流程比較復雜,但是還是遵循viewGroup的繪制原理,即onMeasure,onLayout,onDraw這幾步流程;

繪制流程.png

那么到這里,布局到流程就已經講完了,希望能對你有所幫助,后面會繼續分析RecyclerView的復用機制,敬請期待!

與50位技術專家面對面20年技術見證,附贈技術全景圖

總結

以上是生活随笔為你收集整理的android流程化步骤样式,Android RecyclerView 解析之绘制流程篇的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。