在实际项目里,RecycleView 可以说是我们最常用到的组件,作为绑定并展示LIST数据的组件,经常需要实现平滑滚动到列表里的某个目标ITEM,并且将其置顶在屏幕最上方,而且在特殊情形下,我们需要控制滑动速度,来控制滚动的时长。
一、滑动到指定位置(target position)并且置顶
1. RecycleView默认的几个实现方法及缺陷
((LinearLayoutManager)recycleView.getLayoutManager()).scrollToPositionWithOffset(int position, int offset);
如果你没有滑动过程动画的要求,那上面这行代码将offset的值设置为0,就一步到位地满足需求了。
recycleView.scrollToPosition(int position);
recycleView.smoothScrollToPosition(int position);
以上两个方法遵循的是最少滑动原则,只要target position那项item已经完全可见了,就马上停止滑动;要是target position已经可见了,那根本不会滑动。所以按不同的滑动方向,会出现不同的结果,如果target position在屏幕可视范围的上方,则它默认会将target position置顶;反之,target position在屏幕可视范围的下方,则滚动完成后,target postion会处于屏幕的最下方,无法实现我们的置顶需求。
所以缺陷很明显:要么不动,要么无法置顶
。
2. 优化源码实现置顶方案
我们看下recycleview提供的方法的源代码,看看是否可以进行改进:
public void smoothScrollToPosition(int position) {
if (mLayoutSuppressed) {
return;
if (mLayout == null) {
Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+ "Call setLayoutManager with a non-null argument.");
return;
mLayout.smoothScrollToPosition(this, mState, position);
由代码可以看出,RecyclerView的滑动方法是调用LayoutManager的smoothScrollToPosition方法:
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state,
int position) {
LinearSmoothScroller linearSmoothScroller =
new LinearSmoothScroller(recyclerView.getContext());
linearSmoothScroller.setTargetPosition(position);
startSmoothScroll(linearSmoothScroller);
其中LinearSmoothScroller提供了三个滑动策略:
* Align child view's left or top with parent view's left or top
* @see #calculateDtToFit(int, int, int, int, int)
* @see #calculateDxToMakeVisible(android.view.View, int)
* @see #calculateDyToMakeVisible(android.view.View, int)
public static final int SNAP_TO_START = -1;
* Align child view's right or bottom with parent view's right or bottom
* @see #calculateDtToFit(int, int, int, int, int)
* @see #calculateDxToMakeVisible(android.view.View, int)
* @see #calculateDyToMakeVisible(android.view.View, int)
public static final int SNAP_TO_END = 1;
* <p>Decides if the child should be snapped from start or end, depending on where it
* currently is in relation to its parent.</p>
* <p>For instance, if the view is virtually on the left of RecyclerView, using
* {@code SNAP_TO_ANY} is the same as using {@code SNAP_TO_START}</p>
* @see #calculateDtToFit(int, int, int, int, int)
* @see #calculateDxToMakeVisible(android.view.View, int)
* @see #calculateDyToMakeVisible(android.view.View, int)
public static final int SNAP_TO_ANY = 0;
LinearSmoothScroller确定滑动方案的方法:
* When scrolling towards a child view, this method defines whether we should align the top
* or the bottom edge of the child with the parent RecyclerView.
* @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY; depending on the current target vector
* @see #SNAP_TO_START
* @see #SNAP_TO_END
* @see #SNAP_TO_ANY
protected int getVerticalSnapPreference() {
return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
重写LinearSmoothScroller的getVerticalSnapPreference方法:
class LinearTopSmoothScroller extends LinearSmoothScroller {
public LinearTopSmoothScroller(Context context) {
super(context);
@Override
protected int getVerticalSnapPreference() {
return SNAP_TO_START;
这里为什么返回 SNAP_TO_START?可以看到LinearSmoothScrollerl的方法calculateDtToFit()根据不同滚动策略获取到需要滚动的距离,SNAP_TO_START是按置顶的方案来计算的。所以我们在getVerticalSnapPreference方法里固定返回SNAP_TO_START就可以实现目的。
public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
snapPreference) {
switch (snapPreference) {
case SNAP_TO_START:
return boxStart - viewStart;
case SNAP_TO_END:
return boxEnd - viewEnd;
case SNAP_TO_ANY:
final int dtStart = boxStart - viewStart;
if (dtStart > 0) {
return dtStart;
final int dtEnd = boxEnd - viewEnd;
if (dtEnd < 0) {
return dtEnd;
break;
default:
throw new IllegalArgumentException("snap preference should be one of the"
+ " constants defined in SmoothScroller, starting with SNAP_");
return 0;
调用方式一:
void scrollItemToTop(int position) {
LinearSmoothScroller smoothScroller = new LinearTopSmoothScroller(this);
smoothScroller.setTargetPosition(position);
linearLayoutManager.startSmoothScroll(smoothScroller);
调用方式二:
自定义一个类继承自 LinearLayoutManager:
private class TopLayoutManager extends LinearLayoutManager {
public TopLayoutManager(Context context) {
super(context);
public TopLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
RecyclerView.SmoothScroller smoothScroller = new LinearTopSmoothScroller(recyclerView.getContext());
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
private class LinearTopSmoothScroller extends LinearSmoothScroller {
public LinearTopSmoothScroller(Context context) {
super(context);
@Override
protected int getVerticalSnapPreference() {
return SNAP_TO_START;
调用代码:
TopLayoutManager topLayoutManager = new TopLayoutManager(this);
recycleview.setLayoutManager(topLayoutManager);
recycleview.smoothScrollToPosition(position);
二、调整平移滑动速率
同理,可以在LinearSmoothScroller类找到决定滚动速度的方法并修改。
* Calculates the scroll speed.
* @param displayMetrics DisplayMetrics to be used for real dimension calculations
* @return The time (in ms) it should take for each pixel. For instance, if returned value is
* 2 ms, it means scrolling 1000 pixels with LinearInterpolation should take 2 seconds.
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
上面的MILLISECONDS_PER_INCH的值为25F,如果希望更快可以将值改小,这个方法的返回值表示滚动一个像素需要的时间,单位ms,如果返回值为2ms,表示滚动1000个像素需要花费2秒时长。
平滑滚动到target position,【置顶+调速】的调用方式:
RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(this) {
@Override protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
//默认值是25F(MILLISECONDS_PER_INCH),值越小滑动速度越快,值越大则越慢
return 100F / displayMetrics.densityDpi;
smoothScroller.setTargetPosition(position);
linearLayoutManager.startSmoothScroll(smoothScroller);
目前还有一个问题,虽然我们可以调整速度,但是这里始终是一个固定的滚动速度,试想如果滚动的距离特别远,仍然需要滚动很长的时间;又或者滚动距离太近,那么滚动动画一瞬间就结束了,缺少了流畅感。
所以我们可以根据需要滚动的远或近来设置不同的滚动速度:
RecyclerView.SmoothScroller smoothScroller = new LinearSmoothScroller(this) {
@Override protected int getVerticalSnapPreference() {
return LinearSmoothScroller.SNAP_TO_START;
@Override
protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
// 第一个可见位置
int firstItem = linearLayoutManager.findFirstVisibleItemPosition();
int diff = Math.abs(position - firstItem);
// 将 diff 作分母:滚动距离越远,速度越快。 (100f/diff) 数值如果过小会导致速度过快, 可以再乘一个速度因子变量(speedFactor)来调整
int speedFactor = 5;
float speed = (100f / diff) * speedFactor;
return speed / displayMetrics.densityDpi;
三、其他方案:置顶、置顶加偏移、居中
1. 其他置顶方案
另外一个实现置顶方案:可以参考这篇文章,Android RecyclerView滚动定位 ,它主要解决的是滚动到屏幕下面ITEM,无法置顶的问题,思路是:先用scrollToPosition,将要置顶的项先移动显示出来,然后计算这一项离顶部的距离,用scrollBy完成最后的100米!
这个方案还有个好处就是,如果target position很远(滑动距离很长),也不会导致屏幕滚动过长的时间。向上向下动态滚动(动画过程)距离都不超过一个屏幕的距离。
2. 置顶加偏移
另外,如果希望置顶后,可以有一定的偏移量(离顶部有一定距离),可以参考这篇文章:
RecyclerView的smooth scroller -- 诸多案例
3. 滚动居中
如果希望target position在滚动结束后,停留在屏幕中间,可以参考下这篇文章:
RecyclerView smoothScroll to position in the center. android
Recyclerview滑动对齐方式
遇到一个问题,就是常见的双击让Recyclerview跳到知道position,但是跳转却是到对应position的底部,而不是上面对齐。
方法:smoothScrollToPosition(position)
Recyclerview空实现smoothScrollToPosition,最后由对应的LayoutManager实现,这里是LinearLayoutManager
@Override
public void smoothScrollToPosi
今天给大家分享是如何在RecyclerView实现全选,ItemTouchHelper实现侧滑删除,拖拽功能。比较基础。关于RecyclerView的强大,就不多说了。在Android L SDK发布的新API中最有意思的就是RecyclerView 和 CardView了, 按照官方的说法, RecyclerView 一个ListView 的一个更高级更灵活的一个版本, 可以自定义的东西太多了。
RecyclerView实现全选,ItemTouchHelper实现侧滑删除,拖拽功能
使用RecyclerView,首先我们需要依赖
compile 'com.android.
ListView 由于其强大的功能,在过去的 Andorid 开发中使用非常广泛,直到今天仍然还有很多人在使用着,不过 ListView 也有自己的缺陷,例如需要优化来提升运行效率,还有就是只能够纵向移动,我们要想实现横向移动就实现不了,ListView 的扩展性也不好
为此 Android 提供了一个更强大的控件--RecyclerView 它可以说是一个增强版的 ListView 不仅可以实现和 ListVie
RecyclerView 是 Android 官方提供的一个高度可定制化的列表控件,它可以实现高效的列表滚动,支持垂直和水平滚动。
要实现 RecyclerView 的垂直滚动,可以按照以下步骤进行:
1. 在布局文件中添加 RecyclerView 控件:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
2. 创建 RecyclerView 的 Adapter 类,继承自 RecyclerView.Adapter,并实现以下方法:
- onCreateViewHolder:创建 ViewHolder 对象
- onBindViewHolder:给 ViewHolder 绑定数据
- getItemCount:获取数据的数量
3. 创建 RecyclerView 的 ViewHolder 类,继承自 RecyclerView.ViewHolder,并在构造方法中初始化布局控件。
4. 在 Activity 或 Fragment 中获取 RecyclerView 的实例,设置布局管理器和 Adapter:
// 获取 RecyclerView 实例
RecyclerView recyclerView = findViewById(R.id.recyclerView);
// 创建布局管理器
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
layoutManager.setOrientation(LinearLayoutManager.VERTICAL);
// 设置布局管理器和 Adapter
recyclerView.setLayoutManager(layoutManager);
recyclerView.setAdapter(adapter);
其中,布局管理器用于控制列表的排列方式,这里使用 LinearLayoutManager 实现垂直滚动。
5. 在 Adapter 中实现数据更新的方法,例如添加、删除、修改等操作,然后调用 Adapter 的 notifyDataSetChanged 方法刷新列表。
至此,就完成了 RecyclerView 的垂直滚动实现。