总结:LayoutManager的任务是
布局子视图
滚动子视图
在滚动过程中根据子视图在布局中所处的位置,决定何时添加子视图和回收视图
一个LayoutManager负责在一个RecyclerView中测量和定位项目视图,以及决定什么时候回收那些对用户不可见的itemviews。
通过更改RecyclerView的LayoutManager,可以用来实现一个标准的垂直滚动列表,统一的网格,交错的网格(瀑布流),水平滚动集合等等。
如果LayoutManager指定一个默认的构造函数或者一个带有这样的标签的构造函数:Context,AttributeSet,int,int,RecyclerView会在加载item布局的时候实例化并且设置LayoutManager。
最常用的属性从(Context,AttributeSet,int,int)中获取。
以防万一一个LayoutManager指定了两个构造函数,非默认构造函数将优先。
public static abstract class LayoutManager{
ChildHelper mChildHelper;
RecyclerView mRecyclerView;
//该回调用于检索有关RecyclerView及其子View中的水平方向的信息。
private final ViewBoundsCheck.CallbackmHorizontalBoundCheckCallback =
new ViewBoundsCheck.Callback(){
//取得子item数量
@Override
public int getChildCount(){
returnLayoutManager.this.getChildCount();
}
//取得当前的RecyclerView
@Override
public View getParent() {
return mRecyclerView;
}
//取得某个item
@Override
public View getChildAt(intindex) {
returnLayoutManager.this.getChildAt(index);
}
//取得左边缘起始位置(就是相当于定位这个rv在布局中的位置)
@Override
public int getParentStart(){
returnLayoutManager.this.getPaddingLeft();
}
//取得右边缘位置
@Override
public int getParentEnd() {
returnLayoutManager.this.getWidth() - LayoutManager.this.getPaddingRight();
}
//这里取得左边缘开始位置的方法看不太懂
@Override
public intgetChildStart(View view) {
finalRecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return LayoutManager.this.getDecoratedLeft(view)- params.leftMargin;
}
@Override
public int getChildEnd(Viewview) {
finalRecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
returnLayoutManager.this.getDecoratedRight(view) + params.rightMargin;
}
};
//相对应的,这里检索rv相关和子view垂直相关
private final ViewBoundsCheck.CallbackmVerticalBoundCheckCallback =
new ViewBoundsCheck.Callback(){
@Override
public int getChildCount(){
returnLayoutManager.this.getChildCount();
}
@Override
public View getParent() {
return mRecyclerView;
}
@Override
public View getChildAt(intindex) {
returnLayoutManager.this.getChildAt(index);
}
@Override
public int getParentStart(){
returnLayoutManager.this.getPaddingTop();
}
@Override
public int getParentEnd() {
returnLayoutManager.this.getHeight()
-LayoutManager.this.getPaddingBottom();
}
@Override
public intgetChildStart(View view) {
finalRecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
returnLayoutManager.this.getDecoratedTop(view) - params.topMargin;
}
@Override
public int getChildEnd(Viewview) {
finalRecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
returnLayoutManager.this.getDecoratedBottom(view) + params.bottomMargin;
}
};
//实用程序对象用于检查父RecyclerView对象的边界。
ViewBoundsCheck mHorizontalBoundCheck = newViewBoundsCheck(mHorizontalBoundCheckCallback);
ViewBoundsCheck mVerticalBoundCheck = newViewBoundsCheck(mVerticalBoundCheckCallback);
//这个柔滑滚动也值得学习
@Nullable
SmoothScroller mSmoothScroller;
//请求简单动画的标志
boolean mRequestedSimpleAnimations = false;
//是否被加到视图的标志
boolean mIsAttachedToWindow = false;
//自动测量的标志
boolean mAutoMeasure = false;
//LayoutManager有自己更严格的测量缓存的机制,以避免重新测量一个孩子如果给予的控件比之前测量的大。
//启用测量缓存功能
private boolean mMeasurementCacheEnabled =true;
//启用预取item功能
private boolean mItemPrefetchEnabled =true;
//当预取发生的时候,GapWorkder负责编写,以跟踪最大数量的视图,由collectInitialPrefetchPositions(int,LayoutPrefetchRegistry)请求或者collectAdjacentPrefetchPositions(int,int,State,LayoutPrefetchRegistry)调用。如果通过
collectInitialPrefetchPositions(int,LayoutPrefetchRegistry)进行扩展,将在布局时重置以防止初始预取(通常很大,因为他们是与预期的孩子数量成正比)永久扩展缓存。(真的感觉好严格)
int mPrefetchMaxCountObserved;
//如果是true,那么mPrefetchMaxCountObserved将只有在下一个布局中可用,并且应该被重置。
booleanmPrefetchMaxObservedInInitialPrefetch;
//这些测量规格可能是传递给RecyclerView的onMeasure()方法或者是RecyclerView创造的假的测量规格。
例如,当布局在运行的时候,RecyclerView总是将这些规格设置为EXACTLY因为LayoutManager无法再布局的过程中调整RecyclerView的大小。
另外,为了能够在未指定的测量规格中使用提示,RecyclerView将检查API级别并将大小设置为0到M前以避免可能造成的任何问题,因为他们可能造成腐败的价值(性能浪费)。
如果他们设置模式成未明确状态,那么旧的明天就没有责任去分享大小。
private int mWidthMode, mHeightMode;
private int mWidth, mHeight;
//LayoutManager用于根据位置请求将被预取item的接口。有明确的距离到viewport,表示优先级。
public interface LayoutPrefetchRegistry {
void addPosition(int layoutPosition, int pixelDistance);
}
//设置RecyclerView,如果是空,求全部初始化。否则就设置。并且设置测量规格为EXACTLY(这些规格就如同view的onMeasure一般)
void setRecyclerView(RecyclerViewrecyclerView) {
if (recyclerView == null) {
mRecyclerView = null;
mChildHelper = null;
mWidth = 0;
mHeight = 0;
} else {
mRecyclerView = recyclerView;
mChildHelper =recyclerView.mChildHelper;
mWidth =recyclerView.getWidth();
mHeight = recyclerView.getHeight();
}
mWidthMode = MeasureSpec.EXACTLY;
mHeightMode = MeasureSpec.EXACTLY;
}
void setMeasureSpecs(int wSpec, int hSpec){
mWidth = MeasureSpec.getSize(wSpec);
mWidthMode = MeasureSpec.getMode(wSpec);
if (mWidthMode == MeasureSpec.UNSPECIFIED &&!ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
mWidth = 0;
}
mHeight = MeasureSpec.getSize(hSpec);
mHeightMode = MeasureSpec.getMode(hSpec);
if (mHeightMode == MeasureSpec.UNSPECIFIED &&!ALLOW_SIZE_IN_UNSPECIFIED_SPEC) {
mHeight = 0;
}
}
//在使用自动测量的时候,在测量过程中计算布局后调用这个方法。
它只是遍历所有的子孩子计算一个边界框然后调用setMeasuredDimension(Rect,int,int)。
LayoutManagers(子类)可以覆盖该方法如果他们需要以不同的方式处理边框。
例如,GridLayoutManager覆盖该方法,以确保即使列是空,GridLayoutManager仍然测量宽度足以包括它。
void setMeasuredDimensionFromChildren(intwidthSpec, int heightSpec) {
final int count = getChildCount();
if (count == 0) {
mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
return;
}
int minX = Integer.MAX_VALUE;
int minY = Integer.MAX_VALUE;
int maxX = Integer.MIN_VALUE;
int maxY = Integer.MIN_VALUE;
//开循环取得最小边界
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
final Rect bounds =mRecyclerView.mTempRect;
getDecoratedBoundsWithMargins(child, bounds);
if (bounds.left < minX) {
minX = bounds.left;
}
if (bounds.right > maxX) {
maxX = bounds.right;
}
if (bounds.top < minY) {
minY = bounds.top;
}
if (bounds.bottom > maxY) {
maxY = bounds.bottom;
}
}
mRecyclerView.mTempRect.set(minX, minY, maxX, maxY);
setMeasuredDimension(mRecyclerView.mTempRect, widthSpec, heightSpec);
}
//设定子孩子给定的边界框的测量尺寸并且测量规格已被传入onMeasure(int,int)。它是在测量过结果传递期间在RecyclerView调用LayoutManager#onLayoutChildren(Recycler,State)之后被调用。
这个方法应该调用setMeasuredDimension(int,int)。
默认实现将RecyclerView的填充添加到给定的边界框然后将该值限制在给定的测量规格范围之内。
只有当LayoutManager选择进入自动测量API时才会调用此方法
public void setMeasuredDimension(RectchildrenBounds, int wSpec, int hSpec) {
//被使用的宽度=孩子矩形宽度+左边距+右边距
int usedWidth = childrenBounds.width() + getPaddingLeft() +getPaddingRight();
int usedHeight = childrenBounds.height() + getPaddingTop() +getPaddingBottom();
int width = chooseSize(wSpec, usedWidth, getMinimumWidth());
int height = chooseSize(hSpec, usedHeight, getMinimumHeight());
setMeasuredDimension(width, height);
}
//在底层RecyclerView上调用这个方法
public void requestLayout() {
if(mRecyclerView != null) {
mRecyclerView.requestLayout();
}
}
//(调用RecyclerView的断言方法)断言在布局中间或者在滚动,如果没有的话就会抛出异常。
public void assertInLayoutOrScroll(Stringmessage) {
if (mRecyclerView != null) {
mRecyclerView.assertInLayoutOrScroll(message);
}
}
//从给定的规格和参数中选择最接近所需尺寸的尺寸并且遵从它。
public static int chooseSize(int spec, intdesired, int min) {
final int mode = View.MeasureSpec.getMode(spec);
final int size = View.MeasureSpec.getSize(spec);
//这里就类似于View中onMeasure那一块的源码了
switch (mode) {
case View.MeasureSpec.EXACTLY:
return size;
case View.MeasureSpec.AT_MOST:
return Math.min(size,Math.max(desired, min));
caseView.MeasureSpec.UNSPECIFIED:
default:
return Math.max(desired,min);
}
}
public voidassertNotInLayoutOrScroll(String message) {
if (mRecyclerView != null) {
mRecyclerView.assertNotInLayoutOrScroll(message);
}
}
//1.定义布局是否应由RecyclerView或LayoutManager他自己来处理布局的测量。
2.如果需要的话,该方法通常由值为true的LayoutManager调用来支持WRAP_CONTENT。3.如果您正在使用公共的LayoutManager,但想要自定义测量逻辑,你可以用这个方法调用这个方法并覆盖。
4.AutoMeasure是布局管理员轻松包装其内容或处理RecyclerView的父级提供的各种规格方便的便捷机制。
5.它通过调用onLayoutChildren(Recycler,State)来工作在RecyclerView#onMeasure(int,int)调用期间,然后计算希望的尺寸基于子孩子的位置。当期望所有的存在的RecyclerView中的动画能力时,他才会这样做。
自动测量的工作如下:
6.布局管理器应该调用setAutoMeasureEnabled(true)方法来启用他。
7.所有的框架LayoutManagers使用auto-measure。
8.当onMeasure被调用的时候,如果被分享的规格是EXACTLY,RecyclerView将仅仅会调用LayoutManager的onMeasure方法并且返回,同时不做任何布局测量。
9.如果其中有一个规格不是EXACT,RecyclerView将会使用调用onMeasure方法开启布局处理。这将会处理所有的等待中的Adapter更新并且决定是否去运行一个预布局。
10.如果它决定那么做了,它将会首先调用onLayoutChildren(Recycler, State)方法用isPreLayout()方法设置成true。
11.暂时,getWidth()方法和getHeight()方法将会仍然返回RecyclerView的宽度和高度作为最后的布局的计算。
12.在处理一个预布局的情形的时候,RecyclerView将会调用onLayoutChildren(Recycler, State)和isMeasuring()设置成true并且isPreLayout()设置成false。
13.这个LayoutManager可以通过getHeight(),getHeightMode(),getWidth(),getWidthMode()测量规格。
14.在布局测量之后,RecyclerView设置被测量的宽度和高度通过计算绑定的盒子为子孩子。(加上RecyclerView的边距)。
15.LayoutManager可以重写setMeasuredDimension(Rect, int, int)去选择不同的值。举个例子,GridLayoutManager重写这个值去处理垂直有3列但是只有两个item的情况。他应该仍然测量他的宽度适用于3个item而不是两个。
16.任何跟随着对于RecyclerView的测量的方法将会运行onLayoutChildren(Recycler, State)用State#isMeasuring()设置成true并且State#isPreLayout()设置成false。RecyclerView将会注意那些views被真正的添加/移除/改变为了动画以至于LayoutManager应该不用担心他们并且处理每一个onLayoutChildren(Recycler,State)的调用就好像它是最后一个。
17.当测量完成了并且RecyclerView的onLayout(boolean, int, int, int, int)方法被调用,RecyclerView检查是否它已经做过了布局的计算在测量传递期间,如果它确实是这样的话,它重用那个信息。这将仍然决定去调用onLayoutChildren(Recycler, State)方法如果上一个测量规格是不同的从final的规格或者Adapter内容已经改变了在measure和layout方法调用之间。
18.最后,动画被计算并且运行如常。
public void setAutoMeasureEnabled(booleanenabled) {
mAutoMeasure = enabled;
}
//返回值:LayoutManager有没有使用自动测量的API
public boolean isAutoMeasureEnabled() {
return mAutoMeasure;
}
//注释未翻译
//大体意思:支持预布局item动画
public boolean supportsPredictiveItemAnimations(){
return false;
}
//注释未翻译
//大体意思:设置item预取为启用状态
public final voidsetItemPrefetchEnabled(boolean enabled) {
if (enabled != mItemPrefetchEnabled) {
mItemPrefetchEnabled = enabled;
mPrefetchMaxCountObserved = 0;
if (mRecyclerView != null) {
mRecyclerView.mRecycler.updateViewCacheSize();
}
}
}
//设置是否LayoutManager应该被查询为了它视口之外的view当ui线程是空闲的在边框之间。
public final booleanisItemPrefetchEnabled() {
return mItemPrefetchEnabled;
}
//注释未翻译
//大体意思:收集相邻的预取的位置
public voidcollectAdjacentPrefetchPositions(int dx, int dy, State state,
LayoutPrefetchRegistrylayoutPrefetchRegistry) {}
//注释未翻译
//大体意思:收集初始化的预取的位置
public voidcollectInitialPrefetchPositions(int adapterItemCount,
LayoutPrefetchRegistrylayoutPrefetchRegistry) {}
//传递连接给window
void dispatchAttachedToWindow(RecyclerViewview) {
mIsAttachedToWindow = true;
onAttachedToWindow(view);
}
//传递脱离连接从window
voiddispatchDetachedFromWindow(RecyclerView view, Recycler recycler) {
mIsAttachedToWindow = false;
onDetachedFromWindow(view, recycler);
}
//返回是否LayoutManager目前被连接到一个被连接到视窗的RecyclerView
public boolean isAttachedToWindow() {
return mIsAttachedToWindow;
}
//这个方法引起一个子线程去执行下一个动画时间步骤。这个线程将会被运行在用户接口线程。
当LayoutManager没有被连接到一个RecyclerView调用这个方法不会有任何影响。
public void postOnAnimation(Runnableaction) {
if (mRecyclerView != null) {
ViewCompat.postOnAnimation(mRecyclerView, action);
}
}
//移除某个明确的子线程从消息队列中。
当LayoutManager没有被连接到一个RecyclerView调用这个方法不会有任何影响。
public boolean removeCallbacks(Runnableaction) {
if (mRecyclerView != null) {
returnmRecyclerView.removeCallbacks(action);
}
return false;
}
//当被连接到一个被连接到视窗的RecyclerView时会调用这个方法。
如果RecyclerView被重新连接带着相同的LayoutManager和Adapter,这将不会调用onLayoutChildren(Recycler, State)方法,如果什么都没被改变并且一个布局没有在RecyclerView被请求当他被解除连接的时候。
应该总是通过子类来实现这个方法。
@CallSuper
public void onAttachedToWindow(RecyclerViewview) {
}
//重写onDetachedFromWindow(RecyclerView,Recycler)
@Deprecated
public void onDetachedFromWindow(RecyclerViewview) {
}
//注释未翻译
@CallSuper
public voidonDetachedFromWindow(RecyclerView view, Recycler recycler) {
onDetachedFromWindow(view);
}
//检查这个RecyclerView是否被配置去修剪孩子的views到它的边距。
public boolean getClipToPadding() {
return mRecyclerView != null&& mRecyclerView.mClipToPadding;
}
//注释未翻译(为什么一个打印日志的方法也有这么多的注释?50行!)
public void onLayoutChildren(Recyclerrecycler, State state) {
Log.e(TAG, "You must override onLayoutChildren(Recycler recycler,State state) ");
}
//注释未翻译
public void onLayoutCompleted(State state){
}
//抽象方法
创建一个默认的LayoutParams对象为了RecyclerView的孩子
LayoutParams将会经常想要去使用一个定制的LayoutParams类型去存储额外的对于布局明确的信息。
客户端代码应该实现继承RecyclerView.LayoutParams为了这个目的。
重要提示:如果你使用自己定制的LayoutParams类型你必须也重写
checkLayoutParams(LayoutParams)},
generateLayoutParams(android.view.ViewGroup.LayoutParams)}and
generateLayoutParams(android.content.Context,android.util.AttributeSet)
public abstract LayoutParamsgenerateDefaultLayoutParams();
//决定供给的LayoutParams对象的可用性
这应该检查以确保对象是正确的类型并且所有的值是在可接受的范围之内的。
这个默认的实现返回true对于非空的参数
public booleancheckLayoutParams(LayoutParams lp) {
return lp != null;
}
//创建一个合适的LayoutParams对象给这个LayoutManager,复制有关的值从供给的LayoutParams对象如果可能的话。
public LayoutParamsgenerateLayoutParams(ViewGroup.LayoutParams lp) {
if (lp instanceof LayoutParams) {
return newLayoutParams((LayoutParams) lp);
} else if (lp instanceof MarginLayoutParams) {
return newLayoutParams((MarginLayoutParams) lp);
} else {
return new LayoutParams(lp);
}
}
public LayoutParamsgenerateLayoutParams(Context c, AttributeSet attrs) {
return new LayoutParams(c, attrs);
}
//通过屏幕坐标中的DX像素水平滚动并返回所经过的距离。
默认的实现不做任何事并且返回0。
public int scrollHorizontallyBy(int dx,Recycler recycler, State state) {
return 0;
}
public int scrollVerticallyBy(int dy,Recycler recycler, State state) {
return 0;
}
//查询水平的滚动目前是否被支持。默认的实现是返回false
public boolean canScrollHorizontally() {
return false;
}
public boolean canScrollVertically() {
return false;
}
//滑到指定的Adapter的位置
public void scrollToPosition(int position){
if (DEBUG) {
Log.e(TAG, "You MUSTimplement scrollToPosition. It will soon become abstract");
}
}
public voidsmoothScrollToPosition(RecyclerView recyclerView, State state,
int position) {
Log.e(TAG, "You must override smoothScrollToPosition to supportsmooth scrolling");
}
//开始一个平滑的滚动使用共享的SmoothScroller。
调用这个方法将会退出任何先前的平滑滚动请求。
public void startSmoothScroll(SmoothScrollersmoothScroller) {
if (mSmoothScroller != null && smoothScroller != mSmoothScroller
&&mSmoothScroller.isRunning()) {
mSmoothScroller.stop();
}
mSmoothScroller = smoothScroller;
mSmoothScroller.start(mRecyclerView, this);
}
//查看当前是否处于平滑滚动的状态
public boolean isSmoothScrolling() {
return mSmoothScroller != null && mSmoothScroller.isRunning();
}
//返回被处理的布局方向对于这个RecyclerView
public int getLayoutDirection() {
return ViewCompat.getLayoutDirection(mRecyclerView);
}
//结束所有在View上的由ItemAnimator创建的动画
public void endAnimation(View view) {
if (mRecyclerView.mItemAnimator != null) {
mRecyclerView.mItemAnimator.endAnimation(getChildViewHolderInt(view));
}
}
//增加消失的view
//只在onlayoutchildren(Recycler, State)
对于已知的将要离开的布局,或者是因为它已经
{ # notifyitemremoved(int)删除}或是因为它其实不在
可见部分的容器而被开展以通知recyclerview
如何从视图中使该项动画化。
视图被添加通过这种方法将布局管理器后看不见
在dispatchlayout通过完成后。他们不能通过检索# getchildAt(int)}
或不会被包含进{ # getchildcount() }方法。
public void addDisappearingView(View child){
addDisappearingView(child, -1);
}
public void addDisappearingView(View child,int index) {
addViewInt(child, index, true);
}
//添加一个View到当前被添加的RecyclerView如果需要的话。LayoutManagers应该使用这个方法去添加从Recycler中被获取的views使用Recycler#getViewForPosition(int)
public void addView(View child) {
addView(child, -1);
}
public void addView(View child, int index){
addViewInt(child, index, false);
}
private void addViewInt(View child, intindex, boolean disappearing) {
final ViewHolder holder = getChildViewHolderInt(child);
if (disappearing || holder.isRemoved()) {
// these views will be hiddenat the end of the layout pass.
mRecyclerView.mViewInfoStore.addToDisappearedInLayout(holder);
} else {
// This may look likeunnecessary but may happen if layout manager supports
// predictive layouts andadapter removed then re-added the same item.
// In this case, added versionwill be visible in the post layout (because add is
// deferred) but RV will still bindit to the same View.
// So if a View re-appears inpost layout pass, remove it from disappearing list.
mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(holder);
}
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
if (holder.wasReturnedFromScrap() || holder.isScrap()) {
if (holder.isScrap()) {
holder.unScrap();
} else {
holder.clearReturnedFromScrapFlag();
}
mChildHelper.attachViewToParent(child, index, child.getLayoutParams(),false);
if (DISPATCH_TEMP_DETACH) {
ViewCompat.dispatchFinishTemporaryDetach(child);
}
} else if (child.getParent() == mRecyclerView) { // it was not a scrapbut a valid child
// ensure in correct position
int currentIndex =mChildHelper.indexOfChild(child);
if (index == -1) {
index =mChildHelper.getChildCount();
}
if (currentIndex == -1) {
throw newIllegalStateException("Added View has RecyclerView as parent but"
+ " view isnot a real child. Unfiltered index:"
+mRecyclerView.indexOfChild(child));
}
if (currentIndex != index) {
mRecyclerView.mLayout.moveView(currentIndex, index);
}
} else {
mChildHelper.addView(child,index, false);
lp.mInsetsDirty = true;
if (mSmoothScroller != null&& mSmoothScroller.isRunning()) {
mSmoothScroller.onChildAttachedToWindow(child);
}
}
if (lp.mPendingInvalidate) {
if (DEBUG) {
Log.d(TAG, "consumingpending invalidate on child " + lp.mViewHolder);
}
holder.itemView.invalidate();
lp.mPendingInvalidate = false;
}
}
//从目前附加recyclerview如果需要删除视图。LayoutManagers应该
使用此方法可以完全删除不再需要的子视图。
layoutmanagers应该考虑回收删除视图使用
{Recycler# RecycleView(Android.view.View)}。
//都是通过ChildHelper来处理的
public void removeView(View child) {
mChildHelper.removeView(child);
}
public void removeViewAt(int index) {
final View child = getChildAt(index);
if (child != null) {
mChildHelper.removeViewAt(index);
}
}
public void removeAllViews() {
// Only remove non-animating views
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
mChildHelper.removeViewAt(i);
}
}
//返回RecyclerView的文本的基线的偏移量从他的上边界。
public int getBaseline() {
return -1;
}
//返回Adapter的item的位置代表着被给的View。这不包含任何Adapter改变,那或许已经发生在最后的布局之后。
public int getPosition(View view) {
return ((RecyclerView.LayoutParams)view.getLayoutParams()).getViewLayoutPosition();
}
//取得Adapter中的view类型
public int getItemViewType(View view) {
return getChildViewHolderInt(view).getItemViewType();
}
//遍历给定视图的祖先,并返回包含它的item视图。和它的布局管理器的直接子孩子。
注意这个方法可能返回空如果这个View是一个RecyclerView的孩子。但不是一个LayoutManager的孩子。
@Nullable
public View findContainingItemView(Viewview) {
if (mRecyclerView == null) {
return null;
}
//找到RecyclerView中包含的这个item,找不到返回空
View found = mRecyclerView.findContainingItemView(view);
if (found == null) {
return null;
}
//如果被隐藏了也返回空
if (mChildHelper.isHidden(found)) {
return null;
}
return found;
}
//找到一个代表被给定的Adapter的位置的View
这个方法遍历每一个孩子因为他没有关于Child的信息的命令。
重写这个方法去提升表现如果你的LayoutManager保持关于孩子View的数据
如果一个View被忽略通过ignoreView(View)方法,这也被这个方法忽略。
public View findViewByPosition(intposition) {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
View child = getChildAt(i);
ViewHolder vh =getChildViewHolderInt(child);
if (vh == null) {
continue;
}
if (vh.getLayoutPosition() ==position && !vh.shouldIgnore() &&
(mRecyclerView.mState.isPreLayout() || !vh.isRemoved())) {
return child;
}
}
return null;
}
//暂时从一个View中移除
layoutmanagers可能要执行的一个轻量级的分离操作重新安排
目前在recyclerview的views。一般的布局管理器的实现
要想使用{ # detachandscrapview(android.view.view,recyclerview。Recycler)}
使分离的视图可以重新绑定和重用。
如果一个布局管理器使用此方法分离视图,它要
{# attachview(android.view.view,int,recyclerview。LayoutParams)将}
或{ # removedetachedview(Android。view。View)完全删除、分离的观点
之前的布局管理器的入口点的方法称为recyclerview返回。
public void detachView(View child) {
final int ind = mChildHelper.indexOfChild(child);
if (ind >= 0) {
detachViewInternal(ind, child);
}
}
public void detachViewAt(int index) {
detachViewInternal(index, getChildAt(index));
}
private void detachViewInternal(int index,View view) {
//传递临时的删除
if (DISPATCH_TEMP_DETACH) {
ViewCompat.dispatchStartTemporaryDetach(view);
}
mChildHelper.detachViewFromParent(index);
}
//重新建立联系对于一个之前解除联系的View。这个方法不应该被用来去重新连接那些之前detachAndScrapView(android.view.View, RecyclerView.Recycler)用来报废的Views
public void attachView(View child, intindex, LayoutParams lp) {
ViewHolder vh = getChildViewHolderInt(child);
if (vh.isRemoved()) {
mRecyclerView.mViewInfoStore.addToDisappearedInLayout(vh);
} else {
mRecyclerView.mViewInfoStore.removeFromDisappearedInLayout(vh);
}
mChildHelper.attachViewToParent(child, index, lp, vh.isRemoved());
if (DISPATCH_TEMP_DETACH) {
ViewCompat.dispatchFinishTemporaryDetach(child);
}
}
public void attachView(View child, intindex) {
attachView(child, index, (LayoutParams) child.getLayoutParams());
}
public void attachView(View child) {
attachView(child, -1);
}
public void removeDetachedView(View child){
mRecyclerView.removeDetachedView(child, false);
}
//把一个View从一个地方移动到另外一个地方
public void moveView(int fromIndex, inttoIndex) {
View view = getChildAt(fromIndex);
if (view == null) {
throw newIllegalArgumentException("Cannot move a child from non-existingindex:"
+ fromIndex);
}
detachViewAt(fromIndex);
attachView(view, toIndex);
}
//删除一个子视图并且把它加到Recycler的废品堆里
public void detachAndScrapView(View child,Recycler recycler) {
int index = mChildHelper.indexOfChild(child);
scrapOrRecycleView(recycler, index, child);
}
public void detachAndScrapViewAt(int index,Recycler recycler) {
final View child = getChildAt(index);
scrapOrRecycleView(recycler, index, child);
}
//把一个指定的View移除并且用指定的Recycler来回收它
public void removeAndRecycleView(Viewchild, Recycler recycler) {
removeView(child);
recycler.recycleView(child);
}
public void removeAndRecycleViewAt(intindex, Recycler recycler) {
final View view = getChildAt(index);
removeViewAt(index);
recycler.recycleView(view);
}
//返回当前连接到父RecyclerView上的item数量
public int getChildCount() {
return mChildHelper != null ? mChildHelper.getChildCount() : 0;
}
public View getChildAt(int index) {
return mChildHelper != null ? mChildHelper.getChildAt(index) : null;
}
//这个值被设置只有当自动测量开启的时候
public int getWidthMode() {
return mWidthMode;
}
public int getHeightMode() {
return mHeightMode;
}
public int getWidth() {
return mWidth;
}
public int getHeight() {
return mHeight;
}
public int getPaddingLeft() {
return mRecyclerView != null ? mRecyclerView.getPaddingLeft() : 0;
}
public int getPaddingTop() {
return mRecyclerView != null ? mRecyclerView.getPaddingTop() : 0;
}
public int getPaddingRight() {
return mRecyclerView != null ? mRecyclerView.getPaddingRight() : 0;
}
public int getPaddingBottom() {
return mRecyclerView != null ? mRecyclerView.getPaddingBottom() : 0;
}
public int getPaddingStart() {
return mRecyclerView != null ? ViewCompat.getPaddingStart(mRecyclerView): 0;
}
public int getPaddingEnd() {
return mRecyclerView != null ?ViewCompat.getPaddingEnd(mRecyclerView) : 0;
}
//返回true如果这个LayoutManager绑定的RecyclerView有焦点
public boolean isFocused() {
return mRecyclerView != null && mRecyclerView.isFocused();
}
public boolean hasFocus() {
return mRecyclerView != null && mRecyclerView.hasFocus();
}
//返回有或者包含焦点的item的View
public View getFocusedChild() {
if (mRecyclerView == null) {
return null;
}
final View focused = mRecyclerView.getFocusedChild();
if (focused == null || mChildHelper.isHidden(focused)) {
return null;
}
return focused;
}
//注意这个数量不需要和State#getItemCount()相等。在state是可用的方法中,你应该使用State#getItemCount()代替。对于更多的细节,检查State#getItemCount()方法的注释
(这里的代码写的很漂亮)
public int getItemCount() {
finalAdapter a = mRecyclerView != null ? mRecyclerView.getAdapter() : null;
return a != null ? a.getItemCount(): 0;
}
//抵消(?)所有子视图连接到父母的recyclerview DX像素横轴。
public void offsetChildrenHorizontal(intdx) {
if (mRecyclerView != null) {
mRecyclerView.offsetChildrenHorizontal(dx);
}
}
public void offsetChildrenVertical(int dy){
if (mRecyclerView != null) {
mRecyclerView.offsetChildrenVertical(dy);
}
}
//标志一个视图,以便它不会被废弃或回收。
忽视儿童的范围严格限制在位置跟踪、报废和
循环。方法如{# removeandrecycleallviews(Recycler)}会忽略孩子
而{ # removeallviews() }或{ # offsetchildrenhorizontal(int)}不会忽视孩子。
在这个孩子可以再利用之前,你必须调用{ # stopignoringview(view)}。
你可以调用这个方法只有你的layoutmanger是onlayout或onscroll回调。
public void ignoreView(View view) {
if (view.getParent() != mRecyclerView ||mRecyclerView.indexOfChild(view) == -1) {
// checking this becausecalling this method on a recycled or detached view may
// cause loss of state.
throw newIllegalArgumentException("View should be fully attached to beignored");
}
final ViewHolder vh = getChildViewHolderInt(view);
vh.addFlags(ViewHolder.FLAG_IGNORE);
mRecyclerView.mViewInfoStore.removeViewHolder(vh);
}
public void stopIgnoringView(View view) {
final ViewHolder vh = getChildViewHolderInt(view);
vh.stopIgnoring();
vh.resetInternal();
vh.addFlags(ViewHolder.FLAG_INVALID);
}
//暂时分离并删除所有当前附加的子视图。视图将被废弃。
到了回收站。回收利用废旧的看法可能更喜欢之前
以前回收的其他视图。(注意这里是删除所有哦)
public void detachAndScrapAttachedViews(Recyclerrecycler) {
final int childCount = getChildCount();
for (int i = childCount - 1; i >= 0; i--) {
final View v = getChildAt(i);
scrapOrRecycleView(recycler, i,v);
}
}
private void scrapOrRecycleView(Recyclerrecycler, int index, View view) {
final ViewHolder viewHolder = getChildViewHolderInt(view);
if (viewHolder.shouldIgnore()) {
if (DEBUG) {
Log.d(TAG, "ignoringview " + viewHolder);
}
return;
}
if (viewHolder.isInvalid() && !viewHolder.isRemoved() &&
!mRecyclerView.mAdapter.hasStableIds()) {
removeViewAt(index);
recycler.recycleViewHolderInternal(viewHolder);
} else {
detachViewAt(index);
recycler.scrapView(view);
mRecyclerView.mViewInfoStore.onViewDetached(viewHolder);
}
}
//回收报废的Views
当一个view被解除联系并且被移除,这没有触发一个ViewGroup的无效。这是一个被期待的行为如果被报废的views被用于动画。否则,我们需要去调用移除并且使无效化,RecyclerView去确保UI更新。
void removeAndRecycleScrapInt(Recyclerrecycler) {
//取得碎片的数量
final int scrapCount = recycler.getScrapCount();
// Loop backward, recycler might be changed by removeDetachedView()
for (int i = scrapCount - 1; i >= 0; i--) {
final View scrap =recycler.getScrapViewAt(i);
final ViewHolder vh =getChildViewHolderInt(scrap);
//如果处于被忽略状态,那当然就不能回收这个东西
if (vh.shouldIgnore()) {
continue;
}
// If the scrap view isanimating, we need to cancel them first. If we cancel it
// here, ItemAnimator callbackmay recycle it which will cause double recycling.
// To avoid this, we mark it asnot recycleable before calling the item animator.
// Since removeDetachedViewcalls a user API, a common mistake (ending animations on
// the view) may recycle ittoo, so we guard it before we call user APIs.
设置这个viewHolder成为不可回收状态
vh.setIsRecyclable(false);
//如果他是暂时被移除的,那就彻底移除它?
if (vh.isTmpDetached()) {
mRecyclerView.removeDetachedView(scrap, false);
}
//这个RecyclerView的item的动画不为空,那就把这个动画给结束了
if (mRecyclerView.mItemAnimator!= null) {
mRecyclerView.mItemAnimator.endAnimation(vh);
}
//把它设置为可回收状态
vh.setIsRecyclable(true);
//快速回收这个报废的view
recycler.quickRecycleScrapView(scrap);
}
//清空view
recycler.clearScrap();
if (scrapCount > 0) {
mRecyclerView.invalidate();
}
}
//测量子孩子使用标准的测量方法的政策。采取父亲的RecyclerView的边距并且任何被添加的item的装饰进入账户。
如果这个RecyclerView可以被滚动要么在尺寸被调用或许传递0作为widthUsed或者heightUsed参数作为他们将会被不相干化。
public void measureChild(View child, intwidthUsed, int heightUsed) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Rect insets =mRecyclerView.getItemDecorInsetsForChild(child);
widthUsed += insets.left + insets.right;
heightUsed += insets.top + insets.bottom;
final int widthSpec = getChildMeasureSpec(getWidth(), getWidthMode(),
getPaddingLeft() +getPaddingRight() + widthUsed, lp.width,
canScrollHorizontally());
final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
getPaddingTop() +getPaddingBottom() + heightUsed, lp.height,
canScrollVertically());
if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
child.measure(widthSpec,heightSpec);
}
}
//RecyclerView内部做他自己的View测量缓存,那应该由WRAP_CONTENT来帮助完成。
使用这个方法如果View已经被测量过一次在布局的传递中(layout pass?)。
boolean shouldReMeasureChild(View child,int widthSpec, int heightSpec, LayoutParams lp) {
return !mMeasurementCacheEnabled
|| !isMeasurementUpToDate(child.getMeasuredWidth(),widthSpec, lp.width)
||!isMeasurementUpToDate(child.getMeasuredHeight(), heightSpec, lp.height);
}
//相对于上面那个方法,如果View还没有被测量过。就调用这个方法。
boolean shouldMeasureChild(View child, intwidthSpec, int heightSpec, LayoutParams lp) {
return child.isLayoutRequested()
||!mMeasurementCacheEnabled
||!isMeasurementUpToDate(child.getWidth(), widthSpec, lp.width)
|| !isMeasurementUpToDate(child.getHeight(),heightSpec, lp.height);
}
//除了View的框架的测量缓存,RecyclerView使用它的附加的测量缓存给它的孩子去避免重新测量他们当不需要的时候。这是开启的通过默认的但是这个可以被关闭通过setMeasurementCacheEnabled(boolean)。
public boolean isMeasurementCacheEnabled(){
return mMeasurementCacheEnabled;
}
public voidsetMeasurementCacheEnabled(boolean measurementCacheEnabled) {
mMeasurementCacheEnabled = measurementCacheEnabled;
}
//判断是否测量更新了
private static booleanisMeasurementUpToDate(int childSize, int spec, int dimension) {
final int specMode = MeasureSpec.getMode(spec);
final int specSize = MeasureSpec.getSize(spec);
if (dimension > 0 && childSize != dimension) {
return false;
}
switch (specMode) {
case MeasureSpec.UNSPECIFIED:
return true;
case MeasureSpec.AT_MOST:
return specSize >=childSize;
case MeasureSpec.EXACTLY:
return specSize == childSize;
}
return false;
}
//测量一个view使用标准的测量政策。采取父RecyclerView的边距。任何被添加的item装饰和child边距进入解释(账户?)。
public void measureChildWithMargins(Viewchild, int widthUsed, int heightUsed) {
final LayoutParams lp = (LayoutParams) child.getLayoutParams();
final Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
widthUsed += insets.left + insets.right;
heightUsed += insets.top + insets.bottom;
final int widthSpec =getChildMeasureSpec(getWidth(), getWidthMode(),
getPaddingLeft() +getPaddingRight() +
lp.leftMargin +lp.rightMargin + widthUsed, lp.width,
canScrollHorizontally());
final int heightSpec = getChildMeasureSpec(getHeight(), getHeightMode(),
getPaddingTop() +getPaddingBottom() +
lp.topMargin +lp.bottomMargin + heightUsed, lp.height,
canScrollVertically());
if (shouldMeasureChild(child, widthSpec, heightSpec, lp)) {
child.measure(widthSpec,heightSpec);
}
}
//计算一个测量规格的值用来测量一个孩子的View以一个尺寸。
@Deprecated
public static int getChildMeasureSpec(int parentSize, int padding, intchildDimension,
boolean canScroll) {
int size = Math.max(0, parentSize - padding);
int resultSize = 0;
int resultMode = 0;
if (canScroll) {
if (childDimension >= 0) {
resultSize =childDimension;
resultMode =MeasureSpec.EXACTLY;
} else {
// MATCH_PARENT can't beapplied since we can scroll in this dimension, wrap
// instead usingUNSPECIFIED.
resultSize = 0;
resultMode =MeasureSpec.UNSPECIFIED;
}
} else {
if (childDimension >= 0) {
resultSize =childDimension;
resultMode =MeasureSpec.EXACTLY;
} else if (childDimension ==LayoutParams.MATCH_PARENT) {
resultSize = size;
// TODO this should be myspec.
resultMode =MeasureSpec.EXACTLY;
} else if (childDimension ==LayoutParams.WRAP_CONTENT) {
resultSize = size;
resultMode = MeasureSpec.AT_MOST;
}
}
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
public static int getChildMeasureSpec(intparentSize, int parentMode, int padding,
int childDimension, booleancanScroll) {
int size = Math.max(0, parentSize - padding);
int resultSize = 0;
int resultMode = 0;
if (canScroll) {
if (childDimension >= 0) {
resultSize = childDimension;
resultMode =MeasureSpec.EXACTLY;
} else if (childDimension ==LayoutParams.MATCH_PARENT) {
switch (parentMode) {
caseMeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:
resultSize = size;
resultMode =parentMode;
break;
caseMeasureSpec.UNSPECIFIED:
resultSize = 0;
resultMode =MeasureSpec.UNSPECIFIED;
break;
}
} else if (childDimension ==LayoutParams.WRAP_CONTENT) {
resultSize = 0;
resultMode =MeasureSpec.UNSPECIFIED;
}
} else {
if (childDimension >= 0) {
resultSize =childDimension;
resultMode =MeasureSpec.EXACTLY;
} else if (childDimension ==LayoutParams.MATCH_PARENT) {
resultSize = size;
resultMode = parentMode;
} else if (childDimension ==LayoutParams.WRAP_CONTENT) {
resultSize = size;
if (parentMode ==MeasureSpec.AT_MOST || parentMode == MeasureSpec.EXACTLY) {
resultMode =MeasureSpec.AT_MOST;
} else {
resultMode =MeasureSpec.UNSPECIFIED;
}
}
}
//noinspection WrongConstant
return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
}
//这个Decorate和ItemDecorate类有关
public int getDecoratedMeasuredWidth(Viewchild) {
final Rect insets = ((LayoutParams)child.getLayoutParams()).mDecorInsets;
return child.getMeasuredWidth() + insets.left + insets.right;
}
public int getDecoratedMeasuredHeight(Viewchild) {
final Rect insets = ((LayoutParams)child.getLayoutParams()).mDecorInsets;
return child.getMeasuredHeight() + insets.top + insets.bottom;
}
//注释未翻译
public void layoutDecorated(View child, intleft, int top, int right, int bottom) {
final Rect insets = ((LayoutParams)child.getLayoutParams()).mDecorInsets;
child.layout(left + insets.left, top + insets.top, right - insets.right,
bottom - insets.bottom);
}
//注释未翻译
public void layoutDecoratedWithMargins(Viewchild, 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);
}
//计算视图的包围盒,同时考虑其矩阵的变化。
(平移、尺度等)方面的recyclerview。
如果{ }是{includedecorinsets true},他们是第一次使用前
视图的矩阵,以便装饰偏移也经历相同的转换。
public void getTransformedBoundingBox(Viewchild, boolean includeDecorInsets, Rect out) {
if (includeDecorInsets) {
Rect insets = ((LayoutParams)child.getLayoutParams()).mDecorInsets;
out.set(-insets.left,-insets.top,
child.getWidth() +insets.right, child.getHeight() + insets.bottom);
} else {
out.set(0, 0, child.getWidth(),child.getHeight());
}
if (mRecyclerView != null) {
final Matrix childMatrix =ViewCompat.getMatrix(child);
if (childMatrix != null&& !childMatrix.isIdentity()) {
final RectF tempRectF =mRecyclerView.mTempRectF;
tempRectF.set(out);
childMatrix.mapRect(tempRectF);
out.set(
(int)Math.floor(tempRectF.left),
(int)Math.floor(tempRectF.top),
(int) Math.ceil(tempRectF.right),
(int)Math.ceil(tempRectF.bottom)
);
}
}
out.offset(child.getLeft(), child.getTop());
}
//这回是两个一起上了
public void getDecoratedBoundsWithMargins(Viewview, Rect outBounds) {
RecyclerView.getDecoratedBoundsWithMarginsInt(view, outBounds);
}
public int getDecoratedLeft(View child) {
return child.getLeft() - getLeftDecorationWidth(child);
}
public int getDecoratedTop(View child) {
return child.getTop() - getTopDecorationHeight(child);
}
public int getDecoratedRight(View child) {
return child.getRight() + getRightDecorationWidth(child);
}
public int getDecoratedBottom(View child) {
return child.getBottom() + getBottomDecorationHeight(child);
}
public voidcalculateItemDecorationsForChild(View child, Rect outRect) {
if (mRecyclerView == null) {
outRect.set(0, 0, 0, 0);
return;
}
Rect insets = mRecyclerView.getItemDecorInsetsForChild(child);
outRect.set(insets);
}
public int getTopDecorationHeight(Viewchild) {
return ((LayoutParams) child.getLayoutParams()).mDecorInsets.top;
}
public int getBottomDecorationHeight(Viewchild) {
return ((LayoutParams) child.getLayoutParams()).mDecorInsets.bottom;
}
public int getLeftDecorationWidth(Viewchild) {
return ((LayoutParams) child.getLayoutParams()).mDecorInsets.left;
}
public int getRightDecorationWidth(Viewchild) {
return ((LayoutParams) child.getLayoutParams()).mDecorInsets.right;
}
//当用被给定的方向已经失败对于现在的RecyclerView里的内容寻找一个可调焦的View时调用。
这是一个LayoutManager的机会去填充Views用给定的方向去完成这个请求如果它可以。这个布局管理器应该连接并且返回将被关注的View,如果一个可聚焦的View在被给定的方向被发现了。否则,如果所有存在的(或者新的被填充的View)是不可聚焦的,这返回下一个不可聚焦的View去变成可见的在屏幕上。这个不可聚焦的View是通常的第一个View,那是要么部分的要么全部的在Rv的距离绑定区域之外的在被给定的方向。这个默认的实现返回空。
@Nullable
public View onFocusSearchFailed(View focused, int direction, Recyclerrecycler,
State state) {
return null;
}
//注释未翻译
public View onInterceptFocusSearch(Viewfocused, int direction) {
return null;
}
//返回滚动量带来了直接在孩子的坐标系统中对recyclerview填充区域。
private int[]getChildRectangleOnScreenScrollAmount(RecyclerView parent, View child,
Rect rect, boolean immediate) {
int[] out = new int[2];
final int parentLeft = getPaddingLeft();
final int parentTop = getPaddingTop();
final int parentRight = getWidth() - getPaddingRight();
final int parentBottom = getHeight() - getPaddingBottom();
final int childLeft = child.getLeft() + rect.left - child.getScrollX();
final int childTop = child.getTop() + rect.top - child.getScrollY();
final int childRight = childLeft + rect.width();
final int childBottom = childTop + rect.height();
final int offScreenLeft = Math.min(0, childLeft - parentLeft);
final int offScreenTop = Math.min(0, childTop - parentTop);
final int offScreenRight = Math.max(0, childRight - parentRight);
final int offScreenBottom =Math.max(0, childBottom - parentBottom);
// Favor the "start" layout direction over the end whenbringing one side or the other
// of a large rect into view. If we decide to bring in end because startis already
// visible, limit the scroll such that start won't go out of bounds.
final int dx;
if (getLayoutDirection() == ViewCompat.LAYOUT_DIRECTION_RTL) {
dx = offScreenRight != 0 ?offScreenRight
:Math.max(offScreenLeft, childRight - parentRight);
} else {
dx = offScreenLeft != 0 ?offScreenLeft
: Math.min(childLeft -parentLeft, offScreenRight);
}
// Favor bringing the top into view over the bottom. If top is alreadyvisible and
// we should scroll to make bottom visible, make sure top does not goout of bounds.
final int dy = offScreenTop != 0 ? offScreenTop
: Math.min(childTop -parentTop, offScreenBottom);
out[0] = dx;
out[1] = dy;
return out;
}
public booleanrequestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
boolean immediate) {
return requestChildRectangleOnScreen(parent, child, rect, immediate,false);
}
//注释未翻译
public booleanrequestChildRectangleOnScreen(RecyclerView parent, View child, Rect rect,
boolean immediate,
boolean focusedChildVisible) {
int[] scrollAmount = getChildRectangleOnScreenScrollAmount(parent,child, rect,
immediate);
int dx = scrollAmount[0];
int dy = scrollAmount[1];
if (!focusedChildVisible ||isFocusedChildVisibleAfterScrolling(parent, dx, dy)) {
if (dx != 0 || dy != 0) {
if (immediate) {
parent.scrollBy(dx,dy);
} else {
parent.smoothScrollBy(dx, dy);
}
return true;
}
}
return false;
}
//返回给定子视图是否在填充部分部分或完全可见。
对recyclerview有界区域,根据输入的参数。
如果视图与RV的填充边界区域有非零重叠,则部分可见。
如果acceptendpointinclusion标志设置为true,它也被认为是部分
可见,如果它位于RV的范围之外,它击中RV的开始或结束。
界限。
public booleanisViewPartiallyVisible(@NonNull View child, boolean completelyVisible,
booleanacceptEndPointInclusion) {
int boundsFlag = (ViewBoundsCheck.FLAG_CVS_GT_PVS |ViewBoundsCheck.FLAG_CVS_EQ_PVS
|ViewBoundsCheck.FLAG_CVE_LT_PVE | ViewBoundsCheck.FLAG_CVE_EQ_PVE);
boolean isViewFullyVisible =mHorizontalBoundCheck.isViewWithinBoundFlags(child,
boundsFlag)
&&mVerticalBoundCheck.isViewWithinBoundFlags(child, boundsFlag);
if (completelyVisible) {
return isViewFullyVisible;
} else {
return !isViewFullyVisible;
}
}
//返回当前焦点的孩子是否在给定的滚动量范围内停留在RV的范围内。
private booleanisFocusedChildVisibleAfterScrolling(RecyclerView parent, int dx, int dy) {
final View focusedChild = parent.getFocusedChild();
if (focusedChild == null) {
return false;
}
final int parentLeft = getPaddingLeft();
final int parentTop = getPaddingTop();
final int parentRight = getWidth() - getPaddingRight();
final int parentBottom = getHeight() - getPaddingBottom();
final Rect bounds = mRecyclerView.mTempRect;
getDecoratedBoundsWithMargins(focusedChild, bounds);
if (bounds.left - dx >= parentRight || bounds.right - dx <=parentLeft
|| bounds.top - dy >= parentBottom ||bounds.bottom - dy <= parentTop) {
return false;
}
return true;
}
@Deprecated
public boolean onRequestChildFocus(RecyclerView parent, View child, Viewfocused) {
// eat the request if we are in the middle of a scroll or layout
return isSmoothScrolling() || parent.isComputingLayout();
}
//注释未翻译
public booleanonRequestChildFocus(RecyclerView parent, State state, View child,
View focused) {
return onRequestChildFocus(parent, child, focused);
}
//注释未翻译
public void onAdapterChanged(AdapteroldAdapter, Adapter newAdapter) {
}
//注释未翻译
public boolean onAddFocusables(RecyclerViewrecyclerView, ArrayList
int direction, intfocusableMode) {
return false;
}
当notifyDataSetChanged()被触发的时候而不是给定具体细节什么东西改变的时候调用这个方法。
public void onItemsChanged(RecyclerViewrecyclerView) {
}
public void onItemsAdded(RecyclerViewrecyclerView, int positionStart, int itemCount) {
}
public void onItemsRemoved(RecyclerViewrecyclerView, int positionStart, int itemCount) {
}
public void onItemsUpdated(RecyclerViewrecyclerView, int positionStart, int itemCount) {
}
public void onItemsUpdated(RecyclerViewrecyclerView, int positionStart, int itemCount,
Object payload) {
onItemsUpdated(recyclerView, positionStart, itemCount);
}
public void onItemsMoved(RecyclerViewrecyclerView, int from, int to, int itemCount) {
}
public int computeHorizontalScrollExtent(State state) {
return 0;
}
public int computeHorizontalScrollOffset(State state) {
return 0;
}
public int computeHorizontalScrollRange(State state) {
return 0;
}
public int computeVerticalScrollExtent(State state) {
return 0;
}
public intcomputeVerticalScrollOffset(State state) {
return 0;
}
public int computeVerticalScrollRange(State state) {
return 0;
}
//测量这个被连接的RecyclerView。实现方法必须调用setMeasuredDimension(int, int)
在返回之前。
默认的实现将会处理EXACTLY的测量并且尊重宽高的最小值,包含宿主RecyclerView的特性。如果被测量的是UNSPECIFIED。AT_MOST的测量将会被对待为EXACTLY并且RecyclerView将会消费所有可用的空间。
public void onMeasure(Recycler recycler,State state, int widthSpec, int heightSpec) {
mRecyclerView.defaultOnMeasure(widthSpec, heightSpec);
}
//View#setMeasuredDimension(int, int)设置被测量的尺寸。对于所在的宿主RecyclerView。
public void setMeasuredDimension(intwidthSize, int heightSize) {
mRecyclerView.setMeasuredDimension(widthSize, heightSize);
}
public int getMinimumWidth() {
return ViewCompat.getMinimumWidth(mRecyclerView);
}
public int getMinimumHeight() {
return ViewCompat.getMinimumHeight(mRecyclerView);
}
//当布局管理器应保存它的状态,这是一个好机会去保存你的滑动位置,配置,和 任何另外的那些或许被要求的去重新存储相同的布局状态如果LayoutManager是被重新创建的。
RecyclerView不验证如果布局管理器的状态之间有改变和保存恢复。这将让你分享你的LayoutParams之间信息也是你要确保他们使用相同的封装类责任。
public Parcelable onSaveInstanceState() {
return null;
}
//哇,开源中国里熟悉的方法,到底火蚁啊
public voidonRestoreInstanceState(Parcelable state) {
}
void stopSmoothScroller() {
if (mSmoothScroller != null) {
mSmoothScroller.stop();
}
}
private voidonSmoothScrollerStopped(SmoothScroller smoothScroller) {
if (mSmoothScroller == smoothScroller) {
mSmoothScroller = null;
}
}
public void onScrollStateChanged(int state){
}
public voidremoveAndRecycleAllViews(Recycler recycler) {
for (int i = getChildCount() - 1; i>= 0; i--) {
final View view =getChildAt(i);
if(!getChildViewHolderInt(view).shouldIgnore()) {
removeAndRecycleViewAt(i,recycler);
}
}
}
//注释没翻译
voidonInitializeAccessibilityNodeInfo(AccessibilityNodeInfoCompat info) {
onInitializeAccessibilityNodeInfo(mRecyclerView.mRecycler,mRecyclerView.mState, info);
}
public void onInitializeAccessibilityNodeInfo(Recyclerrecycler, State state,
AccessibilityNodeInfoCompatinfo) {
if (ViewCompat.canScrollVertically(mRecyclerView, -1) ||
ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD);
info.setScrollable(true);
}
if (ViewCompat.canScrollVertically(mRecyclerView, 1) ||
ViewCompat.canScrollHorizontally(mRecyclerView, 1)) {
info.addAction(AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD);
info.setScrollable(true);
}
final AccessibilityNodeInfoCompat.CollectionInfoCompat collectionInfo
= AccessibilityNodeInfoCompat.CollectionInfoCompat
.obtain(getRowCountForAccessibility(recycler, state),
getColumnCountForAccessibility(recycler, state),
isLayoutHierarchical(recycler, state),
getSelectionModeForAccessibility(recycler, state));
info.setCollectionInfo(collectionInfo);
}
//由可访问性委托调用,以初始化可访问性事件。
默认实现将项目计数和滚动信息添加到事件中
public voidonInitializeAccessibilityEvent(AccessibilityEvent event) {
onInitializeAccessibilityEvent(mRecyclerView.mRecycler,mRecyclerView.mState, event);
}
public voidonInitializeAccessibilityEvent(Recycler recycler, State state,
AccessibilityEvent event) {
final AccessibilityRecordCompat record = AccessibilityEventCompat
.asRecord(event);
if (mRecyclerView == null || record == null) {
return;
}
record.setScrollable(ViewCompat.canScrollVertically(mRecyclerView, 1)
||ViewCompat.canScrollVertically(mRecyclerView, -1)
||ViewCompat.canScrollHorizontally(mRecyclerView, -1)
||ViewCompat.canScrollHorizontally(mRecyclerView, 1));
if (mRecyclerView.mAdapter !=null) {
record.setItemCount(mRecyclerView.mAdapter.getItemCount());
}
}
voidonInitializeAccessibilityNodeInfoForItem(View host, AccessibilityNodeInfoCompatinfo) {
final ViewHolder vh = getChildViewHolderInt(host);
// avoid trying to create accessibility node info for removed children
if (vh != null && !vh.isRemoved() &&!mChildHelper.isHidden(vh.itemView)) {
onInitializeAccessibilityNodeInfoForItem(mRecyclerView.mRecycler,
mRecyclerView.mState,host, info);
}
}
public voidonInitializeAccessibilityNodeInfoForItem(Recycler recycler, State state,
View host,AccessibilityNodeInfoCompat info) {
int rowIndexGuess = canScrollVertically() ? getPosition(host) : 0;
int columnIndexGuess = canScrollHorizontally() ? getPosition(host) : 0;
final AccessibilityNodeInfoCompat.CollectionItemInfoCompat itemInfo
=AccessibilityNodeInfoCompat.CollectionItemInfoCompat.obtain(rowIndexGuess, 1,
columnIndexGuess, 1, false,false);
info.setCollectionItemInfo(itemInfo);
}
//注释未翻译
public voidrequestSimpleAnimationsInNextLayout() {
mRequestedSimpleAnimations = true;
}
public intgetSelectionModeForAccessibility(Recycler recycler, State state) {
return AccessibilityNodeInfoCompat.CollectionInfoCompat.SELECTION_MODE_NONE;
}
public intgetRowCountForAccessibility(Recycler recycler, State state) {
if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
return 1;
}
return canScrollVertically() ? mRecyclerView.mAdapter.getItemCount() :1;
}
public intgetColumnCountForAccessibility(Recycler recycler, State state) {
if (mRecyclerView == null || mRecyclerView.mAdapter == null) {
return 1;
}
return canScrollHorizontally() ? mRecyclerView.mAdapter.getItemCount() :1;
}
public booleanisLayoutHierarchical(Recycler recycler, State state) {
return false;
}
boolean performAccessibilityAction(intaction, Bundle args) {
return performAccessibilityAction(mRecyclerView.mRecycler,mRecyclerView.mState,
action, args);
}
//被accessibilitydelegate调用当动作是从recyclerview中要求。
public boolean performAccessibilityAction(Recyclerrecycler, State state, int action,
Bundle args) {
if (mRecyclerView == null) {
return false;
}
int vScroll = 0, hScroll = 0;
switch (action) {
case AccessibilityNodeInfoCompat.ACTION_SCROLL_BACKWARD:
if(ViewCompat.canScrollVertically(mRecyclerView, -1)) {
vScroll = -(getHeight()- getPaddingTop() - getPaddingBottom());
}
if(ViewCompat.canScrollHorizontally(mRecyclerView, -1)) {
hScroll = -(getWidth()- getPaddingLeft() - getPaddingRight());
}
break;
case AccessibilityNodeInfoCompat.ACTION_SCROLL_FORWARD:
if(ViewCompat.canScrollVertically(mRecyclerView, 1)) {
vScroll = getHeight() -getPaddingTop() - getPaddingBottom();
}
if (ViewCompat.canScrollHorizontally(mRecyclerView,1)) {
hScroll = getWidth() -getPaddingLeft() - getPaddingRight();
}
break;
}
if (vScroll == 0 && hScroll == 0) {
return false;
}
mRecyclerView.scrollBy(hScroll, vScroll);
return true;
}
booleanperformAccessibilityActionForItem(View view, int action, Bundle args) {
return performAccessibilityActionForItem(mRecyclerView.mRecycler,mRecyclerView.mState,
view, action, args);
}
public booleanperformAccessibilityActionForItem(Recycler recycler, State state, View view,
int action, Bundle args) {
return false;
}
//解析XML属性以获得布局管理器使用的最常用属性。
public static PropertiesgetProperties(Context context, AttributeSet attrs,
int defStyleAttr, intdefStyleRes) {
Properties properties = new Properties();
TypedArray a = context.obtainStyledAttributes(attrs,R.styleable.RecyclerView,
defStyleAttr, defStyleRes);
properties.orientation =a.getInt(R.styleable.RecyclerView_android_orientation, VERTICAL);
properties.spanCount = a.getInt(R.styleable.RecyclerView_spanCount, 1);
properties.reverseLayout =a.getBoolean(R.styleable.RecyclerView_reverseLayout, false);
properties.stackFromEnd =a.getBoolean(R.styleable.RecyclerView_stackFromEnd, false);
a.recycle();
return properties;
}
//内部API允许LayoutManagers被测量两次。
这不是因为layoutmanagers公众应该能够在一个处理他们的布局
但是,它是通过使现有的layoutmanagers支持包装内容非常方便
当两个方向未定义时。
这个API将默认layoutmanagers妥善落实包内容删除后
非滚动方向。
void setExactMeasureSpecsFrom(RecyclerViewrecyclerView) {
setMeasureSpecs(
MeasureSpec.makeMeasureSpec(recyclerView.getWidth(),MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(recyclerView.getHeight(),MeasureSpec.EXACTLY)
);
}
boolean shouldMeasureTwice() {
return false;
}
//是否有灵活的孩子在两个方向上
booleanhasFlexibleChildInBothOrientations() {
final int childCount = getChildCount();
for (int i = 0; i < childCount; i++) {
final View child =getChildAt(i);
final ViewGroup.LayoutParams lp= child.getLayoutParams();
if (lp.width < 0 &&lp.height < 0) {
return true;
}
}
return false;
}
//静态嵌套类:一些LayoutManager可能要使用的常用属性。
public static class Properties {
/** @attr refandroid.support.v7.recyclerview.R.styleable#RecyclerView_android_orientation */
public int orientation;
/** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_spanCount*/
public int spanCount;
/** @attr refandroid.support.v7.recyclerview.R.styleable#RecyclerView_reverseLayout */
public boolean reverseLayout;
/** @attr ref android.support.v7.recyclerview.R.styleable#RecyclerView_stackFromEnd*/
public boolean stackFromEnd;