Android(安卓)RecyclerView LayoutManager
https://github.com/ChenLittlePing/RecyclerCoverFlow
可以在github 搜索关键字 coverflow
package com.as.demo_ok6.coverflow;import android.animation.Animator;import android.animation.ValueAnimator;import android.graphics.ColorMatrix;import android.graphics.ColorMatrixColorFilter;import android.graphics.Paint;import android.graphics.Rect;import android.support.v7.widget.RecyclerView;import android.util.SparseArray;import android.util.SparseBooleanArray;import android.view.View;import android.view.ViewGroup;import android.view.animation.DecelerateInterpolator;/** * Cover Flow布局类 * 通过重写LayoutManger布局方法{@link #onLayoutChildren(RecyclerView.Recycler, RecyclerView.State)} * 对Item进行布局,并对超出屏幕的Item进行回收 *
通过重写LayoutManger中的{@link #scrollHorizontallyBy(int, RecyclerView.Recycler, RecyclerView.State)} * 进行水平滚动处理 * * @author Chen Xiaoping (562818444@qq.com) * @version V1.0 * @Datetime 2017-04-18 */public class CoverFlowLayoutManger extends RecyclerView.LayoutManager { /** * 最大存储item信息存储数量, * 超过设置数量,则动态计算来获取 */ private final int MAX_RECT_COUNT = 100; /**滑动总偏移量*/ private int mOffsetAll = 0; /**Item宽*/ private int mDecoratedChildWidth = 0; /**Item高*/ private int mDecoratedChildHeight = 0; /**Item间隔与item宽的比例*/ private float mIntervalRatio = 0.5f; /**起始ItemX坐标*/ private int mStartX = 0; /**起始Item Y坐标*/ private int mStartY = 0; /**保存所有的Item的上下左右的偏移量信息*/ private SparseArray mAllItemFrames = new SparseArray<>(); /**记录Item是否出现过屏幕且还没有回收。true表示出现过屏幕上,并且还没被回收*/ private SparseBooleanArray mHasAttachedItems = new SparseBooleanArray(); /**RecyclerView的Item回收器*/ private RecyclerView.Recycler mRecycle; /**RecyclerView的状态器*/ private RecyclerView.State mState; /**滚动动画*/ private ValueAnimator mAnimation; /**正显示在中间的Item*/ private int mSelectPosition = 0; /**前一个正显示在中间的Item*/ private int mLastSelectPosition = 0; /**滑动的方向:左*/ private static int SCROLL_LEFT = 1; /**滑动的方向:右*/ private static int SCROLL_RIGHT = 2; /** * 选中监听 */ private OnSelected mSelectedListener; /**是否为平面滚动,Item之间没有叠加,也没有缩放*/ private boolean mIsFlatFlow = false; /**是否启动Item灰度值渐变*/ private boolean mItemGradualGrey = false; /**是否启动Item半透渐变*/ private boolean mItemGradualAlpha = false; public CoverFlowLayoutManger(boolean isFlat, boolean isGreyItem, boolean isAlphaItem, float cstInterval) { mIsFlatFlow = isFlat; mItemGradualGrey = isGreyItem; mItemGradualAlpha = isAlphaItem; if (cstInterval >= 0) { mIntervalRatio = cstInterval; } else { if (mIsFlatFlow) { mIntervalRatio = 1.1f; } } } @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) { //如果没有item,直接返回 //跳过preLayout,preLayout主要用于支持动画 if (getItemCount() <= 0 || state.isPreLayout()) { mOffsetAll = 0; return; } mAllItemFrames.clear(); mHasAttachedItems.clear(); //得到子view的宽和高,这边的item的宽高都是一样的,所以只需要进行一次测量 View scrap = recycler.getViewForPosition(0); addView(scrap); measureChildWithMargins(scrap, 0, 0); //计算测量布局的宽高 mDecoratedChildWidth = getDecoratedMeasuredWidth(scrap); mDecoratedChildHeight = getDecoratedMeasuredHeight(scrap); mStartX = Math.round((getHorizontalSpace() - mDecoratedChildWidth) * 1.0f / 2); mStartY = Math.round((getVerticalSpace() - mDecoratedChildHeight) *1.0f / 2); float offset = mStartX; /**只存{@link MAX_RECT_COUNT}个item具体位置*/ for (int i = 0; i < getItemCount() && i < MAX_RECT_COUNT; i++) { Rect frame = mAllItemFrames.get(i); if (frame == null) { frame = new Rect(); } frame.set(Math.round(offset), mStartY, Math.round(offset + mDecoratedChildWidth), mStartY + mDecoratedChildHeight); mAllItemFrames.put(i, frame); mHasAttachedItems.put(i, false); offset = offset + getIntervalDistance(); //原始位置累加,否则越后面误差越大 } detachAndScrapAttachedViews(recycler); //在布局之前,将所有的子View先Detach掉,放入到Scrap缓存中 if ((mRecycle == null || mState == null) && //在为初始化前调用smoothScrollToPosition 或者 scrollToPosition,只会记录位置 mSelectPosition != 0) { //所以初始化时需要滚动到对应位置 mOffsetAll = calculateOffsetForPosition(mSelectPosition); onSelectedCallBack(); } layoutItems(recycler, state, SCROLL_RIGHT); mRecycle = recycler; mState = state; } @Override public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { if (mAnimation != null && mAnimation.isRunning()) mAnimation.cancel(); int travel = dx; if (dx + mOffsetAll < 0) { travel = -mOffsetAll; } else if (dx + mOffsetAll > getMaxOffset()){ travel = (int) (getMaxOffset() - mOffsetAll); } mOffsetAll += travel; //累计偏移量 layoutItems(recycler, state, dx > 0 ? SCROLL_RIGHT : SCROLL_LEFT); return travel; } /** * 布局Item * 注意:1,先清除已经超出屏幕的item *
2,再绘制可以显示在屏幕里面的item */ private void layoutItems(RecyclerView.Recycler recycler, RecyclerView.State state, int scrollDirection) { if (state.isPreLayout()) return; Rect displayFrame = new Rect(mOffsetAll, 0, mOffsetAll + getHorizontalSpace(), getVerticalSpace()); int position = 0; for (int i = 0; i < getChildCount(); i++) { View child = getChildAt(i); position = getPosition(child); Rect rect = getFrame(position); if (!Rect.intersects(displayFrame, rect)) {//Item没有在显示区域,就说明需要回收 removeAndRecycleView(child, recycler); //回收滑出屏幕的View mHasAttachedItems.delete(position); } else { //Item还在显示区域内,更新滑动后Item的位置 layoutItem(child, rect); //更新Item位置 mHasAttachedItems.put(position, true); } } if (position == 0) position = mSelectPosition; int min = position - 50 >= 0? position - 50 : 0; int max = position + 50 < getItemCount() ? position + 50 : getItemCount(); for (int i = min; i < max; i++) { Rect rect = getFrame(i); if (Rect.intersects(displayFrame, rect) && !mHasAttachedItems.get(i)) { //重新加载可见范围内的Item View scrap = recycler.getViewForPosition(i); measureChildWithMargins(scrap, 0, 0); if (scrollDirection == SCROLL_LEFT || mIsFlatFlow) { //向左滚动,新增的Item需要添加在最前面 addView(scrap, 0); } else { //向右滚动,新增的item要添加在最后面 addView(scrap); } layoutItem(scrap, rect); //将这个Item布局出来 mHasAttachedItems.put(i, true); } } } /** * 布局Item位置 * @param child 要布局的Item * @param frame 位置信息 */ private void layoutItem(View child, Rect frame) { layoutDecorated(child, frame.left - mOffsetAll, frame.top, frame.right - mOffsetAll, frame.bottom); if (!mIsFlatFlow) { //不是平面普通滚动的情况下才进行缩放 child.setScaleX(computeScale(frame.left - mOffsetAll)); //缩放 child.setScaleY(computeScale(frame.left - mOffsetAll)); //缩放 } if (mItemGradualAlpha) { child.setAlpha(computeAlpha(frame.left - mOffsetAll)); } if (mItemGradualGrey) { greyItem(child, frame); } } /** * 动态获取Item的位置信息 * @param index item位置 * @return item的Rect信息 */ private Rect getFrame(int index) { Rect frame = mAllItemFrames.get(index); if (frame == null) { frame = new Rect(); float offset = mStartX + getIntervalDistance() * index; //原始位置累加(即累计间隔距离) frame.set(Math.round(offset), mStartY, Math.round(offset + mDecoratedChildWidth), mStartY + mDecoratedChildHeight); } return frame; } /** * 变化Item的灰度值 * @param child 需要设置灰度值的Item * @param frame 位置信息 */ private void greyItem(View child, Rect frame) { float value = computeGreyScale(frame.left - mOffsetAll); ColorMatrix cm = new ColorMatrix(new float[]{ value, 0, 0, 0, 120*(1-value), 0, value, 0, 0, 120*(1-value), 0, 0, value, 0, 120*(1-value), 0, 0, 0, 1, 250*(1-value), });// cm.setSaturation(0.9f); // Create a paint object with color matrix Paint greyPaint = new Paint(); greyPaint.setColorFilter(new ColorMatrixColorFilter(cm)); // Create a hardware layer with the grey paint child.setLayerType(View.LAYER_TYPE_HARDWARE, greyPaint); if (value >= 1) { // Remove the hardware layer child.setLayerType(View.LAYER_TYPE_NONE, null); } } @Override public void onScrollStateChanged(int state) { super.onScrollStateChanged(state); switch (state){ case RecyclerView.SCROLL_STATE_IDLE: //滚动停止时 fixOffsetWhenFinishScroll(); break; case RecyclerView.SCROLL_STATE_DRAGGING: //拖拽滚动时 break; case RecyclerView.SCROLL_STATE_SETTLING: //动画滚动时 break; } } @Override public void scrollToPosition(int position) { if (position < 0 || position > getItemCount() - 1) return; mOffsetAll = calculateOffsetForPosition(position); if (mRecycle == null || mState == null) {//如果RecyclerView还没初始化完,先记录下要滚动的位置 mSelectPosition = position; } else { layoutItems(mRecycle, mState, position > mSelectPosition ? SCROLL_RIGHT : SCROLL_LEFT); onSelectedCallBack(); } } @Override public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) { int finalOffset = calculateOffsetForPosition(position); if (mRecycle == null || mState == null) {//如果RecyclerView还没初始化完,先记录下要滚动的位置 mSelectPosition = position; } else { startScroll(mOffsetAll, finalOffset); } } @Override public boolean canScrollHorizontally() { return true; } @Override public void onAdapterChanged(RecyclerView.Adapter oldAdapter, RecyclerView.Adapter newAdapter) { removeAllViews(); mRecycle = null; mState = null; mOffsetAll = 0; mSelectPosition = 0; mLastSelectPosition = 0; mHasAttachedItems.clear(); mAllItemFrames.clear(); } /** * 获取整个布局的水平空间大小 */ private int getHorizontalSpace() { return getWidth() - getPaddingRight() - getPaddingLeft(); } /** * 获取整个布局的垂直空间大小 */ private int getVerticalSpace() { return getHeight() - getPaddingBottom() - getPaddingTop(); } /** * 获取最大偏移量 */ private float getMaxOffset() { return (getItemCount() - 1) * getIntervalDistance(); } /** * 计算Item缩放系数 * @param x Item的偏移量 * @return 缩放系数 */ private float computeScale(int x) { float scale = 1 - Math.abs(x - mStartX) * 1.0f / Math.abs(mStartX + mDecoratedChildWidth / mIntervalRatio); if (scale < 0) scale = 0; if (scale > 1) scale = 1; return scale; } /** * 计算Item的灰度值 * @param x Item的偏移量 * @return 灰度系数 */ private float computeGreyScale(int x) { float itemMidPos = x + mDecoratedChildWidth / 2; //item中点x坐标 float itemDx2Mid = Math.abs(itemMidPos - getHorizontalSpace() / 2); //item中点距离控件中点距离 float value = 1 - itemDx2Mid * 1.0f / (getHorizontalSpace() /2); if (value < 0.1) value = 0.1f; if (value > 1) value = 1; value = (float) Math.pow(value,.8); return value; } /** * 计算Item半透值 * @param x Item的偏移量 * @return 缩放系数 */ private float computeAlpha(int x) { float alpha = 1 - Math.abs(x - mStartX) * 1.0f / Math.abs(mStartX + mDecoratedChildWidth / mIntervalRatio); if (alpha < 0.3f) alpha = 0.3f; if (alpha > 1) alpha = 1.0f; return alpha; } /** * 计算Item所在的位置偏移 * @param position 要计算Item位置 */ private int calculateOffsetForPosition(int position) { return Math.round(getIntervalDistance() * position); } /** * 修正停止滚动后,Item滚动到中间位置 */ private void fixOffsetWhenFinishScroll() { int scrollN = (int) (mOffsetAll * 1.0f / getIntervalDistance()); float moreDx = (mOffsetAll % getIntervalDistance()); if (moreDx > (getIntervalDistance() * 0.5)) { scrollN ++; } int finalOffset = (int) (scrollN * getIntervalDistance()); startScroll(mOffsetAll, finalOffset); mSelectPosition = Math.round (finalOffset * 1.0f / getIntervalDistance()); } /** * 滚动到指定X轴位置 * @param from X轴方向起始点的偏移量 * @param to X轴方向终点的偏移量 */ private void startScroll(int from, int to) { if (mAnimation != null && mAnimation.isRunning()) { mAnimation.cancel(); } final int direction = from < to ? SCROLL_RIGHT : SCROLL_LEFT; mAnimation = ValueAnimator.ofFloat(from, to); mAnimation.setDuration(500); mAnimation.setInterpolator(new DecelerateInterpolator()); mAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mOffsetAll = Math.round((float) animation.getAnimatedValue()); layoutItems(mRecycle, mState, direction); } }); mAnimation.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { } @Override public void onAnimationEnd(Animator animation) { onSelectedCallBack(); } @Override public void onAnimationCancel(Animator animation) { } @Override public void onAnimationRepeat(Animator animation) { } }); mAnimation.start(); } /** * 获取Item间隔 */ private float getIntervalDistance() { return mDecoratedChildWidth * mIntervalRatio; } /** * 计算当前选中位置,并回调 */ private void onSelectedCallBack() { mSelectPosition = Math.round (mOffsetAll / getIntervalDistance()); if (mSelectedListener != null && mSelectPosition != mLastSelectPosition) { mSelectedListener.onItemSelected(mSelectPosition); } mLastSelectPosition = mSelectPosition; } /** * 获取第一个可见的Item位置 *
Note:该Item为绘制在可见区域的第一个Item,有可能被第二个Item遮挡 */ public int getFirstVisiblePosition() { Rect displayFrame = new Rect(mOffsetAll, 0, mOffsetAll + getHorizontalSpace(), getVerticalSpace()); int cur = getCenterPosition(); for (int i = cur - 1; i >= 0; i--) { Rect rect = getFrame(i); if (!Rect.intersects(displayFrame, rect)) { return i + 1; } } return 0; } /** * 获取最后一个可见的Item位置 *
Note:该Item为绘制在可见区域的最后一个Item,有可能被倒数第二个Item遮挡 */ public int getLastVisiblePosition() { Rect displayFrame = new Rect(mOffsetAll, 0, mOffsetAll + getHorizontalSpace(), getVerticalSpace()); int cur = getCenterPosition(); for (int i = cur + 1; i < getItemCount(); i++) { Rect rect = getFrame(i); if (!Rect.intersects(displayFrame, rect)) { return i - 1; } } return cur; } /** * 获取可见范围内最大的显示Item个数 */ public int getMaxVisibleCount() { int oneSide = (int) ((getHorizontalSpace() - mStartX) / (getIntervalDistance())); return oneSide * 2 + 1; } /** * 获取中间位置 *
Note:该方法主要用于{@link RecyclerCoverFlow#getChildDrawingOrder(int, int)}判断中间位置 *
如果需要获取被选中的Item位置,调用{@link #getSelectedPos()} */ public int getCenterPosition() { int pos = (int) (mOffsetAll / getIntervalDistance()); int more = (int) (mOffsetAll % getIntervalDistance()); if (more > getIntervalDistance() * 0.5f) pos++; return pos; } /** * 设置选中监听 * @param l 监听接口 */ public void setOnSelectedListener(OnSelected l) { mSelectedListener = l; } /** * 获取被选中Item位置 */ public int getSelectedPos() { return mSelectPosition; } /** * 选中监听接口 */ public interface OnSelected { /** * 监听选中回调 * @param position 显示在中间的Item的位置 */ void onItemSelected(int position); } public static class Builder { boolean isFlat = false; boolean isGreyItem = false; boolean isAlphaItem = false; float cstIntervalRatio = -1f; public Builder setFlat(boolean flat) { isFlat = flat; return this; } public Builder setGreyItem(boolean greyItem) { isGreyItem = greyItem; return this; } public Builder setAlphaItem(boolean alphaItem) { isAlphaItem = alphaItem; return this; } public Builder setIntervalRatio(float ratio) { cstIntervalRatio = ratio; return this; } public CoverFlowLayoutManger build() { return new CoverFlowLayoutManger(isFlat, isGreyItem, isAlphaItem, cstIntervalRatio); } }}
package com.as.demo_ok6.coverflow;import android.content.Context;import android.support.annotation.Nullable;import android.support.v7.widget.RecyclerView;import android.util.AttributeSet;import android.view.MotionEvent;/** * 继承RecyclerView重写{@link #getChildDrawingOrder(int, int)}对Item的绘制顺序进行控制 * * @author Chen Xiaoping (562818444@qq.com) * @version V1.0 * @Datetime 2017-04-18 */public class RecyclerCoverFlow extends RecyclerView { /** * 按下的X轴坐标 */ private float mDownX; /** * 布局器构建者 */ private CoverFlowLayoutManger.Builder mManagerBuilder; public RecyclerCoverFlow(Context context) { super(context); init(); } public RecyclerCoverFlow(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(); } public RecyclerCoverFlow(Context context, @Nullable AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); } private void init() { createManageBuilder(); setLayoutManager(mManagerBuilder.build()); setChildrenDrawingOrderEnabled(true); //开启重新排序 setOverScrollMode(OVER_SCROLL_NEVER); } /** * 创建布局构建器 */ private void createManageBuilder() { if (mManagerBuilder == null) { mManagerBuilder = new CoverFlowLayoutManger.Builder(); } } /** * 设置是否为普通平面滚动 * @param isFlat true:平面滚动;false:叠加缩放滚动 */ public void setFlatFlow(boolean isFlat) { createManageBuilder(); mManagerBuilder.setFlat(isFlat); setLayoutManager(mManagerBuilder.build()); } /** * 设置Item灰度渐变 * @param greyItem true:Item灰度渐变;false:Item灰度不变 */ public void setGreyItem(boolean greyItem) { createManageBuilder(); mManagerBuilder.setGreyItem(greyItem); setLayoutManager(mManagerBuilder.build()); } /** * 设置Item灰度渐变 * @param alphaItem true:Item半透渐变;false:Item透明度不变 */ public void setAlphaItem(boolean alphaItem) { createManageBuilder(); mManagerBuilder.setAlphaItem(alphaItem); setLayoutManager(mManagerBuilder.build()); } /** * 设置Item的间隔比例 * @param intervalRatio Item间隔比例。 * 即:item的宽 x intervalRatio */ public void setIntervalRatio(float intervalRatio) { createManageBuilder(); mManagerBuilder.setIntervalRatio(intervalRatio); setLayoutManager(mManagerBuilder.build()); } @Override public void setLayoutManager(LayoutManager layout) { if (!(layout instanceof CoverFlowLayoutManger)) { throw new IllegalArgumentException("The layout manager must be CoverFlowLayoutManger"); } super.setLayoutManager(layout); } @Override protected int getChildDrawingOrder(int childCount, int i) { int center = getCoverFlowLayout().getCenterPosition() - getCoverFlowLayout().getFirstVisiblePosition(); //计算正在显示的所有Item的中间位置 if (center < 0) center = 0; else if (center > childCount) center = childCount; int order; if (i == center) { order = childCount - 1; } else if (i > center) { order = center + childCount - 1 - i; } else { order = i; } return order; } /** * 获取LayoutManger,并强制转换为CoverFlowLayoutManger */ public CoverFlowLayoutManger getCoverFlowLayout() { return ((CoverFlowLayoutManger)getLayoutManager()); } /** * 获取被选中的Item位置 */ public int getSelectedPos() { return getCoverFlowLayout().getSelectedPos(); } /** * 设置选中监听 * @param l 监听接口 */ public void setOnItemSelectedListener(CoverFlowLayoutManger.OnSelected l) { getCoverFlowLayout().setOnSelectedListener(l); } @Override public boolean dispatchTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: mDownX = ev.getX(); getParent().requestDisallowInterceptTouchEvent(true); //设置父类不拦截滑动事件 break; case MotionEvent.ACTION_MOVE: if ((ev.getX() > mDownX && getCoverFlowLayout().getCenterPosition() == 0) || (ev.getX() < mDownX && getCoverFlowLayout().getCenterPosition() == getCoverFlowLayout().getItemCount() -1)) { //如果是滑动到了最前和最后,开放父类滑动事件拦截 getParent().requestDisallowInterceptTouchEvent(false); } else { //滑动到中间,设置父类不拦截滑动事件 getParent().requestDisallowInterceptTouchEvent(true); } break; } return super.dispatchTouchEvent(ev); }}
https://github.com/zhangqifan1/Demo_ok6
2.https://github.com/zhangqifan1/FlyPiyInSky s烧烤摊里面有很多
3.OverFlying 这个 ViewPager 其实有个这种的 RecyclerView 的 也不错
public class OverFlyingLayoutManager extends RecyclerView.LayoutManager { private static final String TAG = "OverFlying"; private @FloatRange(from = 0.01, to = 1.0) float edgePercent = 0.8f;//触发边缘动画距离百分比 private @FloatRange(from = 1) float slowTimes = 3;//到达此距离后放慢倍数 private int orientation = OrientationHelper.VERTICAL; private boolean offsetUseful = false; private int overFlyingDist; private int totalHeight = 0; private int totalWidth = 0; private int verticalScrollOffset; private int horizontalScrollOffset; //头部是否也要层叠,默认需要 private boolean topOverFlying; private int viewWidth, viewHeight; public OverFlyingLayoutManager() { this(OrientationHelper.VERTICAL); } public OverFlyingLayoutManager(int orientation) { this(orientation, true); } public OverFlyingLayoutManager(int orientation, boolean topOverFlying) { this.orientation = orientation; this.topOverFlying = topOverFlying; } @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) { super.onLayoutChildren(recycler, state); // 先把所有的View先从RecyclerView中detach掉,然后标记为"Scrap"状态,表示这些View处于可被重用状态(非显示中)。 // 实际就是把View放到了Recycler中的一个集合中。 if (getItemCount() == 0) {//没有Item detachAndScrapAttachedViews(recycler); return; } if (getChildCount() == 0 && state.isPreLayout()) { return; } reset(); detachAndScrapAttachedViews(recycler); calculateChildrenSite(recycler, state); } private void reset() { totalHeight = 0; totalWidth = 0; if (!offsetUseful) { verticalScrollOffset = 0; horizontalScrollOffset = 0; } offsetUseful = false; } private void calculateChildrenSite(RecyclerView.Recycler recycler, RecyclerView.State state) { if (orientation == OrientationHelper.VERTICAL) { calculateChildrenSiteVertical(recycler, state); addAndLayoutViewVertical(recycler, state, verticalScrollOffset); } else { calculateChildrenSiteHorizontal(recycler, state); addAndLayoutViewHorizontal(recycler, state, horizontalScrollOffset); } } private void calculateChildrenSiteVertical(RecyclerView.Recycler recycler, RecyclerView.State state) { View view = recycler.getViewForPosition(0);//暂时这么解决,不能layout出所有的子View measureChildWithMargins(view, 0, 0); calculateItemDecorationsForChild(view, new Rect()); viewHeight = getDecoratedMeasuredHeight(view); overFlyingDist = (int) (slowTimes * viewHeight); totalHeight = getItemCount() * viewHeight; Log.d(TAG, "childCountI = " + getChildCount() + " itemCount= " + recycler.getScrapList().size()); } private void calculateChildrenSiteHorizontal(RecyclerView.Recycler recycler, RecyclerView.State state) { View view = recycler.getViewForPosition(0);//暂时这么解决,不能layout出所有的子View measureChildWithMargins(view, 0, 0); calculateItemDecorationsForChild(view, new Rect()); viewWidth = getDecoratedMeasuredWidth(view); overFlyingDist = (int) (slowTimes * viewWidth); totalWidth = getItemCount() * viewWidth; Log.d(TAG, "childCountI = " + getChildCount() + " itemCount= " + recycler.getScrapList().size()); } @Override public boolean canScrollHorizontally() { // 返回true表示可以横向滑动 return orientation == OrientationHelper.HORIZONTAL; } @Override public boolean canScrollVertically() { // 返回true表示可以纵向滑动 return orientation == OrientationHelper.VERTICAL; } @Override public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) { //列表向下滚动dy为正,列表向上滚动dy为负,这点与Android坐标系保持一致。 int tempDy = dy; if (verticalScrollOffset <= totalHeight - getVerticalSpace()) { verticalScrollOffset += dy; //将竖直方向的偏移量+travel } if (verticalScrollOffset > totalHeight - getVerticalSpace()) { verticalScrollOffset = totalHeight - getVerticalSpace(); tempDy = 0; } else if (verticalScrollOffset < 0) { verticalScrollOffset = 0; tempDy = 0; } detachAndScrapAttachedViews(recycler); addAndLayoutViewVertical(recycler, state, verticalScrollOffset); //从新布局位置、显示View return tempDy; } @Override public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) { int tempDx = dx; if (horizontalScrollOffset <= totalWidth - getHorizontalSpace()) { horizontalScrollOffset += dx; //将竖直方向的偏移量+travel } if (horizontalScrollOffset > totalWidth - getHorizontalSpace()) { horizontalScrollOffset = totalWidth - getHorizontalSpace(); tempDx = 0; } else if (horizontalScrollOffset < 0) { horizontalScrollOffset = 0; tempDx = 0; } detachAndScrapAttachedViews(recycler); addAndLayoutViewHorizontal(recycler, state, horizontalScrollOffset); //从新布局位置、显示View return tempDx; } private void addAndLayoutViewVertical(RecyclerView.Recycler recycler, RecyclerView.State state, int offset) { int itemCount = getItemCount(); if (itemCount <= 0 || state.isPreLayout()) { return; } int displayHeight = getVerticalSpace(); for (int i = itemCount - 1; i >= 0; i--) { // 遍历Recycler中保存的View取出来 int bottomOffset = (i + 1) * viewHeight - offset; int topOffset = i * viewHeight - offset; boolean needAdd = true; if (bottomOffset - displayHeight >= overFlyingDist) { needAdd = false; } if (topOffset < -overFlyingDist && i != 0 && topOverFlying || topOffset < -overFlyingDist && !topOverFlying) { needAdd = false; } if (needAdd) { View view = recycler.getViewForPosition(i); addView(view); // 因为刚刚进行了detach操作,所以现在可以重新添加 measureChildWithMargins(view, 0, 0); // 通知测量view的margin值 int width = getDecoratedMeasuredWidth(view); // 计算view实际大小,包括了ItemDecorator中设置的偏移量。 int height = getDecoratedMeasuredHeight(view); //调用这个方法能够调整ItemView的大小,以除去ItemDecorator。 calculateItemDecorationsForChild(view, new Rect()); int realBottomOffset = bottomOffset; if (topOverFlying) { if (i != 0) {//除第一个外的顶部黏性动画 if (topOffset <= height * edgePercent) {//到达顶部边界了 int edgeDist = (int) (height * edgePercent);//边界触发距离 int top = (int) (edgeDist - (edgeDist - topOffset) / slowTimes);//到达边界后速度放慢到原来5分之一 top = Math.max(top, 0); realBottomOffset = top + height; } } else { realBottomOffset = height; } } if (i != itemCount - 1) {//除最后一个外的底部慢速动画 if (displayHeight - bottomOffset <= height * edgePercent) { int edgeDist = (int) (displayHeight - height * edgePercent); int bottom = (int) (edgeDist + (bottomOffset - edgeDist) / slowTimes); bottom = Math.min(bottom, displayHeight); realBottomOffset = bottom; } } else { realBottomOffset = totalHeight > displayHeight ? displayHeight : totalHeight; } layoutDecoratedWithMargins(view, 0, realBottomOffset - height, width, realBottomOffset); } } Log.d(TAG, "childCount = " + getChildCount() + " itemCount= " + itemCount); } private void addAndLayoutViewHorizontal(RecyclerView.Recycler recycler, RecyclerView.State state, int offset) { int itemCount = getItemCount(); if (itemCount <= 0 || state.isPreLayout()) { return; } int displayWidth = getHorizontalSpace(); for (int i = itemCount - 1; i >= 0; i--) { int rightOffset = (i + 1) * viewWidth - offset; int leftOffset = i * viewWidth - offset; boolean needAdd = true; if (rightOffset - displayWidth >= overFlyingDist) { needAdd = false; } if (leftOffset < -overFlyingDist && i != 0 || leftOffset < -overFlyingDist && !topOverFlying) { needAdd = false; } if (needAdd) { // 遍历Recycler中保存的View取出来 View view = recycler.getViewForPosition(i); addView(view); // 因为刚刚进行了detach操作,所以现在可以重新添加 measureChildWithMargins(view, 0, 0); // 通知测量view的margin值 int width = getDecoratedMeasuredWidth(view); // 计算view实际大小,包括了ItemDecorator中设置的偏移量。 int height = getDecoratedMeasuredHeight(view); //调用这个方法能够调整ItemView的大小,以除去ItemDecorator。 calculateItemDecorationsForChild(view, new Rect()); int realRightOffset = rightOffset; if (topOverFlying) {//除第一个外的左边缘慢速动画 if (i != 0) { if (leftOffset <= width * edgePercent) {//到达边界了 int edgeDist = (int) (width * edgePercent);//边界触发距离 int left = (int) (edgeDist - (edgeDist - leftOffset) / slowTimes);///到达边界后速度放慢到原来5分之一 left = Math.max(0, left); if (left < 0) { left = 0; } realRightOffset = left + width; } } else { realRightOffset = width; } } if (i != itemCount - 1) {//除最后一个外的右边缘慢速动画 if (displayWidth - rightOffset <= width * edgePercent) { int edgeDist = (int) (displayWidth - width * edgePercent); int right = (int) (edgeDist + (rightOffset - edgeDist) / slowTimes); if (right >= displayWidth) { right = displayWidth; } realRightOffset = right; } } else { realRightOffset = totalWidth > displayWidth ? displayWidth : totalWidth; } layoutDecoratedWithMargins(view, realRightOffset - width, 0, realRightOffset, height); } } Log.d(TAG, "childCount = " + getChildCount() + " itemCount= " +itemCount); } private int getVerticalSpace() { // 计算RecyclerView的可用高度,除去上下Padding值 return getHeight() - getPaddingBottom() - getPaddingTop(); } private int getHorizontalSpace() { return getWidth() - getPaddingLeft() - getPaddingRight(); } public void setEdgePercent(@FloatRange(from = 0.01, to = 1.0) float edgePercent) { this.edgePercent = edgePercent; } /** * {@inheritDoc} */ @Override public View findViewByPosition(int position) { final int childCount = getChildCount(); if (childCount == 0) { return null; } final int firstChild = getPosition(getChildAt(0)); final int viewPosition = position - firstChild; if (viewPosition >= 0 && viewPosition < childCount) { final View child = getChildAt(viewPosition); if (getPosition(child) == position) { return child; // in pre-layout, this may not match } } return super.findViewByPosition(position); } @Override public void scrollToPosition(int position) { offsetUseful = true; if (orientation == OrientationHelper.VERTICAL && viewHeight != 0) { verticalScrollOffset = position * viewHeight; } else if (orientation == OrientationHelper.HORIZONTAL && viewWidth != 0) { horizontalScrollOffset = position * viewWidth; } requestLayout(); } public void setSlowTimes(@IntRange(from = 1) int slowTimes) { this.slowTimes = slowTimes; }}
<?xml version="1.0" encoding="utf-8"?>
List list = new ArrayList() {{ for (int i = 0; i < 100; i++) { add(" "+i+" "); } }}; /*******0*******/ final RecyclerView recycelrview = findViewById(R.id.recycelrview); OverFlyingLayoutManager overFlyingLayoutManager = new OverFlyingLayoutManager(OrientationHelper.HORIZONTAL); recycelrview.setLayoutManager(overFlyingLayoutManager); MyAdapter myAdapter = new MyAdapter(R.layout.item_text, list); recycelrview.setAdapter(myAdapter); /*******1*******/ final RecyclerView recycelrview1 = findViewById(R.id.recycelrview1); OverFlyingLayoutManager overFlyingLayoutManager1 = new OverFlyingLayoutManager(OrientationHelper.HORIZONTAL,false); recycelrview1.setLayoutManager(overFlyingLayoutManager1); MyAdapter myAdapter1 = new MyAdapter(R.layout.item_text, list); recycelrview1.setAdapter(myAdapter1); /*******2*******/ final RecyclerView recycelrview2 = findViewById(R.id.recycelrview2); OverFlyingLayoutManager overFlyingLayoutManager2 = new OverFlyingLayoutManager(OrientationHelper.VERTICAL); recycelrview2.setLayoutManager(overFlyingLayoutManager2); MyAdapter myAdapter2 = new MyAdapter(R.layout.item_text, list); recycelrview2.setAdapter(myAdapter2); /*******3*******/ final RecyclerView recycelrview3 = findViewById(R.id.recycelrview3); OverFlyingLayoutManager overFlyingLayoutManager3 = new OverFlyingLayoutManager(OrientationHelper.VERTICAL,false); recycelrview3.setLayoutManager(overFlyingLayoutManager3); MyAdapter myAdapter3 = new MyAdapter(R.layout.item_text, list); recycelrview3.setAdapter(myAdapter3);
<?xml version="1.0" encoding="utf-8"?>
首先RecyclerView 不能wrap_content 第二 条目的 宽高影响着效果
4.https://github.com/DingMouRen/LayoutManagerGroup 这个也很多
更多相关文章
- 改变滚动条的颜色ScrollView
- Android(安卓)ListView滑动回弹——overScrollBy
- Android布局文件.xml中的自定义属性(结合封装的自定义View)
- android获取recycleview滚动的距离
- absolutelayout 实现图片重叠
- Android在布局文件指定位置动态增加删除布局
- smartrefreshlayout 只开启纯滚动模式
- ViewFlipper实现View轮播点击等效果
- Android(安卓)Gallery获取滑动停止的位置