Google最新發(fā)布的Support RecyclerView包更新到24.2.0,這次來(lái)聊聊RecyclerView的新特性和SnapHelper的關(guān)系。
一句話介紹SnapHelper
: SnapHelper是RecyclerView功能的一種拓展,使RecyclerView滑動(dòng)行為類似ViewPager,無(wú)論怎么滑動(dòng)最終停留在某頁(yè)正中間。ViewPager一次只能滑動(dòng)一頁(yè),RecyclerView+SnapHelper方式可以一次滑動(dòng)好幾頁(yè),且最終都停留在某頁(yè)正中間。非常實(shí)用和酷炫。
SnapHelper的實(shí)現(xiàn)原理是監(jiān)聽(tīng)RecyclerView.OnFlingListener中的onFling接口。LinearSnapHelper
是抽象類SnapHelper的具體實(shí)現(xiàn)。
上面的效果只需下面幾行代碼即可。重點(diǎn)在于new LinearSnapHelper().attachToRecyclerView(recyclerView);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
new LinearSnapHelper().attachToRecyclerView(recyclerView);
接下來(lái)具體分析LinearSnapHelper是怎么實(shí)現(xiàn)類似ViewPager的功能的
attachToRecyclerView,居中處理分析
public void attachToRecyclerView(@Nullable RecyclerView recyclerView) throws IllegalStateException {
...
snapToTargetExistingView();
...
}
/**
* 1. 找到居中顯示的View
* 2. 計(jì)算view離當(dāng)前的位置距離, 調(diào)用mRecyclerView.smoothScrollBy使其居中
*/
private void snapToTargetExistingView() {
View snapView = findSnapView(layoutManager);
int[] snapDistance = calculateDistanceToFinalSnap(layoutManager, snapView);
if (snapDistance[0] != 0 || snapDistance[1] != 0) {
mRecyclerView.smoothScrollBy(snapDistance[0], snapDistance[1]);
}
}
/**
* 1. 找到當(dāng)前RecyclerView的居中位置center
* 2. 循環(huán)遍歷子節(jié)點(diǎn),找出子節(jié)點(diǎn)居中位置最接近c(diǎn)enter的視圖,及SnapView
*/
public View findSnapView(RecyclerView.LayoutManager layoutManager) {
...
}
/**
* 計(jì)算到targetView要移動(dòng)的距離
*/
@Override
public int[] calculateDistanceToFinalSnap(
@NonNull RecyclerView.LayoutManager layoutManager, @NonNull View targetView) {
...
}
RecyclerView.OnFling,滑動(dòng)后停止到居中位置分析
SnapHelper extends RecyclerView.OnFlingListener,重載onFling函數(shù)
public boolean onFling(int velocityX, int velocityY) {
LayoutManager layoutManager = mRecyclerView.getLayoutManager();
if (layoutManager == null) {
return false;
}
RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
if (adapter == null) {
return false;
}
int minFlingVelocity = mRecyclerView.getMinFlingVelocity();
return (Math.abs(velocityY) > minFlingVelocity || Math.abs(velocityX) > minFlingVelocity)
&& snapFromFling(layoutManager, velocityX, velocityY);
}
/**
* 同樣的套路,先根據(jù)移動(dòng)速度確定最終位置,然后startSmoothScroll
*/
private boolean snapFromFling(@NonNull LayoutManager layoutManager, int velocityX,
int velocityY) {
...
int targetPosition = findTargetSnapPosition(layoutManager, velocityX, velocityY);
if (targetPosition == RecyclerView.NO_POSITION) {
return false;
}
smoothScroller.setTargetPosition(targetPosition);
layoutManager.startSmoothScroll(smoothScroller);
return true;
}