RecyclerView 作為一個列表滑動控制元件,我們都知道它既可以橫向滑動,也可以豎直滑動,可以實現線性佈局管理,瀑布流佈局管理,還有 GridView 佈局管理。其實我們可以控制其 Item 的停留位置,並使其實現畫廊效果。如果大家熟悉 SnapHelper 的話,估計大家就都會了。
什麼是 SnapHelper
SnapHelper 的實現原理就是是監聽 RecyclerView.OnFlingListener 中的 onFling 介面。support library 中只提供了一個繼承類 LinearSnapHelper ,LinearSnapHelper 是抽象類 SnapHelper 的具體實現。
通過 LinearSnapHelper,我們就可以使 RecyclerView 實現類似 ViewPager 的功能,無論怎麼滑動最終都會停留在列表頁面正中間。
SnapHelper 和 ViewPager 的區別就是 ViewPager 一次只能滑動一頁,而 RecyclerView + SnapHelper 的方式可以實現一次滑動好幾頁。
效果如下:
居中實現方式
使用 SnapHelper 配合 RecyclerView 實現控制 Item 位置居中顯示,非常簡單,官方預設提供的 LinearSnapHelper 就是居中的,我們直接使用即可。
程式碼如下:
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
new LinearSnapHelper().attachToRecyclerView(recyclerView);複製程式碼
自定義 SnapHelper
官方提供的預設是居中顯示,其實我們也可以自定義,比如:靠左顯示,讓可見的第一個 Item 居左顯示。
效果圖如下
自定義 SnapHelper ,一般需要實現兩個方法:
- int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) 當拖拽或滑動結束時會回撥該方法,返回一個out = int[2],out[0]x軸,out[1] y軸,這就是我們需要修改的位置偏移量
- View findSnapView(RecyclerView.LayoutManager layoutManager) 該方法返回上面方法中需要的 targetView 。
程式碼如下
public class CustomSnapHelper extends LinearSnapHelper {
private OrientationHelper mHorizontalHelper;
@Override
public int[] calculateDistanceToFinalSnap(RecyclerView.LayoutManager layoutManager, View targetView) {
int[] out = new int[2];
//判斷支援水平滾動,修改水平方向的位置,是修改的out[0]的值
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();
}
@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 firstChild = ((LinearLayoutManager) layoutManager).findFirstVisibleItemPosition();
int lastChild = ((LinearLayoutManager) layoutManager).findLastVisibleItemPosition();
if (firstChild == RecyclerView.NO_POSITION) {
return null;
}
//這行的作用是如果是最後一個,翻到最後一條,解決顯示不全的問題
if (lastChild == layoutManager.getItemCount() - 1) {
return layoutManager.findViewByPosition(lastChild);
}
View child = layoutManager.findViewByPosition(firstChild);
//獲取偏左顯示的Item
if (helper.getDecoratedEnd(child) >= helper.getDecoratedMeasurement(child) / 2
&& helper.getDecoratedEnd(child) > 0) {
return child;
} else {
return layoutManager.findViewByPosition(firstChild + 1);
}
}
return super.findSnapView(layoutManager);
}
private OrientationHelper getHorizontalHelper(
RecyclerView.LayoutManager layoutManager) {
if (mHorizontalHelper == null) {
mHorizontalHelper = OrientationHelper.createHorizontalHelper(layoutManager);
}
return mHorizontalHelper;
}
}複製程式碼
呼叫自定義的 SnapHelper 程式碼如下,配合 RecyclerView:
CustomSnapHelper mMySnapHelper = new CustomSnapHelper();
mMySnapHelper.attachToRecyclerView(rv);複製程式碼
最後,其實垂直方向也可以實現哦,大家可以嘗試一下垂直方向的使用方式是不是非常簡單。
程式碼 Demo 地址:github.com/loonggg/Sna…
歡迎大家關注我的技術分享公眾號:非著名程式設計師(smart_android)。技術文章均先首發於我的技術分享的微信公眾號。