最近在開發(fā)的時候步势,遇到了需要通過代碼使得RecyclerView能夠滑到指定item頂部位置的需求丹诀,在查看源碼之后喝滞,發(fā)現(xiàn)RecyclerView已經(jīng)提供了實現(xiàn)滑動到指定位置的方法,下面是可實現(xiàn)方法:
//平滑滾動
recyclerView.smoothScrollToPosition(position);
//非平滑滾動
recyclerView.scrollToPosition(position);
LinearLayoutManager manager = (LinearLayoutManager)recyclerView.getLayoutManager();
//平滑滾動
manager.scrollToPosition(position)
當然异旧,除了上述方法以外涵卵,RecyclerView還有scrollBy莱褒、smoothScrollBy這兩個方法(RecyclerView不支持scrollTo)击困,可以實現(xiàn)滑動到指定位置,但是使用這三個方法滑動到對應(yīng)item位置广凸,需要計算item的高度或?qū)挾仍牟瑁瑢崿F(xiàn)起來過于復(fù)雜。
在LayoutManager中谅海,也包含了滾動的方法脸哀,且調(diào)用對應(yīng)的方法也能實現(xiàn)滾動:
實際上,RecyclerView的滑動方法扭吁,都是通過代理manager的滑動方法實現(xiàn)的撞蜂,下面是RecyclerView中smoothScrollToPosition(int position)的源碼:
/**
* Starts a smooth scroll to an adapter position.
* <p>
* To support smooth scrolling, you must override
* {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} and create a
* {@link SmoothScroller}.
* <p>
* {@link LayoutManager} is responsible for creating the actual scroll action. If you want to
* provide a custom smooth scroll logic, override
* {@link LayoutManager#smoothScrollToPosition(RecyclerView, State, int)} in your
* LayoutManager.
*
* @param position The adapter position to scroll to
* @see LayoutManager#smoothScrollToPosition(RecyclerView, State, int)
*/
public void smoothScrollToPosition(int position) {
if (mLayoutFrozen) {
return;
}
if (mLayout == null) {
Log.e(TAG, "Cannot smooth scroll without a LayoutManager set. "
+ "Call setLayoutManager with a non-null argument.");
return;
}
layoutManger.smoothScrollToPosition(this, mState, position);
}
雖然使用LayoutManager中的滾動方法,一樣可以實現(xiàn)滑動侥袜,但是一般情況下蝌诡,如果RecyclerView包含與LayoutManager同樣的方法,要優(yōu)先選擇使用RecyclerView中的方法枫吧。
好了浦旱,廢話不多說,下面以smoothScrollToPosition為例九杂,看看調(diào)用滑動方法的效果:
可以發(fā)現(xiàn)颁湖,點擊對應(yīng)按鈕調(diào)用滑動方法時代兵,有時會無效。這是因為默認情況下爷狈,如果item可見植影,調(diào)用滑動到該item的方法時,該方法將不執(zhí)行滑動涎永。這就說明思币,調(diào)用scrollToPosition或者smoothScrollToPosition并不能保證能夠滑到item的頂部。這就尷尬了羡微,這個問題該怎么解決呢谷饿?
查閱資料后,發(fā)現(xiàn)可以通過下面兩種方法解決:
1.LinearLayoutManger.scrollToPositionWithOffset(int position, int offset)##
該方法是在LinearLayoutManager中妈倔,其中offset為滑動偏移量博投,當設(shè)置offset為0時,會滑動position對應(yīng)的item的頂部盯蝴,此方法與scrollToPosition一樣毅哗,為非平滑滾動:
LinearLayoutManager manager = (LinearLayoutManager)recyclerView.getLayoutManager();
manager.scrollToPositionWithOffset(position, 0);
效果如下:
2.復(fù)寫LinearSmoothScoller的getVerticalSnapPreference()##
此解決辦法來源于stackOverFlow,只要復(fù)寫LinearSmoothScroller的getVerticalSnapPreference(),即可實現(xiàn)平滑滾動到指定item頂部捧挺。首先我們看看getVerticalSnapPreference()方法:
/**
* 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;
}
看上面的注釋虑绵,可以知道,SNAP_TO_START代表滑動到item頂部闽烙,因此只要使該方法固定返回SNAP_TO_START翅睛,就能實現(xiàn)滑動到item頂部:
package cn.axen.mvp.scroller
import android.content.Context
import android.support.v7.widget.LinearSmoothScroller
public class TopLinearSmoothScroller extends LinearSmoothScroller {
public TopLinearSmoothScroller(Context context) {
super(context)
}
@Override
public int getVerticalSnapPreference(): Int {
return SNAP_TO_START
}
}
最后,復(fù)寫LinearLayoutManager的smoothScrollToPosition方法黑竞,調(diào)用RecyclerView的smoothScrollToPosition方法:
LinearLayoutManager manager = new LinearLayoutManager(this) {
@Override
public void smoothScrollToPosition(RecyclerView view, RecyclerView.State state, int position) {
TopLinearSmoothScroller scroller = new TopLinearSmoothScroller(view.getContext());
scroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
};
recyclerView.smoothScrollToPosition(position);
效果如下:
參考博客:
RecyclerView自動滑動到指定的position
RecyclerView 滑動控制筆記
RecyclerView-How To smooth scroll to top of item on a certain position?