我是靠谱客的博主 活泼钢铁侠,这篇文章主要介绍淘宝页面RecyclerView分析及阿里Vlayout框架使用与分析,现在分享给大家,希望可以做个参考。

可以发现布局似乎十分复杂,使用Monitor工具看看它的布局情况:

在这里插入图片描述

主界面居然只有一个RecyclerView就能实现??在这里插入图片描述

查了资料才发现,原来淘宝使用了阿里团队开发的一个开源框架Vlayout,(全称VirtualLayout)

复制代码
1
2
implementation 'com.alibaba.android:vlayout:1.0.3'

这个Vlayout可以在一个RecyclerView中加载不同的适配器,因为适配器可以加载不同的布局,它们分别是:

1、线性布局。使用ReyclerView开发最常用的布局。

2、网格布局。使用的频率也很高,只是一般都是自己实现,而在这个框架中已经被实现并封装好了。

3、固定布局

4、浮动布局

5、栅格布局

6、一拖N布局

7、吸边布局

8、瀑布流布局

在这里插入图片描述

截图来自github上的开源项目Vlayout,地址:
Vlayout地址

具体的demo也可以查看这个地址.

接下来看看如何使用

使用方法也十分简单:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this); mRecyclerView.setLayoutManager(layoutManager); DelegateAdapter adapter = new DelegateAdapter(layoutManager,true){ @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { super.onBindViewHolder(holder, position); List<Integer> list = new ArrayList<>(); list.add(R.drawable.image1); list.add(R.drawable.image2); list.add(R.drawable.image3); list.add(R.drawable.image4); list.add(R.drawable.image5); list.add(R.drawable.image6); Banner banner = holder.itemView.findViewById(R.id.banner); banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR); banner.setImageLoader(new GlideImageLoader()); banner.setImages(list); banner.setBannerAnimation(Transformer.DepthPage); banner.isAutoPlay(true); banner.setDelayTime(3000); banner.setIndicatorGravity(BannerConfig.CENTER); banner.start(); banner.setOnBannerListener(new OnBannerListener() { @Override public void OnBannerClick(int position) { Toast.makeText(getApplicationContext(), "banner点击了" + position, Toast.LENGTH_SHORT).show(); } }); } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.vlayout_banner,parent,false); return new BaseViewHolder(view); } @Override public int getItemCount() { return 1; } }; mRecyclerView.setAdapter(adapter);

跟原本的使用的方法基本一致,只不过将布局管理换成了VirtualLayoutManager,以及将RecyclerView.Adapter换成了自定义的DelegateAdapter,其实DelegateAdapter查看源码的话依然可以发现它是继承于RecyclerView.Adapter,只不过进行了进一层的封装。其他的操作与ReyclerView一模一样,分别实现onCreateViewHolder,onBindViewHolder和getItemCount,这样就添加了一个Adapter上去了。这样一看好像与原本的RecyclerView实现没有什么区别,但也并没有比原先的复杂。但是后续如果想要更多的Adapter,怎么操作?在我过去接触Vlayout之前,我在布局中添加了多个ReyclerView,每个ReyclerView只有一个Adapter。现在的话,能不能有一个大的主Adapter,这个主Adapter可以添加许多小的Adapter,这样是不是很完美?Vlayout中就提供了这样的类:DelegateAdapter。

使用方法:
1、自定义Adapter继承并实现DelegateAdapter.Adapter的方法
2、生成DelegateAdapter对象并添加。

伪代码:

class MyAdapter extends DelegateAdapter.Adapter{
。。。。
}

MyAdapter adapter1 = new MyAdapter(… )
MyAdapter adapter2 = new MyAdapter(… )

DelegateAdapter mainAdapter = new DelegateAdapter(…)

mainAdapter.addAdapter(adapter1)
mainAdapter.addAdapter(adapter2)

mRecyclerView.setAdapter(adapter);

这样就完成了!

下面以淘宝页面为例:

首先我对DelegateAdapter进行封装,因为不管实现什么样的Adapter,onCreateViewHolder都是相同的:

复制代码
1
2
3
4
View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId,parent,false); return new BaseViewHolder(view);

即:根据不同的布局id生成不同的view,然后使用相同的ViewHolder进行返回。因此这个可以进行封装。

getItemCount相同:

复制代码
1
2
3
4
5
@Override public int getItemCount() { return mCount; }

