RecycleView滑動(dòng),頂部tab跟著變化位置,類似于淘寶詳情頁,效果是這樣的

1.實(shí)現(xiàn)思路
簡單講就是監(jiān)聽RecycleView的滑動(dòng),根據(jù)滑動(dòng)調(diào)整tab位置。相應(yīng)的監(jiān)聽tab點(diǎn)擊事件,滑動(dòng)recycleView到相應(yīng)位置。
2.滑動(dòng)RecycleView調(diào)整tab位置
2.1獲得滑動(dòng)位置
LayoutManager提供了獲得首個(gè)可見item的方法
int position = layoutManager.findFirstVisibleItemPosition();
2.2將滑動(dòng)位置轉(zhuǎn)換為類型位置
這里我item類型有普通類型和大類的標(biāo)題類型,通過instanceof判斷類型是否是我所屬的大類的標(biāo)題類型,然后根據(jù)大類標(biāo)題類型到頂部的距離判斷,再根據(jù)類型名稱進(jìn)行定位。
然后通過getTop獲得到頂部的距離,根據(jù)距離進(jìn)行分類間的跳轉(zhuǎn)。
int top = mRecyclerView.getChildAt(0).getTop();
2.3完整代碼
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
//不捕獲自動(dòng)滾動(dòng),非用戶觸摸產(chǎn)生的滑動(dòng)
if (isFormTabScroll) {
return;
}
//監(jiān)聽滑動(dòng)距離,改變tab選中位置
int position = layoutManager.findFirstVisibleItemPosition();
int realPosition = position - mAdapter.getHeaderSize();
//因?yàn)橛?個(gè)header所以>=-1
if (realPosition >= -1 && realPosition < mAdapter.getList().size()) {
//通過判斷分類卡片位置定位tab
if (mealCardIndxList.size() == 0) {//記錄大類的流量卡片位置
for (int i = 0; i < mAdapter.getList().size(); i++) {
if (mAdapter.getList().get(i) instanceof CardFlowMeal) {
mealCardIndxList.add(i);
}
}
}
if (mealCardIndxList.size() + 1 == indicator.getTitles().size()) {//防止下標(biāo)越界異常
//判斷當(dāng)前item所處類定位tab
for (int i = 0; i < mealCardIndxList.size(); i++) {
if (i == 0) {
if (realPosition >= -1 && realPosition < mealCardIndxList.get(i)) {
if (indicator.getCurrentPosition() != 0) {
indicator.setCurrentItem(0);
break;
}
}
} else if (realPosition >= mealCardIndxList.get(i - 1) && realPosition < mealCardIndxList.get(i)) {
if (indicator.getCurrentPosition() != i) {
indicator.setCurrentItem(i);
break;
}
}
//可能會有i==0的情況下屬于如下情況 所以不加else if
if (i == mealCardIndxList.size() - 1) {
if (realPosition >= mealCardIndxList.get(i)) {
if (indicator.getCurrentPosition() != i + 1) {
indicator.setCurrentItem(i + 1);
}
}
}
}
}
}
}
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
switch (newState) {
case RecyclerView.SCROLL_STATE_DRAGGING:
isFormTabScroll = false;
break;
case RecyclerView.SCROLL_STATE_IDLE:
if (flowDetailActivity != null && !flowDetailActivity.isCloseNps()) {
UIUtils.postDelayRunnable(new Runnable() {
@Override
public void run() {
if (!flowDetailActivity.isShowNps()) {
flowDetailActivity.showNPS();
}
}
}, 100);
}
break;
default:
break;
}
}
});
3.點(diǎn)擊tab位置RecycleView滑動(dòng)到相應(yīng)位置
通過RecycleView的scrollBy()和scrollToPosition()兩個(gè)方法實(shí)現(xiàn)。為scrollToPosition()只會保證滑動(dòng)的position出現(xiàn)在視野中,不會保證該position在頂端,所以需要通過scrollBy()完成置頂?shù)幕瑒?dòng)。
public void moveRecycleViewToPosition(int n) {
isFormTabScroll = true;
//先從RecyclerView的LayoutManager中獲取第一項(xiàng)和最后一項(xiàng)的Position
int firstItem = layoutManager.findFirstVisibleItemPosition();
int lastItem = layoutManager.findLastVisibleItemPosition();
//然后區(qū)分情況
if (n < firstItem) {
//當(dāng)要置頂?shù)捻?xiàng)在當(dāng)前顯示的第一個(gè)項(xiàng)的前面時(shí)
mRecyclerView.scrollToPosition(n);
if (n != 0) {
mIndex = n;
needMoveToTop = true;
}
} else if (n <= lastItem) {
//當(dāng)要置頂?shù)捻?xiàng)已經(jīng)在屏幕上顯示時(shí)
int top = mRecyclerView.getChildAt(n - firstItem).getTop();
mRecyclerView.scrollBy(0, top);
} else {
//當(dāng)要置頂?shù)捻?xiàng)在當(dāng)前顯示的最后一項(xiàng)的后面時(shí)
mRecyclerView.scrollToPosition(n);
//這里這個(gè)變量是用在RecyclerView滾動(dòng)監(jiān)聽里面的
mIndex = n;
needMoveToTop = true;
}
}
可以看到在該position不在屏幕中時(shí),指定了一個(gè)字段needMoveToTop = true,因?yàn)樾枰?code>scrollToPosition()方法后再執(zhí)行scrollBy()完成置頂操作。這個(gè)標(biāo)記是監(jiān)聽RecycleView滑動(dòng)完成之后用來標(biāo)識完成scrollBy()最后置頂滑動(dòng)的,具體代碼如下:
mRecyclerView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
@Override
public void onScrollChanged() {
//將改item滑動(dòng)到頂部
if (needMoveToTop) {
needMoveToTop = false;
//獲取要置頂?shù)捻?xiàng)在當(dāng)前屏幕的位置,mIndex是記錄的要置頂項(xiàng)在RecyclerView中的位置
int n = mIndex - layoutManager.findFirstVisibleItemPosition();
if (0 <= n && n < mRecyclerView.getChildCount()) {
//獲取要置頂?shù)捻?xiàng)頂部離RecyclerView頂部的距離
int top = mRecyclerView.getChildAt(n).getTop();
//最后的移動(dòng)
mRecyclerView.scrollBy(0, top);
}
}
}
});
總體實(shí)現(xiàn)難度并不大,至此就完成了滑動(dòng)錨點(diǎn)定位的邏輯。