赞
踩
在项目中遇到了需要实现RecycleView自动滑动的效果,虽然RecycleView有自带的滑动到某个位置。但是速度太快,还没看见就结束了。所以需要重新订制一下。
道理很简单,RecycleView有一个延时的方法:postDelayed,再起一个线程,配合上延时方法,就可以实现滚动了。看代码:
首先,自定义一个View继承RecycleView,
AutoPollRecyclerView.java
import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import java.lang.ref.WeakReference; import androidx.annotation.Nullable; import androidx.recyclerview.widget.RecyclerView; public class AutoPollRecyclerView extends RecyclerView { private static final long TIME_AUTO_POLL = 16; public AutoPollTask autoPollTask; private boolean running; //标示是否正在自动轮询 public boolean canRun;//标示是否可以自动轮询,可在不需要的是否置false public AutoPollRecyclerView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); autoPollTask = new AutoPollTask(this); } static class AutoPollTask implements Runnable { private final WeakReference<AutoPollRecyclerView> mReference; //使用弱引用持有外部类引用->防止内存泄漏 public AutoPollTask(AutoPollRecyclerView reference) { this.mReference = new WeakReference<AutoPollRecyclerView>(reference); } @Override public void run() { AutoPollRecyclerView recyclerView = mReference.get(); if (recyclerView != null && recyclerView.running && recyclerView.canRun) { recyclerView.scrollBy(2, 2); recyclerView.postDelayed(recyclerView.autoPollTask, recyclerView.TIME_AUTO_POLL); } } } //开启:如果正在运行,先停止->再开启 public void start() { if (running) stop(); canRun = true; running = true; postDelayed(autoPollTask, TIME_AUTO_POLL); } public void stop() { running = false; canRun = false; removeCallbacks(autoPollTask); } @Override public boolean onTouchEvent(MotionEvent e) { switch (e.getAction()) { case MotionEvent.ACTION_DOWN: if (running) stop(); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_OUTSIDE: if (canRun) start(); break; } return super.onTouchEvent(e); } }
剩下的用法和RecycleView平时的用法一样,只需要在Adapter加载后,执行 start() 即可,如果想停止自动滚动,只需要在业务需要的地方执行stop() 。
此上是正常滚动效果,下面是像viewPager一样的翻页滚动效果:
GallerySnapHelper.java
import android.graphics.PointF; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearSmoothScroller; import android.support.v7.widget.OrientationHelper; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.SnapHelper; import android.util.DisplayMetrics; import android.view.View; import android.support.annotation.NonNull; import android.support.annotation.Nullable; public class GallerySnapHelper extends SnapHelper { private static final float INVALID_DISTANCE = 1f; private static final float MILLISECONDS_PER_INCH = 40f; private OrientationHelper mHorizontalHelper; private RecyclerView mRecyclerView; @Override public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException { mRecyclerView = recyclerView; super.attachToRecyclerView(recyclerView); } @Override public int[] calculateDistanceToFinalSnap(@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) { int[] out = new int[2]; if (layoutManager.canScrollHorizontally()) { out[0] = distanceToStart(targetView, getHorizontalHelper(layoutManager)); } else { out[0] = 0; } return out; } private int distanceToStart(View targetView, OrientationHelper helper) { return helper.getDecoratedStart(targetView) - helper.getStartAfterPadding(); } @Nullable protected LinearSmoothScroller createSnapScroller(final RecyclerView.LayoutManager layoutManager) { if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) { return null; } return new LinearSmoothScroller(mRecyclerView.getContext()) { @Override protected void onTargetFound(View targetView, RecyclerView.State state, Action action) { int[] snapDistances = calculateDistanceToFinalSnap(mRecyclerView.getLayoutManager(), targetView); final int dx = snapDistances[0]; final int dy = snapDistances[1]; final int time = calculateTimeForDeceleration(Math.max(Math.abs(dx), Math.abs(dy))); if (time > 0) { action.update(dx, dy, time, mDecelerateInterpolator); } } @Override protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) { return MILLISECONDS_PER_INCH / displayMetrics.densityDpi; } }; } @Override public int findTargetSnapPosition(RecyclerView.LayoutManager layoutManager, int velocityX, int velocityY) { if (!(layoutManager instanceof RecyclerView.SmoothScroller.ScrollVectorProvider)) { return RecyclerView.NO_POSITION; } final int itemCount = layoutManager.getItemCount(); if (itemCount == 0) { return RecyclerView.NO_POSITION; } final View currentView = findSnapView(layoutManager); if (currentView == null) { return RecyclerView.NO_POSITION; } final int currentPosition = layoutManager.getPosition(currentView); if (currentPosition == RecyclerView.NO_POSITION) { return RecyclerView.NO_POSITION; } RecyclerView.SmoothScroller.ScrollVectorProvider vectorProvider = (RecyclerView.SmoothScroller.ScrollVectorProvider) layoutManager; // deltaJumps sign comes from the velocity which may not match the order of children in // the LayoutManager. To overcome this, we ask for a vector from the LayoutManager to // get the direction. PointF vectorForEnd = vectorProvider.computeScrollVectorForPosition(itemCount - 1); if (vectorForEnd == null) { // cannot get a vector for the given position. return RecyclerView.NO_POSITION; } //在松手之后,列表最多只能滚多一屏的item数 int deltaThreshold = layoutManager.getWidth() / getHorizontalHelper(layoutManager).getDecoratedMeasurement(currentView); int hDeltaJump; if (layoutManager.canScrollHorizontally()) { hDeltaJump = estimateNextPositionDiffForFling(layoutManager, getHorizontalHelper(layoutManager), velocityX, 0); if (hDeltaJump > deltaThreshold) { hDeltaJump = deltaThreshold; } if (hDeltaJump < -deltaThreshold) { hDeltaJump = -deltaThreshold; } if (vectorForEnd.x < 0) { hDeltaJump = -hDeltaJump; } } else { hDeltaJump = 0; } if (hDeltaJump == 0) { return RecyclerView.NO_POSITION; } int targetPos = currentPosition + hDeltaJump; if (targetPos < 0) { targetPos = 0; } if (targetPos >= itemCount) { targetPos = itemCount - 1; } return targetPos; } @Override public View findSnapView(RecyclerView.LayoutManager layoutManager) { return findStartView(layoutManager, getHorizontalHelper(layoutManager)); } private View findStartView(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) { if (layoutManager instanceof LinearLayoutManager) { int firstChildPosition = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition(); if (firstChildPosition == RecyclerView.NO_POSITION) { return null; } if (((LinearLayoutManager) layoutManager).findLastCompletelyVisibleItemPosition() == layoutManager.getItemCount() - 1) { return null; } View firstChildView = layoutManager.findViewByPosition(firstChildPosition); if (helper.getDecoratedEnd(firstChildView) >= helper.getDecoratedMeasurement(firstChildView) / 2 && helper.getDecoratedEnd(firstChildView) > 0) { return firstChildView; } else { return layoutManager.findViewByPosition(firstChildPosition + 1); } } else { return null; } } private int estimateNextPositionDiffForFling(RecyclerView.LayoutManager layoutManager, OrientationHelper helper, int velocityX, int velocityY) { int[] distances = calculateScrollDistance(velocityX, velocityY); float distancePerChild = computeDistancePerChild(layoutManager, helper); if (distancePerChild <= 0) { return 0; } int distance = distances[0]; if (distance > 0) { return (int) Math.floor(distance / distancePerChild); } else { return (int) Math.ceil(distance / distancePerChild); } } private float computeDistancePerChild(RecyclerView.LayoutManager layoutManager, OrientationHelper helper) { View minPosView = null; View maxPosView = null; int minPos = Integer.MAX_VALUE; int maxPos = Integer.MIN_VALUE; int childCount = layoutManager.getChildCount(); if (childCount == 0) { return INVALID_DISTANCE; } for (int i = 0; i < childCount; i++) { View child = layoutManager.getChildAt(i); final int pos = layoutManager.getPosition(child); if (pos == RecyclerView.NO_POSITION) { continue; } if (pos < minPos) { minPos = pos; minPosView = child; } if (pos > maxPos) { maxPos = pos; maxPosView = child; } } if (minPosView == null || maxPosView == null) { return INVALID_DISTANCE; } int start = Math.min(helper.getDecoratedStart(minPosView), helper.getDecoratedStart(maxPosView)); int end = Math.max(helper.getDecoratedEnd(minPosView), helper.getDecoratedEnd(maxPosView)); int distance = end - start; if (distance == 0) { return INVALID_DISTANCE; } return 1f * distance / ((maxPos - minPos) + 1); } private OrientationHelper getHorizontalHelper(RecyclerView.LayoutManager layoutManager) { if (mHorizontalHelper == null) { mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager); } return mHorizontalHelper; } }
使用方法:
val mGallerySnapHelper = GallerySnapHelper()
mGallerySnapHelper.attachToRecyclerView(mRecycleView)
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。