package com.mcxtzhang.cstnorecyclelistview; import android.support.v7.widget.RecyclerView; import android.util.Log; import android.view.View; import android.view.ViewGroup; /** * 介绍:换一种思路实现LM:一个普通的竖直列表(ListView) * 支持不同大小的View * 显示底端更多的,就从上往下排列, * 显示头部更多的,就从下往上排. * 作者:zhangxutong * 邮箱:zhangxutong@imcoming.com * 时间: 2016/10/20. */ public class ZxtCstLM2 extends RecyclerView.LayoutManager { private final static int LAYOUT_FROM_TOP = 1;//从top->bottom布局子child private final static int LAYOUT_FROM_BOTTOM = 2;//从bottom->top布局子child private int mLayoutDirection;//layout child的方向 private int mFirstVisiblePosition;//记录第一个可见的postion 不太好算啊 private int mLastVisiblePosition; //记录最后一个可见的position private int mVerticalScrollOffset = 0;//竖直滑动了多少距离 @Override public RecyclerView.LayoutParams generateDefaultLayoutParams() { return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); } @Override public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) { if (getItemCount() == 0) { detachAndScrapAttachedViews(recycler); return; } if (getChildCount() == 0 && state.isPreLayout()) { return; } detachAndScrapAttachedViews(recycler); //初始化postion和布局方向 mFirstVisiblePosition = 0; mLastVisiblePosition = getItemCount() - 1; /* int leftOffset = getPaddingLeft(); int topOffset = getPaddingTop();*/ mLayoutDirection = LAYOUT_FROM_TOP; //布局子View fill(recycler, state); } /** * 核心布局子View函数 * * @param recycler * @param state */ private void fill(RecyclerView.Recycler recycler, RecyclerView.State state) { //布局 offset初始化 int topOffset = getPaddingTop(); int leftOffset = getPaddingLeft(); int bottomOffset = getBottomBorder();// //没有变化的View的下标 - View //SparseArray<View> unChangedIndexView = new SparseArray<View>(); if (getChildCount() > 0) { //有子View topOffset终于找到了 感动到哭,遍历一遍找到基准的Top Bottom if (mLayoutDirection == LAYOUT_FROM_TOP) { View topView = getChildAt(0); topOffset = getDecoratedTop(topView); //leftOffset = getDecoratedLeft(topView); } else { View bottomView = getChildAt(getChildCount() - 1); bottomOffset = getDecoratedBottom(bottomView); //leftOffset = getDecoratedLeft(bottomView); } //先将屏幕上不显示的移除掉,并且累加offset int beforeRemoveFirstPosition = mFirstVisiblePosition; for (int i = getChildCount() - 1; i >= 0; i--) { //step1 recycle回收越界的View View child = getChildAt(i); //View下边超出上界 回收,isSHowDown true if (getDecoratedBottom(child) < getPaddingTop()) { removeAndRecycleView(child, recycler); mFirstVisiblePosition++; topOffset += getDecoratedMeasuredHeight(child); continue; } //View上边超过下界 回收,isshowDOwn false if (getDecoratedTop(child) > getBottomBorder()) { removeAndRecycleView(child, recycler); mLastVisiblePosition--; bottomOffset -= getDecoratedMeasuredHeight(child); continue; } //step2 将没越界的View不做任何处理 //unChangedIndexView.put(beforeRemoveFirstPosition + i, child); } //recycle掉不可见的后,detach所有可见的view detachAndScrapAttachedViews(recycler); } //理论上不会 不过还是做个边界修正 if (mFirstVisiblePosition < 0) { mFirstVisiblePosition = 0; } if (mLastVisiblePosition > getItemCount() - 1) { mLastVisiblePosition = getItemCount() - 1; } if (mLayoutDirection == LAYOUT_FROM_TOP) {//从上往下显示 mLastVisiblePosition = getItemCount() - 1; for (int i = mFirstVisiblePosition; i <= mLastVisiblePosition; i++) { View child = recycler.getViewForPosition(i); addView(child); measureChildWithMargins(child, 0, 0); layoutDecoratedWithMargins(child, leftOffset, topOffset, leftOffset + getDecoratedMeasuredWidth(child), topOffset + getDecoratedMeasuredHeight(child)); //} topOffset += getDecoratedMeasuredHeight(child); if (/*getDecoratedBottom(child)*/topOffset > (getBottomBorder())) { //下越界 不再add mLastVisiblePosition = i; } } } else {//从下往上显示 mFirstVisiblePosition = 0;//从下往上看,上面最小应该是0 for (int i = mLastVisiblePosition; i >= mFirstVisiblePosition; i--) { View child = recycler.getViewForPosition(i); addView(child, 0); measureChildWithMargins(child, 0, 0); layoutDecoratedWithMargins(child, leftOffset, bottomOffset - getDecoratedMeasuredHeight(child), leftOffset + getDecoratedMeasuredWidth(child), bottomOffset); //} bottomOffset -= getDecoratedMeasuredHeight(child); if (bottomOffset < getPaddingTop()) {//达到上届 不再add mFirstVisiblePosition = i; } } } Log.d("TAG", "count= [" + getChildCount() + "]" + ",[recycler.getScrapList().size():" + recycler.getScrapList().size()); } @Override public boolean canScrollVertically() { return true; } @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { //位移0、没有子View 当然不移动 if (dy == 0 || getChildCount() == 0) { return 0; } int offset = dy; //边界处理 和滑动距离处理 if (offset > 0) { View bottomView = getChildClosestToEnd(); //最后一个子View隐藏起来的距离,一般情况下它的取值是,0~bottomView的Height,但是当滑动到最后一个ItemCount的时候,它才可能为负值 int bottomViewHeightHind = (getDecoratedBottom(bottomView) - getBottomBorder()); /* if (bottomViewHeightHind == 0) { //offset 随意吧 Log.w("TAG", "不设置限制:bottomViewHeightHind == 0]"); } else if (bottomViewHeightHind > 0) {// if (offset > bottomViewHeightHind) {//下边界 bottom , 拉的太大 要留白了 Log.d("TAG", "scrollVerticallyBy() called with: offset = [" + offset + "], getDecoratedBottom(bottomView()) = [" + getDecoratedBottom(bottomView) + "], getBottomBorder() = [" + getBottomBorder() + "]"); //offset = bottomViewHeightHind;//这里还是正值 } } else {//反常了 你赶紧给我修正! Log.e("TAG", "//反常了,只有最后一个VIew越界才会出现 你赶紧给我修正!],下面的代码修正"); }*/ //这里很重要,获取bottomView的postion int bottomPosition = getPosition(bottomView); //如果是最后一个子View 而且没有隐藏的距离了 if (bottomPosition == getItemCount() - 1 && bottomViewHeightHind <= 0) { offset = bottomViewHeightHind;//滑动的太嗨了,需要回弹 } } else {//,offset 负 if (mVerticalScrollOffset + offset < 0) {//处理上边界 top offset = -mVerticalScrollOffset;//这里还是负值 } } //平移childView,传入负值向上平移View,正值向下平移View offsetChildrenVertical(-offset); mVerticalScrollOffset += offset; //根据位移方向 设置布局子View方向 if (offset > 0) { mLayoutDirection = LAYOUT_FROM_TOP; } else if (offset < 0) { mLayoutDirection = LAYOUT_FROM_BOTTOM; } fill(recycler, state); return offset;// 边界效果和fling } /** * 模仿源码的 * * @return */ private View getChildClosestToStart() { return getChildAt(false ? getChildCount() - 1 : 0); } private View getChildClosestToEnd() { return getChildAt(false ? 0 : getChildCount() - 1); //return findViewByPosition(mLastVisiblePosition); } public int getVerticalSpace() { return getHeight() - getPaddingTop() - getPaddingBottom(); } public int getHorizontalSpace() { return getWidth() - getPaddingLeft() - getPaddingRight(); } /** * 返回下边界 * * @return */ public int getBottomBorder() { return getHeight() - getPaddingBottom(); } }