這個案例是利用豎向的RecyclerView和橫向的HorizontalScrollView 實(shí)現(xiàn)的一個可橫向和豎向滑動的view,
如果這個demo的思路幫到了你,請不要吝嗇你的Star 謝謝!
在模擬器上錄了一個效果圖,股票APP中常用的效果,下面來分析一下怎么去做這個
gif效果
整體布局如下
布局
其實(shí)看似很簡單,也有很多種解決辦法,但是我經(jīng)過試驗(yàn)之后發(fā)現(xiàn)這種是性能最好的,因?yàn)樽钔鈱邮莻€RecyclerView,無任何嵌套,ViewHolder可重復(fù)回收利用,界面絲滑,就是需要處理好手勢以及細(xì)節(jié)問題
這里面幾個比較難的部分,總結(jié)一下,3個難點(diǎn)問題
1.頭布局的HorizontalScrollView如何與下面RecyclerView的Item中HorizontalScrollView實(shí)現(xiàn)同步橫向滾動
2.當(dāng)觸摸下面RecyclerView的單個Item的HorizontalScrollView時,整個右面布局跟隨左右滑動
3.當(dāng)RecyclerView的右邊部分Item滑動到最左邊的時候,下拉刷新,部分item位置會錯位
帶著上面三個問題,逐個來解決
1.HorizontalScrollView的滑動監(jiān)聽,我們可以去看一下,它有一個onScrollChanged方法的回調(diào),
@Override
protected void onScrollChanged(int l,int t,int oldl,int oldt){
super.onScrollChanged(l,t,oldl,oldt);
}
首先處理觸摸頭部時來滾動下面部分的監(jiān)聽,對頭部的HorizontalScrollView進(jìn)行監(jiān)聽,在這個回調(diào)的方法里面想辦法去滾動item中的每一個HorizontalScrollView就可以了
2.第二個問題,就有點(diǎn)小復(fù)雜;由于下部分是個RecyclerView,就涉及到ViewHolder的緩存復(fù)用機(jī)制,RV的緩存筆記
我的做法是,將每一個ViewHolder保存下來,并在觸摸單個Item的HorizontalScrollView時,在其響應(yīng)的onScrollChanged方法中,滑動除觸摸之外的所有ViewHolder中的HorizontalScrollView
RecyclerView的ViewHolder中緩存ViewHolder
if(!mViewHolderList.contains(holder)){
mViewHolderList.add(holder);
}
監(jiān)聽當(dāng)前觸摸的item的viewholder中的HorizontalScrollView滾動,并遍歷所有緩存的viewholder,同步滾動其他item的HorizontalScrollView
holder.horItemScrollview.setOnCustomScrollChangeListener((listener,scrollX,scrollY,oldScrollX,oldScrollY)->{
for(inti=0;i<mViewHolderList.size();i++){
ItemViewHolder touchViewHolder=mViewHolderList.get(i);
if(touchViewHolder!=holder){
touchViewHolder.horItemScrollview.scrollTo(scrollX,0);
}
}
if(null!=onContentScrollListener)onContentScrollListener.onScroll(scrollX);**
offestX=scrollX;//記錄滾動的距離(增量)**
});
回到第一個問題,這時候就很好解決了,同樣的,遍歷viewholder集合,同步移動頭部HorizontalScrollView的移動距離
這里還有一個問題需要解決,當(dāng)RV可見部分被橫向滾動之后,再上下滾動外層的RV,新創(chuàng)建的ViewHolder需要做一個同步的左右滾動
監(jiān)聽外層RecyclerView的上下滾動,同步滾動內(nèi)部Item的橫向距離(在Item有橫向滾動的時候)
//滑動監(jiān)聽,同步滾動所有item
refreshRv.setOnSlideListener(newRefreshRecyclerView.OnSlideListener(){
@Override
publicvoidonSlide(){
}
@Override
publicvoidslideStop(){
}
@Override
publicvoidonScrolled(RecyclerViewrecyclerView,intdx,intdy){
List<StockSelectionResultAdapter.ItemViewHolder>viewHolderList=mStockSelectionResultAdapter.getViewHolderList();
if(null!=viewHolderList){
intsize=viewHolderList.size();
for(inti=0;i<size;i++){
viewHolderList.get(i).horItemScrollview.scrollTo(mStockSelectionResultAdapter.getOffestX(),0);
}
}
}
});
3.這個問題的最終原因,還是因?yàn)閂iewHolder的緩存導(dǎo)致,當(dāng)RV被下拉刷新之后,重置數(shù)據(jù),調(diào)用了notifyDataSetChanged,結(jié)合ViewHolder緩存,對數(shù)據(jù)進(jìn)行重新布局,但是當(dāng)我在viewholder的creat方法中無論怎么更改當(dāng)前holder持有的HorizontalScrollView的距離都不生效,還是錯位,而且會出現(xiàn)5個錯位,其他正常,我看了一下正好是那5個緩存的viewholder的HorizontalScrollView滾動距離設(shè)置之后沒有生效,我猜想可能是viewholder取出之后,還沒有進(jìn)行布局之前我設(shè)置了HorizontalScrollView的滾動距離,所以沒有生效,那么怎么監(jiān)聽viewholder的布局呢?
我找了一圈,只找到getViewTreeObserver().addOnGlobalLayoutListener這個,監(jiān)聽view的layout的listener,不知道還有沒有其他方法來監(jiān)聽這個viewholder從緩存中拿出來之后,在什么時機(jī)被layout完成
下面這段代碼在viewholder的creat方法中調(diào)用,即可解決下拉刷新之后錯位問題(這里我用了個flag記錄每個holder是否加載完,加載完之后在這里回調(diào),并設(shè)置flag為true,這樣可以避免這個listener被調(diào)用多次,算得上是個小技巧)
holder.horItemScrollview.getViewTreeObserver().addOnGlobalLayoutListener(()->{
if(!holder.isLayoutFinish()){
holder.horItemScrollview.scrollTo(offestX,0);
holder.setLayoutFinish(true);
}
});
作者:奮斗小青年Jerome
鏈接:http://www.reibang.com/p/75bba86dd61c
來源:簡書
著作權(quán)歸作者所有邑雅。商業(yè)轉(zhuǎn)載請聯(lián)系作者獲得授權(quán),非商業(yè)轉(zhuǎn)載請注明出處妈经。
安卓使用RecyclerView+HorizontalScrollView 實(shí)現(xiàn)Item整體橫向滑動 - 簡書