前言:在Android的日常开发中,经常会与RecycleView打交道,如果你只是进行简单的列表展示,可能你不会用到OrientationHelper工具类。但是,如果你有更深层次的需求,对OrientationHelper和LayoutManager的理解和应用就必不可少了。本文,将从源码的角度,对OrientationHelper各参数及方法进行剖析。
OrientationHelper源码分析
之所以RecycleVIew会取代ListView,成为Android开发者们的新宠。不仅因为它对内存的优化做得更好,效率更高;还因为它能随意变换布局样式(水平列表、垂直列表、网格和瀑布流),自由性更强。用过RecycleView的同学都知道,想要让列表项竖直展示还是水平展示,只需要在初始化LayoutManager的时候指定显示方向即可,代码如下:
//初始化水平LayoutManager
LinearLayoutManager horizontalManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
//初始化竖直LayoutManager
LinearLayoutManager verticalManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
OrientationHelper其实就是对RecycleView中子View管理的工具类,并且它只是一个抽象类,类中定义了获取View布局信息的相关方法。看到这里,可能会有部分同学就会有疑惑了。。抽象类?? 难道还要让我们自己去实现这些方法?? 稍安勿躁,继续分析源码,你会发现createHorizontalHelper(对应水平的LayoutManager)和createVerticalHelper(对应竖直的LayoutManager)方法已经为我们实现了这些方法,并且返回了OrientationHelper供我们使用。接下来,我们就对上面出现的问题一一剖析。
OrientationHelper抽象类源码:
public abstract class OrientationHelper {
private static final int INVALID_SIZE = Integer.MIN_VALUE;
protected final RecyclerView.LayoutManager mLayoutManager;
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
private int mLastTotalSpace = INVALID_SIZE;
final Rect mTmpRect = new Rect();
private OrientationHelper(RecyclerView.LayoutManager layoutManager) {
mLayoutManager = layoutManager;
}
}
OrientationHelper抽象类其实很简单,就是定义一些参数、构造方法和获取View数据的抽象方法。真正需要关注的是createHorizontalHelper和createVerticalHelper的方法中的实现。
createHorizontalHelper方法详解:
public static OrientationHelper createHorizontalHelper(
RecyclerView.LayoutManager layoutManager) {
return new OrientationHelper(layoutManager) {
@Override
public int getEndAfterPadding() {
//返回RecycleView内容右边界位置(RecycleView的宽度,出去右侧内边距)
return mLayoutManager.getWidth() - mLayoutManager.getPaddingRight();
}
@Override
public int getEnd() {
//返回RecycleView的宽度
return mLayoutManager.getWidth();
}
@Override
public void offsetChildren(int amount) {
//水平横移子View amount距离
mLayoutManager.offsetChildrenHorizontal(amount);
}
@Override
public int getStartAfterPadding() {
//获取RecycleView左侧内边距(paddingLeft)
return mLayoutManager.getPaddingLeft();
}
@Override
public int getDecoratedMeasurement(View view) {
//返回view在水平方向上所占位置的大小(包括view的左右外边距)
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredWidth(view) + params.leftMargin
+ params.rightMargin;
}
@Override
public int getDecoratedMeasurementInOther(View view) {
//返回view在竖直方向上所占位置的大小(包括view的上下外边距)
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedMeasuredHeight(view) + params.topMargin
+ params.bottomMargin;
}
@Override
public int getDecoratedEnd(View view) {
//返回view右边界点(包含右内边距和右外边距)在父View中的位置(以父View的(0,0)点位坐标系)
//通俗地讲:子View右边界点到父View的(0,0)点的水平间距
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedRight(view) + params.rightMargin;
}
@Override
public int getDecoratedStart(View view) {
//返回view左边界点(包含左内边距和左外边距)在父View中的位置(以父View的(0,0)点位坐标系)
//通俗地讲:子View左边界点到父View的(0,0)点的水平间距
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams)
view.getLayoutParams();
return mLayoutManager.getDecoratedLeft(view) - params.leftMargin;
}
@Override
public int getTransformedEndWithDecoration(View view) {
//返回view水平方向的结束位置(包含右侧的装饰,如你自定义了分割线(ItemDecoration),这个是带分割线宽度的结束位置),相对父容器
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
return mTmpRect.right;
}
@Override
public int getTransformedStartWithDecoration(View view) {
//返回view水平方向的开始位置(包含左侧的装饰,如你自定义了分割线(ItemDecoration),这个是减去分割线宽度的开始位置),相对父容器
mLayoutManager.getTransformedBoundingBox(view, true, mTmpRect);
return mTmpRect.left;
}
@Override
public int getTotalSpace() {
//返回Recycleview水平内容区空间大小(宽度,除去左右内边距)
return mLayoutManager.getWidth() - mLayoutManager.getPaddingLeft()
- mLayoutManager.getPaddingRight();
}
@Override
public void offsetChild(View view, int offset) {
//相当于layout效果(在layout基础上偏移offset像素)
view.offsetLeftAndRight(offset);
}
@Override
public int getEndPadding() {
//返回Recycleview右侧内边距大小
return mLayoutManager.getPaddingRight();
}
@Override
public int getMode() {
//获取Recycleview宽度测量模式
return mLayoutManager.getWidthMode();
}
@Override
public int getModeInOther() {
//获取Recycleview高度测量模式
return mLayoutManager.getHeightMode();
}
};
}
这即是createHorizontalHelper方法中创建的OrientationHelper帮助类,它主要用于对水平LayoutManager提供帮助。具体方法的返回值含义,在代码中已经进行了详细的注解,这里就不再一一解释了。除此之外,还有针对竖直LayoutManager而提供的OrientationHelper工具类,由createVerticalHelper方法提供。它的返回值含义和createHorizontalHelper方法中各返回值含义类似,大家可以一一对应,这里就不过多赘述。
Ps:上面提到了ItemDecoration,这个类是为RecycleView的Item提供装饰的类。用它添加Item的装饰,我们需要实现它里面的两个重要方法,分别是:getItemOffsets(设置View在左,上、右和下方的留白空间大小)和onDraw(绘制留白空间)。特别注意:getItemOffsets方法中的outRect很有意思,如下:
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
//outRect设置view在几个方向上的留白大小。类似如下对应关系
//outRect.left marginLeft
//outRect.top marginTop
//outRect.right marginRight
//outRect.bottom marginBottom
//但实际上并不是设置margin,只是在在这几个方位上留白,方便用户绘制view间的装饰
}