package mcxtzhang.recyclerviewdemo;
import android.graphics.Rect;
import android.support.v7.widget.RecyclerView;
import android.util.Log;
import android.util.SparseArray;
import android.util.SparseBooleanArray;
import android.view.View;
import android.view.ViewGroup;
/**
* 介绍:自定义LayoutManager
* 作者:zhangxutong
* 邮箱:mcxtzhang@163.com
* CSDN:http://blog.csdn.net/zxt0601
* 时间: 16/09/28.
*/
public class CstLinearLayoutManager extends RecyclerView.LayoutManager {
//保存所有的Item的上下左右的偏移量信息
private SparseArray<Rect> allItemFrames = new SparseArray<>();
//记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收
private SparseBooleanArray hasAttachedItems = new SparseBooleanArray();
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams() {
return new RecyclerView.LayoutParams(RecyclerView.LayoutParams.WRAP_CONTENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
//如果没有item,直接返回
if (getItemCount() <= 0) {//参考源码
removeAndRecycleAllViews(recycler);
return;
}
// 跳过preLayout,preLayout主要用于支持动画
if (state.isPreLayout()) {
return;
}
detachAndScrapAttachedViews(recycler);
//定义竖直方向的偏移量
int offsetY = 0;
totalHeight = 0;
for (int i = 0; i < getItemCount(); i++) {
View child = recycler.getViewForPosition(i);
addView(child);
measureChildWithMargins(child, 0, 0);
//把宽高拿到,宽高都是包含ItemDecorate的尺寸
int width = getDecoratedMeasuredWidth(child);
int height = getDecoratedMeasuredHeight(child);
RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams) child.getLayoutParams();
//最后,将View布局
/* layoutDecorated(child, getPaddingLeft() + lp.leftMargin, getPaddingTop() + lp.topMargin + offsetY,
getPaddingLeft() + lp.leftMargin + width + lp.rightMargin, getPaddingTop() + lp.topMargin + offsetY + height + lp.bottomMargin);*/
Rect frame = allItemFrames.get(i);
if (frame == null) {
frame = new Rect();
}
frame.set(getPaddingLeft() + lp.leftMargin, getPaddingTop() + lp.topMargin + offsetY,
getPaddingLeft() + lp.leftMargin + width + lp.rightMargin, getPaddingTop() + lp.topMargin + offsetY + height + lp.bottomMargin);
// 将当前的Item的Rect边界数据保存
allItemFrames.put(i, frame);
// 由于已经调用了detachAndScrapAttachedViews,因此需要将当前的Item设置为未出现过
hasAttachedItems.put(i, false);
//将竖直方向偏移量增大height
offsetY += (height + lp.leftMargin + lp.bottomMargin);
}
//如果所有子View的高度和没有填满RecyclerView的高度,
// 则将高度设置为RecyclerView的高度
totalHeight = Math.max(offsetY, getVerticalSpace());
recycleAndFillItems(recycler, state);
}
/**
* 回收不需要的Item,并且将需要显示的Item从缓存中取出
*/
private void recycleAndFillItems(RecyclerView.Recycler recycler, RecyclerView.State state) {
if (state.isPreLayout()) { // 跳过preLayout,preLayout主要用于支持动画
return;
}
// 当前scroll offset状态下的显示区域
Rect displayFrame = new Rect(0, verticalScrollOffset, getHorizontalSpace(), verticalScrollOffset + getVerticalSpace());
/**
* 将滑出屏幕的Items回收到Recycle缓存中
*/
Rect childFrame = new Rect();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
childFrame.left = getDecoratedLeft(child);
childFrame.top = getDecoratedTop(child);
childFrame.right = getDecoratedRight(child);
childFrame.bottom = getDecoratedBottom(child);
//如果Item没有在显示区域,就说明需要回收
if (!Rect.intersects(displayFrame, childFrame)) {
//回收掉滑出屏幕的View
removeAndRecycleView(child, recycler);
}
}
//重新显示需要出现在屏幕的子View
for (int i = 0; i < getItemCount(); i++) {
if (Rect.intersects(displayFrame, allItemFrames.get(i))) {
View scrap = recycler.getViewForPosition(i);
measureChildWithMargins(scrap, 0, 0);
addView(scrap);
Rect frame = allItemFrames.get(i);
//将这个item布局出来
layoutDecorated(scrap,
frame.left,
frame.top - verticalScrollOffset,
frame.right,
frame.bottom - verticalScrollOffset);
}
}
}
//滑动
@Override
public boolean canScrollVertically() {
return true;
}
private int verticalScrollOffset = 0;//滑动了多少距离
private int totalHeight = 0;//view一共的高度
@Override
public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
//先detach掉所有的子View
detachAndScrapAttachedViews(recycler);
//实际要滑动的距离
int travel = dy;
//如果滑动到最顶部
if (verticalScrollOffset + dy < 0) {
travel = -verticalScrollOffset;
} else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部
travel = totalHeight - getVerticalSpace() - verticalScrollOffset;
}
//将竖直方向的偏移量+travel
verticalScrollOffset += travel;
// 平移容器内的item 这句话实现移动
offsetChildrenVertical(-travel);
recycleAndFillItems(recycler, state);
Log.d("TAG", " childView count:" + getChildCount());
return travel;//返回0 没有fling效果
}
private int getVerticalSpace() {
return getHeight() - getPaddingBottom() - getPaddingTop();
}
private int getHorizontalSpace() {
return getWidth() - getPaddingTop() - getPaddingBottom();
}
}