使用RecyclerView滾動到螢幕指定位置
背景:
需求中有一個詳情頁新增懸浮錨點條,要求滾動到不同的item,對應錨點高亮,點選錨點滾動到指定位置。
實現及問題:
監聽滾動過程使錨點高亮並不難實現,問題在於點選錨點滾動到指定位置;
因為專案結構使用的listview,自然想到了listview.smoothScrollToPositionFromTop(int pos, int offset);
因為在頂部有一個titleBar漸變的浮層,所以使用帶有offset的過載方法。但這個方法存在問題,就是滾動定位不準確。listview的setSelectionFromTop(int pos, int offset)這個方法很準確,但是沒有滾動效果(很突兀)。從google也找了最優的解決方案,就是監聽listview的ScrollState,在滾動停止的第一時間呼叫setSelectionFromTop,最後一閃而過的短距離突兀可以忽略。但是在條目特別複雜(比如我們的詳情頁)極端情況還是會出現不準確的問題。
無奈只好使用RecyclerView重構。RecyclerView滾動方法:
rv.smoothScrollToPosition(int pos):沒有offeset引數,因為我要考慮titleBar的高度。
rv.smoothScrollBy(int dx, int dy):存在橫向偏移量,但是沒有position位置。
recyclerView的layoutManger還存在一個方法:
linearLayoutManger.scrollToPositionWithOffset(int pos, int offset):這個看似沒問題,其實也是沒有滾動效果。
最終方案:
後來從 stackOver
mSmoothScroller.setTargetPosition(mCurrentPos);
mDetailRvManager.startSmoothScroll(mSmoothScroller);
package com.xx.xx.detailpage; import android.content.Context; import android.support.v7.widget.LinearSmoothScroller; import android.view.View; /** * @description recyclerView滾動器 */ public class DetailRvSmoothScroller extends LinearSmoothScroller { private View mTitleBar; DetailRvSmoothScroller(Context context, View titleBar) { super(context); this.mTitleBar = titleBar; } /** * 指定滾動停留位置 * @return {@link #LinearSmoothScroller#SNAP_TO_START},{@link #LinearSmoothScroller#SNAP_TO_END},{@link #LinearSmoothScroller#SNAP_TO_ANY} * 1.將子檢視的左側或頂部與父檢視的左側或頂部對齊; * 2.將子檢視的右側或底部與父檢視的右側或底部對齊; * 3.具體取決於其當前與其父代相關的位置,也是預設設定。 */ @Override protected int getVerticalSnapPreference() { return LinearSmoothScroller.SNAP_TO_START; } /** * 計算最終需要滾動的距離 * @param viewStart * @param viewEnd * @param boxStart * @param boxEnd * @param snapPreference * @return */ @Override public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int snapPreference) { switch (snapPreference) { case SNAP_TO_START: return boxStart - viewStart + mTitleBar.getMeasuredHeight(); 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; } }
這裡只是重寫了getVerticalSnapPreference和calculateDtToFit方法。
當然這個滾動器很強大,如果你需要實現指定滾動速度還可以實現calculateSpeedPerPixel方法。