CustomRecycleView
/**
* 自定义recycleview,主要用于实现布局,分割线
*/
public class CustomRecycleView extends RecyclerView {
//流式布局LayoutManager
private FlowLayoutManager mFlowLayoutManager;
public CustomRecycleView(Context context) {
this(context, null);
}
public CustomRecycleView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public CustomRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, @Nullable AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomRecycleView);
int layoutManagerType = a.getInteger(R.styleable.CustomRecycleView_layoutManagerType, 0);
int spanCount = a.getInteger(R.styleable.CustomRecycleView_spanCount, 3);
boolean canScroll = a.getBoolean(R.styleable.CustomRecycleView_canScroll, true);
boolean divider = a.getBoolean(R.styleable.CustomRecycleView_useDivider, false);
int dividerColor = a.getColor(R.styleable.CustomRecycleView_dividerColor, -1);
int dividerHeight = (int) (a.getDimension(R.styleable.CustomRecycleView_dividerHeight, 3));
int dividerResId = a.getResourceId(R.styleable.CustomRecycleView_dividerResId, -1);
//int dividerMarginStart = (int)(a.getDimension(R.styleable.CustomRecycleView_dividerResId,0));
//int dividerMarginEnd = (int)(a.getDimension(R.styleable.CustomRecycleView_dividerResId,0));
DividerItemDecoration.DividerDec dividerDec = new DividerItemDecoration.DividerDec(
a.getBoolean(R.styleable.CustomRecycleView_useStartDivider, false),
a.getBoolean(R.styleable.CustomRecycleView_useTopDivider, false),
a.getBoolean(R.styleable.CustomRecycleView_useEndDivider, false),
a.getBoolean(R.styleable.CustomRecycleView_useBottomDivider, false)
);
if (layoutManagerType == 3) {
mFlowLayoutManager = new FlowLayoutManager();
}
initLayoutManager(layoutManagerType, canScroll, spanCount);
initDivider(divider, layoutManagerType, dividerColor, dividerHeight, dividerResId, dividerDec);
a.recycle();
}
private void initLayoutManager(int layoutManagerType, boolean canScroll, int spanCount) {
setNestedScrollingEnabled(canScroll);
switch (layoutManagerType) {
case 0:
setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false) {
@Override
public boolean canScrollVertically() {
return canScroll && super.canScrollVertically();
}
@Override
public void onLayoutChildren(Recycler recycler, State state) {
//解决java.lang.IndexOutOfBoundsException: Inconsistency detected.的bug
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
});
break;
case 1:
setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false) {
@Override
public boolean canScrollHorizontally() {
return canScroll && super.canScrollHorizontally();
}
@Override
public void onLayoutChildren(Recycler recycler, State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView,
State state, final int position) {
LinearSmoothScroller smoothScroller =
new LinearSmoothScroller(recyclerView.getContext()) {
// 返回:滑过1px时经历的时间(ms)。
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return 150f / displayMetrics.densityDpi;
}
};
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
});
break;
case 2:
setLayoutManager(new GridLayoutManager(getContext(), spanCount) {
@Override
public void onLayoutChildren(Recycler recycler, State state) {
try {
super.onLayoutChildren(recycler, state);
} catch (IndexOutOfBoundsException e) {
e.printStackTrace();
}
}
});
break;
case 3:
setLayoutManager(mFlowLayoutManager);
break;
case 4:
//不设置任何适配器,留给外部自行设置
break;
}
}
/***
* 在setLayoutManager后面调用
* @param divider 是否需要分割线
* @param layoutManagerType 分割线方向
*/
private void initDivider(boolean divider, int layoutManagerType, int dividerColor, int dividerHeight, int dividerResId, DividerItemDecoration.DividerDec dividerDec) {
if (!divider) {
return;
}
DividerItemDecoration dividerItemDecoration = null;
switch (layoutManagerType) {
case 0:
dividerItemDecoration = getDividerItemDecoration(DividerItemDecoration.HORIZONTAL_LIST, dividerColor, dividerHeight, dividerResId, dividerDec);
break;
case 1:
dividerItemDecoration = getDividerItemDecoration(DividerItemDecoration.VERTICAL_LIST, dividerColor, dividerHeight, dividerResId, dividerDec);
break;
case 2:
case 3:
dividerItemDecoration = getDividerItemDecoration(DividerItemDecoration.BOTH_SET, dividerColor, dividerHeight, dividerResId, dividerDec);
break;
default:
break;
}
if (dividerItemDecoration != null) {
addItemDecoration(dividerItemDecoration);
}
}
private DividerItemDecoration getDividerItemDecoration(int orientation, int dividerColor, int dividerHeight, int dividerResId, DividerItemDecoration.DividerDec dividerDec) {
DividerItemDecoration dividerItemDecoration;
if (dividerResId > 0) {
dividerItemDecoration = new DividerItemDecoration(getContext(), orientation, dividerResId, dividerDec);
} else if (dividerHeight > 0) {
dividerItemDecoration = new DividerItemDecoration(getContext(), orientation, dividerHeight, dividerColor, dividerDec);
} else {
dividerItemDecoration = new DividerItemDecoration(getContext(), orientation, dividerDec);
}
return dividerItemDecoration;
}
/***
* 水平平滑滚动
*/
public void moveToPosition(int position) {
if (!(getLayoutManager() instanceof LinearLayoutManager)) {
return;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
if (position > -1 && getAdapter() != null && position < getAdapter().getItemCount()) {
scrollToPosition(position);
layoutManager.scrollToPositionWithOffset(position, 0);
}
}
/***
* 还有问题
* 水平平滑滚动
* @param targetPos 目标位置
*/
public boolean moveToPositionH(int targetPos) {
boolean move = false;
if (!(getLayoutManager() instanceof LinearLayoutManager)) {
return move;
}
LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
//先从RecyclerView的LayoutManager中获取第一项和最后一项的Position
int firstItem = layoutManager.findFirstVisibleItemPosition();
int lastItem = layoutManager.findLastVisibleItemPosition();
//然后区分情况
if (targetPos <= firstItem) {
//当要置顶的项在当前显示的第一个项的前面时
smoothScrollToPosition(targetPos);
} else if (targetPos <= lastItem) {
//当要置顶的项已经在屏幕上显示时
int left = getChildAt(targetPos - firstItem).getLeft();
smoothScrollBy(left, 0);
} else {
//当要置顶的项在当前显示的最后一项的后面时
smoothScrollToPosition(targetPos);
//这里这个变量是用在RecyclerView滚动监听里面的
move = true;
}
return move;
}
}
自定义属性
<declare-styleable name="CustomRecycleView">
<attr name="layoutManagerType" format="enum">
<enum name="linerV" value="0" />
<enum name="linerH" value="1" />
<enum name="grid" value="2" />
<enum name="flow" value="3" />
<enum name="empty" value="4" />
</attr>
<attr name="spanCount" format="integer" />
<attr name="canScroll" format="boolean" />
<attr name="useDivider" format="boolean" />
<attr name="dividerHeight" format="dimension|reference" />
<attr name="dividerColor" format="color" />
<attr name="dividerResId" format="reference" />
<attr name="dividerMarginStart" format="dimension|reference" />
<attr name="dividerMarginEnd" format="dimension|reference" />
<attr name="useTopDivider" format="boolean" />
<attr name="useStartDivider" format="boolean" />
<attr name="useEndDivider" format="boolean" />
<attr name="useBottomDivider" format="boolean" />
</declare-styleable>
DividerItemDecoration :分割线
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private Paint mPaint;
//取名mDivider似乎更恰当
private Drawable mDrawable;
//分割线高度,默认为1px
private int mDividerHeight = 2;
//列表的方向
private int mOrientation;
//系统自带的参数
private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
//水平
public static final int HORIZONTAL_LIST = RecyclerView.HORIZONTAL;
//垂直
public static final int VERTICAL_LIST = RecyclerView.VERTICAL;
//水平+垂直
public static final int BOTH_SET = 2;
private DividerDec mUseDivDec;
public static class DividerDec{
public boolean useTopDiv = false;
public boolean useStartDiv = false;
public boolean useEndDiv = false;
public boolean useBottomDiv = false;
public DividerDec(boolean useStartDiv, boolean useTopDiv, boolean useEndDiv, boolean useBottomDiv) {
this.useStartDiv = useStartDiv;
this.useTopDiv = useTopDiv;
this.useEndDiv = useEndDiv;
this.useBottomDiv = useBottomDiv;
}
}
/**
* 默认分割线:高度为2px,颜色为灰色
*
* @param context 上下文
* @param orientation 列表方向
*/
public DividerItemDecoration(Context context, int orientation, DividerDec dividerDec) {
this.setOrientation(orientation);
//获取xml配置的参数
final TypedArray a = context.obtainStyledAttributes(ATTRS);
//typedArray.getDrawable(attr)这句是说我们可以通过我们的资源获得资源,使用我们的资源名attr去获得资源id
//看不懂就用自己写一个分割线的图片吧,方法:ContextCompat.getDrawable(context, drawableId);
mDrawable = a.getDrawable(0);
//官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
//在TypedArray后调用recycle主要是为了缓存。
mUseDivDec = dividerDec;
a.recycle();
}
/**
* 自定义分割线
*
* @param context 上下文
* @param orientation 列表方向
* @param drawableId 分割线图片
*/
public DividerItemDecoration(Context context, int orientation, int drawableId, DividerDec dividerDec) {
this.setOrientation(orientation);
//旧的getDrawable方法弃用了,这个是新的
mDrawable = ContextCompat.getDrawable(context, drawableId);
mDividerHeight = mDrawable.getIntrinsicHeight();
mUseDivDec = dividerDec;
}
/**
* 自定义分割线
*
* @param context 上下文
* @param orientation 列表方向
* @param dividerHeight 分割线高度
* @param dividerColor 分割线颜色
*/
public DividerItemDecoration(Context context, int orientation,
int dividerHeight, int dividerColor, DividerDec dividerDec) {
this.setOrientation(orientation);
mDividerHeight = dividerHeight;
//抗锯齿画笔
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(dividerColor);
//填满颜色
mPaint.setStyle(Paint.Style.FILL);
mUseDivDec = dividerDec;
}
/**
* 设置方向
*
* @param orientation
*/
public void setOrientation(int orientation) {
if (orientation < 0 || orientation > 2)
throw new IllegalArgumentException("invalid orientation");
mOrientation = orientation;
}
/**
* 绘制分割线之后,需要留出一个外边框,就是说item之间的间距要换一下
*
* @param outRect outRect.set(0, 0, 0, 0);的四个参数理解成margin就好了
* @param view 视图
* @param parent 父级view
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
//下面super...代码其实调用的就是那个过时的getItemOffsets,也就是说这个方法体内容也可以通通移到那个过时的getItemOffsets中
super.getItemOffsets(outRect, view, parent, state);
//获取layoutParams参数
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
//当前位置
int itemPosition = layoutParams.getViewLayoutPosition();
//ItemView数量
int childCount = parent.getAdapter().getItemCount();
switch (mOrientation) {
case BOTH_SET:
//获取Layout的相关参数
int spanCount = this.getSpanCount(parent);
if (isLastRaw(parent, itemPosition, spanCount, childCount)) {
// 如果是最后一行,则不需要绘制底部
outRect.set(0, 0, mDividerHeight, mUseDivDec.useBottomDiv ? mDividerHeight : 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount)) {
// 如果是最后一列,则不需要绘制右边
outRect.set(0, 0, mUseDivDec.useEndDiv ? mDividerHeight : 0, mDividerHeight);
} else {
if (itemPosition % spanCount == 0) {
outRect.set(mUseDivDec.useStartDiv ? mDividerHeight : 0, 0, mDividerHeight / 2, mDividerHeight);
} else {
outRect.set(mDividerHeight / 2, 0, mDividerHeight / 2, mDividerHeight);
}
}
break;
case VERTICAL_LIST:
childCount -= 1;
//水平布局右侧留Margin,如果是最后一列,就不要留Margin了
if(itemPosition == 0 && childCount == 0){
//只有一列
outRect.set(mUseDivDec.useStartDiv ? mDividerHeight : 0,
0, mUseDivDec.useBottomDiv ? mDividerHeight :0,0);
}else if(itemPosition == 0){
//第一列
outRect.set(mUseDivDec.useStartDiv ? mDividerHeight : 0,
0, mDividerHeight /2, 0);
}else if(itemPosition == childCount ){
//最后一列
outRect.set(mDividerHeight/2,
0, mUseDivDec.useEndDiv ? mDividerHeight : 0, 0);
}else{
outRect.set(mDividerHeight/2,
0, mDividerHeight/2, 0);
}
break;
case HORIZONTAL_LIST:
childCount -= 1;
//垂直布局底部留边,最后一行不留
if(itemPosition == 0 && childCount == 0){
//只有一行
outRect.set( 0,
mUseDivDec.useTopDiv ? mDividerHeight : 0, 0, ( mUseDivDec.useBottomDiv ? mDividerHeight :0));
}else if(itemPosition == 0){
//第一行
outRect.set( 0,
mUseDivDec.useTopDiv ? mDividerHeight : 0, 0, mDividerHeight/2);
}else if(itemPosition == childCount ){
//最后一行
outRect.set(0,
mDividerHeight/2, 0, mUseDivDec.useBottomDiv ? mDividerHeight : 0);
}else{
outRect.set(0,
mDividerHeight/2, 0, mDividerHeight/2);
}
break;
default:
break;
}
}
/**
* 绘制分割线
*
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else if (mOrientation == HORIZONTAL_LIST) {
drawHorizontal(c, parent);
} else {
drawHorizontal(c, parent);
drawVertical(c, parent);
}
}
/**
* 绘制横向 item 分割线
*
* @param canvas 画布
* @param parent 父容器
*/
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
final int x = parent.getPaddingLeft();
final int width = parent.getMeasuredWidth() - parent.getPaddingRight();
//getChildCount()(ViewGroup.getChildCount) 返回的是显示层面上的“所包含的子 View 个数”。
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams =
(RecyclerView.LayoutParams) child.getLayoutParams();
//item底部的Y轴坐标+margin值
final int y = child.getBottom() + layoutParams.bottomMargin;
final int height = y + mDividerHeight;
if (mDrawable != null) {
//setBounds(x,y,width,height); x:组件在容器X轴上的起点 y:组件在容器Y轴上的起点
// width:组件的长度 height:组件的高度
mDrawable.setBounds(x, y, width, height);
mDrawable.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(x, y, width, height, mPaint);
}
}
}
/**
* 绘制纵向 item 分割线
*
* @param canvas
* @param parent
*/
private void drawVertical(Canvas canvas, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
final int childSize = parent.getChildCount();
for (int i = 0; i < childSize; i++) {
final View child = parent.getChildAt(i);
RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
final int left = child.getRight() + layoutParams.rightMargin;
final int right = left + mDividerHeight;
if (mDrawable != null) {
mDrawable.setBounds(left, top, right, bottom);
mDrawable.draw(canvas);
}
if (mPaint != null) {
canvas.drawRect(left, top, right, bottom, mPaint);
}
}
}
/**
* 获取列数
*
* @param parent
* @return
*/
private int getSpanCount(RecyclerView parent) {
int spanCount = -1;
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount) {
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
int orientation = ((GridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
// 如果是最后一列,则不需要绘制右边
if ((pos + 1) % spanCount == 0)
return true;
} else {
childCount = childCount - childCount % spanCount;
// 如果是最后一列,则不需要绘制右边
if (pos >= childCount)
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
// 如果是最后一列,则不需要绘制右边
if ((pos + 1) % spanCount == 0)
return true;
} else {
childCount = childCount - childCount % spanCount;
// 如果是最后一列,则不需要绘制右边
if (pos >= childCount)
return true;
}
}
return false;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount) {
int orientation;
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
childCount = childCount - childCount % spanCount;
orientation = ((GridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
// 如果是最后一行,则不需要绘制底部
childCount = childCount - childCount % spanCount;
if (pos >= childCount)
return true;
} else {// StaggeredGridLayoutManager 横向滚动
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0)
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager) {
orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL) {
// 如果是最后一行,则不需要绘制底部
childCount = childCount - childCount % spanCount;
if (pos >= childCount)
return true;
} else {// StaggeredGridLayoutManager 横向滚动
// 如果是最后一行,则不需要绘制底部
if ((pos + 1) % spanCount == 0)
return true;
}
}
return false;
}
}
FlowLayoutManager :流式布局管理器
public class FlowLayoutManager extends RecyclerView.LayoutManager {
private static final String TAG = FlowLayoutManager.class.getSimpleName();
final FlowLayoutManager self = this;
protected int width, height;
private int left, top, right;
//最大容器的宽度
private int usedMaxWidth;
//竖直方向上的偏移量
private int verticalScrollOffset = 0;
public int getTotalHeight() {
return totalHeight;
}
//计算显示的内容的高度
protected int totalHeight = 0;
private Row row = new Row();
private List<Row> lineRows = new ArrayList<>();
//保存所有的Item的上下左右的偏移量信息
private SparseArray<Rect> allItemFrames = new SparseArray<>();
public FlowLayoutManager() {
}
//设置主动测量规则,适应recyclerView高度为wrap_content
@Override
public boolean isAutoMeasureEnabled() {
return true;
}
public int getRowCounts() {
return lineRows.size();
}
//每个item的定义
public class Item {
int useHeight;
View view;
public void setRect(Rect rect) {
this.rect = rect;
}
Rect rect;
public Item(int useHeight, View view, Rect rect) {
this.useHeight = useHeight;
this.view = view;
this.rect = rect;
}
}
//行信息的定义
public class Row {
public void setCuTop(float cuTop) {
this.cuTop = cuTop;
}
public void setMaxHeight(float maxHeight) {
this.maxHeight = maxHeight;
}
//每一行的头部坐标
float cuTop;
//每一行需要占据的最大高度
float maxHeight;
//每一行存储的item
List<Item> views = new ArrayList<>();
public void addViews(Item view) {
views.add(view);
}
}
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
}
//该方法主要用来获取每一个item在屏幕上占据的位置
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
Log.d(TAG, "onLayoutChildren");
totalHeight = 0;
int cuLineTop = top;
//当前行使用的宽度
int cuLineWidth = 0;
int itemLeft;
int itemTop;
int maxHeightItem = 0;
row = new Row();
lineRows.clear();
allItemFrames.clear();
removeAllViews();
if (getItemCount() == 0) {
detachAndScrapAttachedViews(recycler);
verticalScrollOffset = 0;
return;
}
if (getChildCount() == 0 && state.isPreLayout()) {
return;
}
//onLayoutChildren方法在RecyclerView 初始化时 会执行两遍
detachAndScrapAttachedViews(recycler);
if (getChildCount() == 0) {
width = getWidth();
height = getHeight();
left = getPaddingLeft();
right = getPaddingRight();
top = getPaddingTop();
usedMaxWidth = width - left - right;
}
for (int i = 0; i < getItemCount(); i++) {
Log.d(TAG, "index:" + i);
View childAt = recycler.getViewForPosition(i);
if (View.GONE == childAt.getVisibility()) {
continue;
}
measureChildWithMargins(childAt, 0, 0);
int childWidth = getDecoratedMeasuredWidth(childAt);
int childHeight = getDecoratedMeasuredHeight(childAt);
int childUseWidth = childWidth;
int childUseHeight = childHeight;
//如果加上当前的item还小于最大的宽度的话
if (cuLineWidth + childUseWidth <= usedMaxWidth) {
itemLeft = left + cuLineWidth;
itemTop = cuLineTop;
Rect frame = allItemFrames.get(i);
if (frame == null) {
frame = new Rect();
}
frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
allItemFrames.put(i, frame);
cuLineWidth += childUseWidth;
maxHeightItem = Math.max(maxHeightItem, childUseHeight);
row.addViews(new Item(childUseHeight, childAt, frame));
row.setCuTop(cuLineTop);
row.setMaxHeight(maxHeightItem);
} else {
//换行
formatAboveRow();
cuLineTop += maxHeightItem;
totalHeight += maxHeightItem;
itemTop = cuLineTop;
itemLeft = left;
Rect frame = allItemFrames.get(i);
if (frame == null) {
frame = new Rect();
}
frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
allItemFrames.put(i, frame);
cuLineWidth = childUseWidth;
maxHeightItem = childUseHeight;
row.addViews(new Item(childUseHeight, childAt, frame));
row.setCuTop(cuLineTop);
row.setMaxHeight(maxHeightItem);
}
//不要忘了最后一行进行刷新下布局
if (i == getItemCount() - 1) {
formatAboveRow();
totalHeight += maxHeightItem;
}
}
totalHeight = Math.max(totalHeight, getVerticalSpace());
Log.d(TAG, "onLayoutChildren totalHeight:" + totalHeight);
fillLayout(recycler, state);
}
//对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中
private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout() || getItemCount() == 0) { // 跳过preLayout,preLayout主要用于支持动画
return;
}
// 当前scroll offset状态下的显示区域
Rect displayFrame = new Rect(getPaddingLeft(), getPaddingTop() + verticalScrollOffset,
getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom()));
//对所有的行信息进行遍历
for (int j = 0; j < lineRows.size(); j++) {
Row row = lineRows.get(j);
float lineTop = row.cuTop;
float lineBottom = lineTop + row.maxHeight;
//如果该行在屏幕中,进行放置item
// if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {
List<Item> views = row.views;
for (int i = 0; i < views.size(); i++) {
View scrap = views.get(i).view;
measureChildWithMargins(scrap, 0, 0);
addView(scrap);
Rect frame = views.get(i).rect;
//将这个item布局出来
layoutDecoratedWithMargins(scrap,
frame.left,
frame.top - verticalScrollOffset,
frame.right,
frame.bottom - verticalScrollOffset);
}
// } else {
// //将不在屏幕中的item放到缓存中
// List<Item> views = row.views;
// for (int i = 0; i < views.size(); i++) {
// View scrap = views.get(i).view;
// removeAndRecycleView(scrap, recycler);
// }
// }
}
}
/**
* 计算每一行没有居中的viewgroup,让居中显示
*/
private void formatAboveRow() {
List<Item> views = row.views;
for (int i = 0; i < views.size(); i++) {
Item item = views.get(i);
View view = item.view;
int position = getPosition(view);
//如果该item的位置不在该行中间位置的话,进行重新放置
if (allItemFrames.get(position).top < row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2) {
Rect frame = allItemFrames.get(position);
if (frame == null) {
frame = new Rect();
}
frame.set(allItemFrames.get(position).left, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2),
allItemFrames.get(position).right, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2 + getDecoratedMeasuredHeight(view)));
allItemFrames.put(position, frame);
item.setRect(frame);
views.set(i, item);
}
}
row.views = views;
lineRows.add(row);
row = new Row();
}
/**
* 竖直方向需要滑动的条件
*
* @return
*/
@Override
public boolean canScrollVertically() {
return true;
}
//监听竖直方向滑动的偏移量
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
RecyclerView.State state) {
Log.d("TAG", "totalHeight:" + totalHeight);
//实际要滑动的距离
int travel = dy;
//如果滑动到最顶部
if (verticalScrollOffset + dy < 0) {//限制滑动到顶部之后,不让继续向上滑动了
travel = -verticalScrollOffset;//verticalScrollOffset=0
} else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部
travel = totalHeight - getVerticalSpace() - verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()
}
//将竖直方向的偏移量+travel
verticalScrollOffset += travel;
// 平移容器内的item
offsetChildrenVertical(-travel);
fillLayout(recycler, state);
return travel;
}
private int getVerticalSpace() {
return self.getHeight() - self.getPaddingBottom() - self.getPaddingTop();
}
public int getHorizontalSpace() {
return self.getWidth() - self.getPaddingLeft() - self.getPaddingRight();
}
}
使用LinearLayoutManager
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".rv_custom.CustomRecycleViewActivity">
<com.zly.mvvmhabit_demo.widget.CustomRecycleView
android:id="@+id/crv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:dividerColor="#eeeeee"
app:dividerHeight="0.5dp"
app:layoutManagerType="linerV"
app:useDivider="true" />
</LinearLayout>
public class CustomRecycleViewActivity extends AppCompatActivity {
private CustomRecycleView mCustomRecycleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_recyclerview);
mCustomRecycleView = (CustomRecycleView) findViewById(R.id.crv);
List<String> datas = new ArrayList<>();
for (int i = 0; i < 50; i++) {
datas.add("赵丽颖" + i);
}
mCustomRecycleView.setAdapter(new CommomRvAdapter<String>(this, datas, R.layout.item_common_rv) {
@Override
protected void fillData(CommomRvViewHolder holder, int position, String s) {
holder.setText(R.id.tv, s);
}
});
}
}
使用FlowLayoutManager
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".rv_custom.CustomRvFlowLayoutActivity">
<com.zly.mvvmhabit_demo.widget.CustomRecycleView
android:id="@+id/crv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:paddingStart="10dp"
android:paddingEnd="10dp"
app:dividerColor="#00000000"
app:dividerHeight="10dp"
app:layoutManagerType="flow"
app:useDivider="true" />
</LinearLayout>
public class CustomRvFlowLayoutActivity extends AppCompatActivity {
private CustomRecycleView mCustomRecycleView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_custom_rv_flow);
mCustomRecycleView = (CustomRecycleView) findViewById(R.id.crv);
List<String> datas = new ArrayList<>();
for (int i = 0; i < 20; i++) {
datas.add("赵丽颖" + i);
}
mCustomRecycleView.setAdapter(new CommomRvAdapter<String>(this, datas, R.layout.item_common_rv_flow) {
@Override
protected void fillData(CommomRvViewHolder holder, int position, String s) {
holder.setText(R.id.tv_lable, s);
}
});
}
}