前言
產(chǎn)品讓做一個(gè)仿京東金融的效果,在網(wǎng)上搜了搜,就找到一個(gè)通過重寫ViewPage實(shí)現(xiàn)聯(lián)動(dòng)的案例,但效果不理想.最后實(shí)在沒找到適合參考的案例,所以記錄下來,希望能給有同樣需求的童鞋一些幫助.
首先感謝我的同事ChenWei,沒有他的幫助番枚,絕對(duì)達(dá)不到現(xiàn)在的效果审轮。里面好多難點(diǎn)都是他搞定的。
技術(shù)難點(diǎn)
1欧宜、兩個(gè)ViewPager的聯(lián)動(dòng)
2坐榆、滑動(dòng)任一列表時(shí),headerVp和其他列表都跟著滑動(dòng)
3、切換頁(yè)面時(shí)所有列表回到初始位置
4冗茸、有個(gè)頁(yè)面有懸停吸頂欄,我們是用CoordinatorLayout+AppBarLayout+RecyclerView利用behavior實(shí)現(xiàn)的,怎么獲取此頁(yè)面滑動(dòng)的距離席镀、怎么讓其跟隨別的列表滑動(dòng)及怎么歸位
一、兩個(gè)ViewPager的聯(lián)動(dòng)
布局如下:
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="@+id/tabLayout"
android:clipChildren="false"
>
<android.support.v4.view.ViewPager
android:id="@+id/body_vp"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
<android.support.v4.view.ViewPager
android:id="@+id/header_vp"
android:layout_width="match_parent"
android:layout_height="160dp"
android:layout_marginLeft="30dp"
android:layout_marginRight="30dp"
android:clipChildren="false"
/>
</RelativeLayout>
既然要聯(lián)動(dòng),兩個(gè)ViewPager自然就要相互監(jiān)聽.
bodyVp.addOnPageChangeListener(new BaseLinkPageChangeListener(bodyVp, headerVp) {
@Override public void onPageSelected(int position) {
super.onPageSelected(position);
pageScrollToTop();
}
});
headerVp.addOnPageChangeListener(new BaseLinkPageChangeListener(headerVp, bodyVp) {
@Override public void onPageSelected(int position) {
super.onPageSelected(position);
tabLayout.onPageSelected(position);
}
});
在ViewPager滑動(dòng)過程中,如上圖所示,滑動(dòng)完整一頁(yè)時(shí)bodyVp滑動(dòng)距離為屏幕寬度screenWidth,而headerVp滑動(dòng)距離為headerVp自身寬度再加上右邊那個(gè)白色的marging值(為headerWidth + margin),那么當(dāng)bodyVp滑動(dòng)距離為bodyX時(shí),headVp滑動(dòng)距離headerX就為bodyX / screenWidth * (headerWidth + margin);
然后調(diào)用headerVp.scrollTo(headerX, 0),headerVp就能跟隨滑動(dòng)了.
下面是封裝的OnPagerChangeLIstener:
public class BaseLinkPageChangeListener implements ViewPager.OnPageChangeListener {
private ViewPager linkViewPager;
private ViewPager selfViewPager;
private int pos;
public BaseLinkPageChangeListener(ViewPager selfViewPager, ViewPager linkViewPager) {
this.linkViewPager = linkViewPager;
this.selfViewPager = selfViewPager;
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
int marginX = ((selfViewPager.getWidth() + selfViewPager.getPageMargin()) * position
+ positionOffsetPixels) * (linkViewPager.getWidth() + linkViewPager.getPageMargin()) / (
selfViewPager.getWidth()
+ selfViewPager.getPageMargin());
if (linkViewPager.getScrollX() != marginX) {
linkViewPager.scrollTo(marginX, 0);
}
}
@Override public void onPageSelected(int position) {
this.pos=position;
}
@Override public void onPageScrollStateChanged(int state) {
if (state == ViewPager.SCROLL_STATE_IDLE) {
linkViewPager.setCurrentItem(pos);
}
}
}
ps
其實(shí)headerVp如果主要用來展示數(shù)據(jù),沒有復(fù)雜的觸摸操作的話,可以吧bodyVp放在上層,只是將headerVp占用的那塊空間用透明布局填充.這時(shí)無論是滑動(dòng)headerVp,還是bodyVp其實(shí)都是在滑動(dòng)bodyVp,這樣只需讓headerVp跟著bodyVp滑動(dòng)就行了.
二夏漱、滑動(dòng)任一列表時(shí),headerVp和其他列表都跟著滑動(dòng)
首先說一點(diǎn),headerVp和另外的列表隨著當(dāng)前頁(yè)面向上滑動(dòng)時(shí),如果headerVp不可見了,他們將不會(huì)再向上滑動(dòng).
1.計(jì)算當(dāng)前l(fā)ist滑動(dòng)距離
- scrollView:
scrollView有個(gè)onScrollChanged()方法,我是寫了一個(gè)類繼承NestedScrollView,
然后重寫onScrollChanged()方法,再把top暴露出去.
@Override protected void onScrollChanged(int l, int t, int oldl, int oldt) {
super.onScrollChanged(l, t, oldl, oldt);
if (this.onScrollChangedListener != null) {
onScrollChangedListener.onScrollChanged(t, oldt);
}
}
top就是scrollView頂部的y坐標(biāo)值.
在scrollView界面就可以監(jiān)聽到滑動(dòng)的距離了
scrollView.setOnScrollChangedListener(new OnScrollChangedListener() {
@Override public void onScrollChanged(int top, int oldTop) {
if (isPageVisible()) {
((MainActivity) getActivity()).pageScrollTo(Math.min(top, maxScrollDisY()));
}
}
});
- recycleVIew
直接用recyclerView.computeVerticalScrollOffset()就可以獲取recycleView移動(dòng)的距離了.
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
if (isPageVisible()) {
if (layoutManager.findFirstVisibleItemPosition() == 0) {
((MainActivity) getActivity()).pageScrollTo(recyclerView.computeVerticalScrollOffset());
} else {
((MainActivity) getActivity()).pageScrollTo(maxScrollDisY());
}
}
}
});
- CoordinatorLayout+AppBarLayout+RecyclerView
這時(shí)通過監(jiān)聽appBarLayout來獲取距離
appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
if (isPageVisible()) {
((MainActivity) getActivity()).pageScrollTo(
Math.min(Math.abs(verticalOffset), maxScrollDisY()));
}
}
});
2. 跟隨移動(dòng)
- headerVp直接調(diào)用headerVp.setTranslationY(-distance);
- scrollView調(diào)用scrollView.scrollTo(0, disY);
- recycleView用layoutManager.scrollToPositionWithOffset(0, -disY);
- layoutManager.scrollToPositionWithOffset(0, -disY);
coordinatorLayout.scrollTo(0, disY);
三豪诲、切換頁(yè)面時(shí)所有列表回到初始位置
scrollView和recycleView比較簡(jiǎn)單,重點(diǎn)說下coordinateLayout的那種情況.
@Override public void pageScrollToTop() {
layoutManager.scrollToPositionWithOffset(0, 0);
appBarLayout.setExpanded(true);
coordinatorLayout.scrollTo(0, 0);
}
coordinateLayout和recycleView和appBarLayout必須都要調(diào)用對(duì)應(yīng)的方法才能完成復(fù)位.尤其是appBarLayout很容易忽略.
總結(jié)
由于篇幅原因,還有很多細(xì)節(jié)沒有展開講,感興趣的童鞋就麻煩看下源碼吧...