最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
…
}
dispatchLayoutStep1
方法中调用了 mLayout
的 onLayoutChildren
方法。上面分析告诉我们,mLayout
就是 LayoutManager
,所以我们转到 LayoutManager
的 onLayoutChildren
方法。
public void onLayoutChildren(Recycler recycler, State state) {
Log.e(TAG, "You must override onLayoutChildren(Recycler recycler, State state) ");
}
onLayoutChildren
方法是一个空实现,其具体实现在各个子类中。我们拿 LinearLayoutManager
进行分析,看其中 onLayoutChildren
的实现。
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
// 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
…
if (mAnchorInfo.mLayoutFromEnd) {
…
} else {
// fill towards end
updateLayoutStateToFillEnd(mAnchorInfo);
mLayoutState.mExtra = extraForEnd;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
final int lastElement = mLayoutState.mCurrentPosition;
if (mLayoutState.mAvailable > 0) {
extraForStart += mLayoutState.mAvailable;
}
// fill towards start
updateLayoutStateToFillStart(mAnchorInfo);
mLayoutState.mExtra = extraForStart;
mLayoutState.mCurrentPosition += mLayoutState.mItemDirection;
fill(recycler, mLayoutState, state, false);
startOffset = mLayoutState.mOffset;
if (mLayoutState.mAvailable > 0) {
extraForEnd = mLayoutState.mAvailable;
// start could not consume all it should. add more items towards end
updateLayoutStateToFillEnd(lastElement, endOffset);
mLayoutState.mExtra = extraForEnd;
fill(recycler, mLayoutState, state, false);
endOffset = mLayoutState.mOffset;
}
}
…
}
onLayoutChildren
方法中的注释已经为我们说明了 RecyclerView
的布局算法,mAnchorInfo
为布局锚点信息,包含了子控件在Y轴上起始绘制偏移量(coordinate),itemView
在 Adapter
中的索引位置(position)和布局方向(mLayoutFromEnd)-表示start、end方向。该方法的功能是:确定布局锚点,并以此为起点向开始和结束方向填充 ItemView
,如下图所示。
在 onLayoutChildren
方法中,调用了 fill
方法,从该方法名可以知道,该方法应该是将子控件加入到RecyclerView
中的。
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
…
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
…
layoutChunk(recycler, state, layoutState, layoutChunkResult);
…
}
…
return start - layoutState.mAvailable;
}
fill
方法中循环调用了 layoutChunkResult
方法。
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
…
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
…
}
measureChildWithMargins(view, 0, 0);
result.mConsumed = mOrientationHelper.getDecoratedMeasurement(view);
int left, top, right, bottom;
…
layoutDecoratedWithMargins(view, left, top, right, bottom);
…
result.mFocusable = view.hasFocusable();
}
layoutChunk
方法中,layoutState的next方法将从 Recycler
获取的 View
添加到 RecyclerView
中,从而完成了整个 RecyclerView
的布局。
以上就是 RecyclerView
渲染过程的源码分析,接下来我们来分析一下 RecyclerView
的滑动过程。
RecyclerView
本质上就是一个 View
,所以我们从它的 onTouchEvent
方法入手进行分析。
public boolean onTouchEvent(MotionEvent e) {
…
switch (action) {
case MotionEvent.ACTION_DOWN: {
…
} break;
case MotionEvent.ACTION_POINTER_DOWN: {
…
} break;
case MotionEvent.ACTION_MOVE: {
…
if (mScrollState == SCROLL_STATE_DRAGGING) {
mLastTouchX = x - mScrollOffset[0];
mLastTouchY = y - mScrollOffset[1];
if (scrollByInternal(
canScrollHorizontally ? dx : 0,
canScrollVertically ? dy : 0,
vtev)) {
getParent().requestDisallowInterceptTouchEvent(true);
}
if (mGapWorker != null && (dx != 0 || dy != 0)) {
mGapWorker.postFromTraversal(this, dx, dy);
}
}
} break;
case MotionEvent.ACTION_POINTER_UP: {
…
} break;
case MotionEvent.ACTION_UP: {
…
} break;
case MotionEvent.ACTION_CANCEL: {
…
} break;
}
…
return true;
}
onTouchEvent
方法中主要关注的是 action
为 MotionEvent.ACTION_MOVE
的情况,在滑动过程中调用了scrollByInternal
方法。
boolean scrollByInternal(int x, int y, MotionEvent ev) {
…
if (mAdapter != null) {
…
if (x != 0) {
consumedX = mLayout.scrollHorizontallyBy(x, mRecycler, mState);
unconsumedX = x - consumedX;
}
if (y != 0) {
consumedY = mLayout.scrollVerticallyBy(y, mRecycler, mState);
unconsumedY = y - consumedY;
}
…
}
…
return consumedX != 0 || consumedY != 0;
}
当上下滑动时,垂直方向上的y偏移量是不等于0的,从而执行了 LayoutManager
的 scrollVerticallyBy
方法。我们拿 LinearLayoutManager
的 scrollVerticallyBy
来举例。
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
if (mOrientation == HORIZONTAL) {
return 0;
}
return scrollBy(dy, recycler, state);
}
当上下滑动时,执行了 scrollBy
方法。
int scrollBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
…
final int consumed = mLayoutState.mScrollingOffset
- fill(recycler, mLayoutState, state, false);
…
mOrientationHelper.offsetChildren(-scrolled);
…
return scrolled;
}
scrollBy
方法中又执行了 fill
方法,该方法的作用是向可填充区域填充 itemView
,我们具体看一下 fill
方法的实现。
int fill(RecyclerView.Recycler recycler, LayoutState layoutState,
RecyclerView.State state, boolean stopOnFocusable) {
…
while ((layoutState.mInfinite || remainingSpace > 0) && layoutState.hasMore(state)) {
layoutChunkResult.resetInternal();
layoutChunk(recycler, state, layoutState, layoutChunkResult);
if (layoutChunkResult.mFinished) {
break;
}
layoutState.mOffset += layoutChunkResult.mConsumed * layoutState.mLayoutDirection;
…
}
return start - layoutState.mAvailable;
}
fill方法中又调用了layoutChunk方法。
void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state,
LayoutState layoutState, LayoutChunkResult result) {
View view = layoutState.next(recycler);
…
LayoutParams params = (LayoutParams) view.getLayoutParams();
if (layoutState.mScrapList == null) {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addView(view);
} else {
addView(view, 0);
}
} else {
if (mShouldReverseLayout == (layoutState.mLayoutDirection
== LayoutState.LAYOUT_START)) {
addDisappearingView(view);
} else {
最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
tate.LAYOUT_START)) {
addDisappearingView(view);
} else {
最后
针对于上面的问题,我总结出了互联网公司Android程序员面试涉及到的绝大部分面试题及答案,并整理做成了文档,以及系统的进阶学习视频资料。
(包括Java在Android开发中应用、APP框架知识体系、高级UI、全方位性能调优,NDK开发,音视频技术,人工智能技术,跨平台技术等技术资料),希望能帮助到你面试前的复习,且找到一个好的工作,也节省大家在网上搜索资料的时间来学习。
[外链图片转存中…(img-wJW7M0s5-1715451111325)]
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!