需求
城市列表快速定位:通過右邊的地區(qū)字母快速導(dǎo)航到特定位置
通訊錄的快速定位
分析思路
網(wǎng)上查看的文章說:分三種情況,即屏幕上方合砂、屏幕中青扔、屏幕下方;分別處理代碼邏輯翩伪;
實(shí)際分析后發(fā)現(xiàn)完全可以使用比較簡單的方式實(shí)現(xiàn):
默認(rèn)API
RecyclerView 提供了3種方法用于滑動到特定位置的API
API | 區(qū)別 |
---|---|
scrollBy(int x, int y) |
根據(jù)x赎懦、y軸的距離,滑動 |
smoothScrollToPosition(int position) |
平滑滾動到特定 position |
scrollToPosition(int position) |
滾動到特定position |
但是通過具體代碼的實(shí)踐:均不能達(dá)到預(yù)期效果
-
scrollBy()
方法可以通計(jì)算 child.getTop() 獲得其需要滑動的距離幻工,但是如果 child 在屏幕之外励两,需要的 child 未創(chuàng)建,無法獲得 -
scrollToPosition(int position)
囊颅、smoothScrollToPosition(int position)
方法可以通過 position 滑動到特定下標(biāo)当悔,但是有個特點(diǎn):- position < firstViewItemPosition,則列表向下滾動 目標(biāo) position 滾動到頂部位置
- firstVisibleItemPosition < position < lastVisibleItemPosition :列表不會滾動
- position > lastVisibleItemPosition : 列表向上滾動踢代, 目標(biāo)position 滾動到屏幕底部位置
可見均不能達(dá)到我們的預(yù)期盲憎;
那我們?nèi)シ治鲆幌?smoothScrollToPosition() 的具體實(shí)現(xiàn),看下我們是否可以干預(yù)一下其滾動規(guī)則
源碼分析
// RecyclerView.java
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;
}
// @VisibleForTesting LayoutManager mLayout;
// 這里調(diào)用了 LayouManager 去實(shí)現(xiàn)滾動胳挎。
mLayout.smoothScrollToPosition(this, mState, position);
}
// LayoutManager 類
public void smoothScrollToPosition(RecyclerView recyclerView, State state, int position) {
// LayoutManager 中并沒有實(shí)現(xiàn)代碼饼疙,而是由 我們設(shè)置的 其子類來具體的實(shí)現(xiàn)的
Log.e(TAG, "You must override smoothScrollToPosition to support smooth scrolling");
}
// LinearLayoutManager 類
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
// 滾動控制器
LinearSmoothScroller linearSmoothScroller = new LinearSmoothScroller(recyclerView.getContext());
// 設(shè)置滾動目標(biāo) position
linearSmoothScroller.setTargetPosition(position);
// 由 LineraManager 調(diào)用
startSmoothScroll(linearSmoothScroller);
}
// LayoutManager 類
public void startSmoothScroll(SmoothScroller smoothScroller) {
// 數(shù)據(jù)以及狀態(tài)的校驗(yàn)
if (mSmoothScroller != null && smoothScroller != mSmoothScroller && mSmoothScroller.isRunning()) {
mSmoothScroller.stop();
}
mSmoothScroller = smoothScroller;
// 觸發(fā)滾動
mSmoothScroller.start(mRecyclerView, this);
}
到這里,我們可以發(fā)現(xiàn)慕爬,RecyclerView 的滾動觸發(fā)是由 LayoutManager 調(diào)用窑眯,其控制器的定義是由具體的 Manager 來設(shè)置;
繼續(xù)來分析 SmoothScroller 的源碼
// 抽象類
public static abstract class SmoothScroller{}
/**
* 線性平滑滾動控制器医窿。
*/
public class LinearSmoothScroller extends RecyclerView.SmoothScroller {
// 其他源碼 ...
/**
* 當(dāng)滾動到子視圖時,這種方法定義了:child 與 parent 是否應(yīng)該左對齊或右對齊磅甩。
*
* When scrolling towards a child view, this method defines whether we should align the left
* or the right edge of the child with the parent RecyclerView.
*
* @return SNAP_TO_START, SNAP_TO_END or SNAP_TO_ANY;
*/
protected int getHorizontalSnapPreference() {
return mTargetVector == null || mTargetVector.x == 0 ? SNAP_TO_ANY :
mTargetVector.x > 0 ? SNAP_TO_END : SNAP_TO_START;
}
/**
* 當(dāng)滾動到子視圖時,這種方法定義了:child 與 parent 是否應(yīng)該頂對齊或底對齊。
*
* 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;
*/
protected int getVerticalSnapPreference() {
return mTargetVector == null || mTargetVector.y == 0 ? SNAP_TO_ANY :
mTargetVector.y > 0 ? SNAP_TO_END : SNAP_TO_START;
}
// 其他源碼 ...
}
通過注釋就可以看出:上面兩個方法控制了 childView 的對齊方式姥卢,且是 protected 修飾的方法卷要,那我們完全可以通過自定義返回對齊方式
具體實(shí)現(xiàn)
// 定義滾動控制器
private LinearSmoothScroller smoothScroller;
smoothScroller= new LinearSmoothScroller(inflater.getContext()) {
// 這里考慮的是垂直列表
@Override
protected int getVerticalSnapPreference() {
// 固定返回頂對齊方式
return SNAP_TO_START;
}
};
// 設(shè)置滾動目標(biāo) position
smoothScroller.setTargetPosition(index);
// 主動觸發(fā)滾動
recyclerView.getLayoutManager().startSmoothScroll(smoothScroller);
至此渣聚,即可實(shí)現(xiàn)文頭的需求方案;
效果圖
recyclerView導(dǎo)航效果圖.gif