onCreateLayoutHelper。继承DelegateAdapter.Adapter必须实现这个方法,因此Adapter必须要有一个LayoutHelper(),这个LayoutHelper会根据以上提供的8种不同的布局实现类进行对应的布局。

复制代码
1
2
3
4
5
@Override public LayoutHelper onCreateLayoutHelper() { return mLayoutHelper; }

唯一无法封装的onBindViewHolder,因为这个对开发者来说会有不同的实现,因此它的“不定”的,所有无法封装。

BaseDelegateAdapter.java

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class BaseDelegateAdapter extends DelegateAdapter.Adapter<BaseViewHolder> { private int mLayoutId; private int mCount; private LayoutHelper mLayoutHelper; public BaseDelegateAdapter(LayoutHelper layoutHelper, int layoutId,int count){ mLayoutId = layoutId; mCount = count; mLayoutHelper = layoutHelper; } @Override public BaseViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(mLayoutId,parent,false); return new BaseViewHolder(view); } @Override public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) { } @Override public int getItemCount() { return mCount; } @Override public LayoutHelper onCreateLayoutHelper() { return mLayoutHelper; } }

BaseViewHolder.java

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
public class BaseViewHolder extends RecyclerView.ViewHolder { private SparseArray<View> views; public BaseViewHolder(@NonNull View itemView) { super(itemView); views = new SparseArray<>(); } private void setTextView(@IdRes int id,int content){ View view = views.get(id); if(view == null){ try { throw new Exception("请先调用getView保存View先"); } catch (Exception e) { e.printStackTrace(); } } if(view instanceof TextView){ ((TextView) view).setText(content); }else{ try { throw new Exception("这个id不是一个TextView视图"); } catch (Exception e) { e.printStackTrace(); } } } private <T extends View> T getView(@IdRes int viewId){ View view = views.get(viewId); if(view == null){ view = itemView.findViewById(viewId); views.put(viewId,view); } return (T) view; } }

之后就是如上所说的使用方法了:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private void initView(){ mRecyclerView = findViewById(R.id.recyclerView); VirtualLayoutManager layoutManager = new VirtualLayoutManager(this); DelegateAdapter adapter = new DelegateAdapter(layoutManager,true); mRecyclerView.setLayoutManager(layoutManager); BaseDelegateAdapter bannerAdapter = new BaseDelegateAdapter(new LinearLayoutHelper(),R.layout.vlayout_banner,1){ @Override public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) { super.onBindViewHolder(holder, position); List<Integer> list = new ArrayList<>(); list.add(R.drawable.image1); list.add(R.drawable.image2); list.add(R.drawable.image3); list.add(R.drawable.image4); list.add(R.drawable.image5); list.add(R.drawable.image6); Banner banner = holder.itemView.findViewById(R.id.banner); banner.setBannerStyle(BannerConfig.CIRCLE_INDICATOR); banner.setImageLoader(new GlideImageLoader()); banner.setImages(list); banner.setBannerAnimation(Transformer.DepthPage); banner.isAutoPlay(true); banner.setDelayTime(3000); banner.setIndicatorGravity(BannerConfig.CENTER); banner.start(); banner.setOnBannerListener(new OnBannerListener() { @Override public void OnBannerClick(int position) { Toast.makeText(getApplicationContext(), "banner点击了" + position, Toast.LENGTH_SHORT).show(); } }); } }; adapter.addAdapter(bannerAdapter); mRecyclerView.setAdapter(adapter); }

demo地址:https://github.com/lyx19970504/VLayoutDemo

关于使用方法到这里结束了,下面是源码的解析:

从RecyclerView的setAdapter方法进入:

复制代码
1
2
3
4
5
6
7
8
public void setAdapter(@Nullable Adapter adapter) { // bail out if layout is frozen setLayoutFrozen(false); setAdapterInternal(adapter, false, true); processDataSetCompletelyChanged(false); requestLayout(); }

这里调用了requestLayout,这是方法能让View本身重新绘制,即调用onMeasure,onLayout,onDraw方法。

onLayout方法:

复制代码
1
2
3
4
5
6
7
8
@Override protected void onLayout(boolean changed, int l, int t, int r, int b) { TraceCompat.beginSection(TRACE_ON_LAYOUT_TAG); dispatchLayout(); TraceCompat.endSection(); mFirstLayoutComplete = true; }

调用dispatchLayout方法:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void dispatchLayout() { 。。。 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(); }

可以看到调用的比较重要的方法是dispatchLayoutStep2()和dispatchLayoutStep3()。一般来说开发者都会添加注释,否则这会增加阅读的困难度。

复制代码
1
2
3
4
5
6
7
8
/** * The final step of the layout where we save the information about views for animations, * trigger animations and do any necessary cleanup. */ private void dispatchLayoutStep3() { ...... }

如上是Step3的注释,可以发现这个步骤是保存views的相关动画信息,动画信息可以先不管。于是进入dispatchLayoutStep2()看看:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
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); }

在这个方法中基本是设置一些参数,但是却调用一个很重要的方法:

复制代码
1
2
mLayout.onLayoutChildren(mRecycler, mState);

从方法名可以看出是对所有的子view进行布局

mLayout则是一个LayoutManager:

复制代码
1
2
@VisibleForTesting LayoutManager mLayout;

那么到这里思路就十分清晰了:首先再activity中,RecyclerView对象首先设置LayoutManager(setLayoutManager),我们比较常用的就是LinearLayoutManager,代码会一路往下走,最后由LayoutMnager调用onLayoutChildren对子views进行布局。

而VirtualLayoutManager框架将则对LinearLayoutManager进一步的扩充使其支持跟多的布局实现(事实上VirtualLayoutManager是继承于LinearLayoutManager的)

VirtualLayoutManager的onLayoutChildren方法:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
@Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) { Trace.beginSection(TRACE_LAYOUT); } if (mNoScrolling && state.didStructureChange()) { mSpaceMeasured = false; mSpaceMeasuring = true; } runPreLayout(recycler, state); try { super.onLayoutChildren(recycler, state); } catch (Exception e) { e.printStackTrace(); throw e; } finally { // MaX_VALUE means invalidate scrolling offset - no scroll runPostLayout(recycler, state, Integer.MAX_VALUE); // hack to indicate its an initial layout } 。。。 }

可以发现调用了父类的方法:

复制代码
1
2
super.onLayoutChildren(recycler, state);

它的父类是ExposeLinearLayoutManagerEx,而ExposeLinearLayoutManagerEx继承于LinearLayoutManager,所以才说VirtualLayoutManager继承于LinearLayoutManager。

ExposeLinearLayoutManagerEx的onLayoutChildren实现:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
public void onLayoutChildren(Recycler recycler, State state) { this.ensureLayoutStateExpose(); this.mLayoutState.mRecycle = false; this.myResolveShouldLayoutReverse(); this.mAnchorInfo.reset(); this.mAnchorInfo.mLayoutFromEnd = this.mShouldReverseLayoutExpose ^ this.getStackFromEnd(); this.updateAnchorInfoForLayoutExpose(state, this.mAnchorInfo); int extra = this.getExtraLayoutSpace(state); boolean before = state.getTargetScrollPosition() < this.mAnchorInfo.mPosition; int extraForStart; int extraForEnd; if (before == this.mShouldReverseLayoutExpose) { extraForEnd = extra; extraForStart = 0; } else { extraForStart = extra; extraForEnd = 0; } extraForStart += this.mOrientationHelper.getStartAfterPadding(); extraForEnd += this.mOrientationHelper.getEndPadding(); int endOffset; int fixOffset; if (state.isPreLayout() && this.mCurrentPendingScrollPosition != -1 && this.mPendingScrollPositionOffset != -2147483648) { View existing = this.findViewByPosition(this.mCurrentPendingScrollPosition); if (existing != null) { if (this.mShouldReverseLayoutExpose) { endOffset = this.mOrientationHelper.getEndAfterPadding() - this.mOrientationHelper.getDecoratedEnd(existing); fixOffset = endOffset - this.mPendingScrollPositionOffset; } else { endOffset = this.mOrientationHelper.getDecoratedStart(existing) - this.mOrientationHelper.getStartAfterPadding(); fixOffset = this.mPendingScrollPositionOffset - endOffset; } if (fixOffset > 0) { extraForStart += fixOffset; } else { extraForEnd -= fixOffset; } } } this.onAnchorReady(state, this.mAnchorInfo); this.detachAndScrapAttachedViews(recycler); this.mLayoutState.mIsPreLayout = state.isPreLayout(); this.mLayoutState.mOnRefresLayout = true; ExposeLinearLayoutManagerEx.LayoutState var10000; int startOffset; if (this.mAnchorInfo.mLayoutFromEnd) { this.updateLayoutStateToFillStartExpose(this.mAnchorInfo); this.mLayoutState.mExtra = extraForStart; this.fill(recycler, this.mLayoutState, state, false); startOffset = this.mLayoutState.mOffset; if (this.mLayoutState.mAvailable > 0) { extraForEnd += this.mLayoutState.mAvailable; } this.updateLayoutStateToFillEndExpose(this.mAnchorInfo); this.mLayoutState.mExtra = extraForEnd; var10000 = this.mLayoutState; var10000.mCurrentPosition += this.mLayoutState.mItemDirection; this.fill(recycler, this.mLayoutState, state, false); endOffset = this.mLayoutState.mOffset; } else { this.updateLayoutStateToFillEndExpose(this.mAnchorInfo); this.mLayoutState.mExtra = extraForEnd; this.fill(recycler, this.mLayoutState, state, false); endOffset = this.mLayoutState.mOffset; if (this.mLayoutState.mAvailable > 0) { extraForStart += this.mLayoutState.mAvailable; } this.updateLayoutStateToFillStartExpose(this.mAnchorInfo); this.mLayoutState.mExtra = extraForStart; var10000 = this.mLayoutState; var10000.mCurrentPosition += this.mLayoutState.mItemDirection; this.fill(recycler, this.mLayoutState, state, false); startOffset = this.mLayoutState.mOffset; } if (this.getChildCount() > 0) { if (this.mShouldReverseLayoutExpose ^ this.getStackFromEnd()) { fixOffset = this.fixLayoutEndGapExpose(endOffset, recycler, state, true); startOffset += fixOffset; endOffset += fixOffset; fixOffset = this.fixLayoutStartGapExpose(startOffset, recycler, state, false); startOffset += fixOffset; endOffset += fixOffset; } else { fixOffset = this.fixLayoutStartGapExpose(startOffset, recycler, state, true); startOffset += fixOffset; endOffset += fixOffset; fixOffset = this.fixLayoutEndGapExpose(endOffset, recycler, state, false); startOffset += fixOffset; endOffset += fixOffset; } } this.layoutForPredictiveAnimationsExpose(recycler, state, startOffset, endOffset); if (!state.isPreLayout()) { this.mCurrentPendingScrollPosition = -1; this.mPendingScrollPositionOffset = -2147483648; this.mOrientationHelper.onLayoutComplete(); } this.mLastStackFromEnd = this.getStackFromEnd(); this.mCurrentPendingSavedState = null; }

看起来非常复杂,完成了很多的事情,但是开发者进行了注释:

复制代码
1
2
3
4
5
6
7
8
// layout algorithm: // 1) by checking children and other variables, find an anchor coordinate and an anchor // item position. // 2) fill towards start, stacking from bottom // 3) fill towards end, stacking from top // 4) scroll to fulfill requirements like stack from bottom. // create layout state

可以发现第一步是检查,后面则是对什么东西进行填充,注意这里使用的是this.fill而不是fill,看看fill的实现:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
protected int fill(RecyclerView.Recycler recycler, LayoutState layoutState, RecyclerView.State state, boolean stopOnFocusable) { 。。。 。。。 // max offset we should set is mFastScroll + available final int start = layoutState.mAvailable; if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) { // TODO ugly bug fix. should not happen if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } recycleByLayoutStateExpose(recycler, layoutState); } int remainingSpace = layoutState.mAvailable + layoutState.mExtra; while (remainingSpace > 0 && layoutState.hasMore(state)) { layoutChunkResultCache.resetInternal(); layoutChunk(recycler, state, layoutState, layoutChunkResultCache); if (layoutChunkResultCache.mFinished) { break; } layoutState.mOffset += layoutChunkResultCache.mConsumed * layoutState.mLayoutDirection; /** * Consume the available space if: * * layoutChunk did not request to be ignored * * OR we are laying out scrap children * * OR we are not doing pre-layout */ if (!layoutChunkResultCache.mIgnoreConsumed || mLayoutState.mScrapList != null || !state.isPreLayout()) { layoutState.mAvailable -= layoutChunkResultCache.mConsumed; // we keep a separate remaining space because mAvailable is important for recycling remainingSpace -= layoutChunkResultCache.mConsumed; } if (layoutState.mScrollingOffset != LayoutState.SCOLLING_OFFSET_NaN) { layoutState.mScrollingOffset += layoutChunkResultCache.mConsumed; if (layoutState.mAvailable < 0) { layoutState.mScrollingOffset += layoutState.mAvailable; } recycleByLayoutStateExpose(recycler, layoutState); } if (stopOnFocusable && layoutChunkResultCache.mFocusable) { break; } } 。。。 }

一样的复杂又冗长。但是有个方法特别有吸引力:this.layoutChunk,翻译过来的意思是布局 块,也就是对块布局。注意这个this,它代表调用的VirtualLayoutManager的layoutChunk而不是ExposeLinearLayoutManagerEx的layoutChunk.看看它的实现:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Override protected void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult result) { final int position = layoutState.mCurrentPosition; mTempLayoutStateWrapper.mLayoutState = layoutState; LayoutHelper layoutHelper = mHelperFinder == null ? null : mHelperFinder.getLayoutHelper(position); if (layoutHelper == null) layoutHelper = mDefaultLayoutHelper; layoutHelper.doLayout(recycler, state, mTempLayoutStateWrapper, result, this); mTempLayoutStateWrapper.mLayoutState = null; // no item consumed if (layoutState.mCurrentPosition == position) { Log.w(TAG, "layoutHelper[" + layoutHelper.getClass().getSimpleName() + "@" + layoutHelper.toString() + "] consumes no item!"); // break as no item consumed result.mFinished = true; } else { // Update height consumed in each layoutChunck pass final int positionAfterLayout = layoutState.mCurrentPosition - layoutState.mItemDirection; final int consumed = result.mIgnoreConsumed ? 0 : result.mConsumed; // TODO: change when supporting reverseLayout Range<Integer> range = new Range<>(Math.min(position, positionAfterLayout), Math.max(position, positionAfterLayout)); final int idx = findRangeLength(range); if (idx >= 0) { Pair<Range<Integer>, Integer> pair = mRangeLengths.get(idx); if (pair != null && pair.first.equals(range) && pair.second == consumed) return; mRangeLengths.remove(idx); } mRangeLengths.add(Pair.create(range, consumed)); Collections.sort(mRangeLengths, new Comparator<Pair<Range<Integer>, Integer>>() { @Override public int compare(Pair<Range<Integer>, Integer> a, Pair<Range<Integer>, Integer> b) { if (a == null && b == null) return 0; if (a == null) return -1; if (b == null) return 1; Range<Integer> lr = a.first; Range<Integer> rr = b.first; return lr.getLower() - rr.getLower(); } }); } }

这个互相调用的过程比较复杂,用图来表示就是:
VirtualLayoutManager.onLayoutChildren(){
super.onLayoutChildren(recycler, state);
}
➡️
ExposeLinearLayoutManagerEx.onLayoutChildren(){
this.fill(…)
}
➡️
ExposeLinearLayoutManagerEx.fill(){
this.layoutChunk(…)
}
➡️
VirtualLayoutManager.layoutChunk

至于layoutChunk后面再谈。

总体流程如图:

在这里插入图片描述

接下来看看DelegateAdapter的实现。首先这个类的好处就是它可以添加多个adapter,每个adapter可以根据不同的LayoutHelper而实现不同的布局。这些helper类框架中都已经实现好了。

从DelegateAdapter(可以理解为一个总管adapter)的addAdapter开始:

addAdapter最终会调用内部的这个方法:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public void addAdapters(int position, @Nullable List<Adapter> adapters) { 。。。 List<LayoutHelper> helpers = new LinkedList<>(super.getLayoutHelpers()); for (Adapter adapter : adapters) { // every adapter has an unique index id AdapterDataObserver observer = new AdapterDataObserver(mTotal, mIndexGen == null ? mIndex++ : mIndexGen.incrementAndGet()); adapter.registerAdapterDataObserver(observer); hasStableIds = hasStableIds && adapter.hasStableIds(); LayoutHelper helper = adapter.onCreateLayoutHelper(); helper.setItemCount(adapter.getItemCount()); mTotal += helper.getItemCount(); helpers.add(position, helper); mAdapters.add(position, Pair.create(observer, adapter)); position++; } if (!hasObservers()) { super.setHasStableIds(hasStableIds); } super.setLayoutHelpers(helpers); }

可以发现使用了一个LayoutHelper列表将helper添加的adapter储存起来。并且调用父类的setLayoutHelpers方法:

复制代码
1
2
3
4
public void setLayoutHelpers(List<LayoutHelper> helpers) { this.mLayoutManager.setLayoutHelpers(helpers); }

它的父类VirtualLayoutAdapter,对setLayoutHelpers的实现是调用了VirtualLayoutManager的setLayoutHelpers方法。

setLayoutHelpers实现:

public void setLayoutHelpers(@Nullable List helpers) {
for (LayoutHelper helper : mHelperFinder) {
oldHelpersSet.put(System.identityHashCode(helper), helper);
}

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
// set ranges if (helpers != null) { int start = 0; for (int i = 0; i < helpers.size(); i++) { LayoutHelper helper = helpers.get(i); if (helper instanceof FixAreaLayoutHelper) { ((FixAreaLayoutHelper) helper).setAdjuster(mFixAreaAdjustor); } if (helper instanceof BaseLayoutHelper && mLayoutViewBindListener != null) { ((BaseLayoutHelper) helper).setLayoutViewBindListener(mLayoutViewBindListener); } if (helper.getItemCount() > 0) { helper.setRange(start, start + helper.getItemCount() - 1); } else { helper.setRange(-1, -1); } start += helper.getItemCount(); } } this.mHelperFinder.setLayouts(helpers); for (LayoutHelper helper : mHelperFinder) { newHelpersSet.put(System.identityHashCode(helper), helper); } for (Iterator<Map.Entry<Integer, LayoutHelper>> it = oldHelpersSet.entrySet().iterator(); it.hasNext(); ) { Map.Entry<Integer, LayoutHelper> entry = it.next(); Integer key = entry.getKey(); if (newHelpersSet.containsKey(key)) { newHelpersSet.remove(key); it.remove(); } } for (LayoutHelper helper : oldHelpersSet.values()) { helper.clear(this); } if (!oldHelpersSet.isEmpty() || !newHelpersSet.isEmpty()) { mSpaceMeasured = false; } oldHelpersSet.clear(); newHelpersSet.clear(); requestLayout(); }

在这个方法中又出现了新的类:LayoutHelper,这个LayoutHelper就像一个管理容器,它负责管理所有的helper类,并且为HelperFinder设置所有的helper。至于HelperFinder是什么,相信从名字上就能看出来,它是一个HelperFinder的查找工具。

设置完成了以后,回到之前的layoutChunk中:

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
@Override protected void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state, LayoutState layoutState, com.alibaba.android.vlayout.layout.LayoutChunkResult result) { final int position = layoutState.mCurrentPosition; mTempLayoutStateWrapper.mLayoutState = layoutState; LayoutHelper layoutHelper = mHelperFinder == null ? null : mHelperFinder.getLayoutHelper(position); if (layoutHelper == null) layoutHelper = mDefaultLayoutHelper; layoutHelper.doLayout(recycler, state, mTempLayoutStateWrapper, result, this); 。。。 。。。 }

首先HelperFinder工具会根据位置来获得adapter对应的LayoutHelper,一共有8种已经内置的helper,这个在之前已经介绍过了。根据不同的helper会调用不同的doLayout实现方法。怎么根据位置获得呢?比如说,adapter1设置了10个控件,adapter2设置了5个控件,adapter3设置了3个控件,然后helperFinder在0-9的位置时,就会得到adapter1的helper,在10-15的位置时就会获得adapter2的helper,在16-18的位置时就会获得adapter3的helper.

adapter必须实现设置helper的方法:↓

复制代码
1
2
3
4
5
@Override public LayoutHelper onCreateLayoutHelper() { return mLayoutHelper; }

doLayout方法,则是真正对控件进行布局的方法。不同的helper有不同的doLayout方法。

总结

代码一共这么几行:

复制代码
1
2
3
4
5
6
7
8
VirtualLayoutManager layoutManager = new VirtualLayoutManager(this); DelegateAdapter adapter = new DelegateAdapter(layoutManager,true); mRecyclerView.setLayoutManager(layoutManager); adapter.addAdapter(x1) adapter.addAdapter(x2) ...(很多子adapter) mRecyclerView.setAdapter(adapter);

首先将VirtualLayoutManager添加给RecyclerView和DelegateAdapter,在DelegateAdapter添加子adapter的过程中,会获取所有子adapter的helper类然后存储起来,这些helper类分别对应不同的布局模式。RecyclerView设置DelegateAdapter后,RecyclerView设置的VirtualLayoutManager会调用layoutChunk方法,这个方法中会根据位置获取不同adapter对应的helper方法,然后使用他们的doLayout方法进行控件的摆放。

最后

以上就是活泼钢铁侠最近收集整理的关于淘宝页面RecyclerView分析及阿里Vlayout框架使用与分析的全部内容,更多相关淘宝页面RecyclerView分析及阿里Vlayout框架使用与分析内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(92)

评论列表共有 0 条评论

立即
投稿
返回
顶部