Recycleview雙列表聯(lián)動與懸停

預覽.png

前言
==

最近看了別人的一篇blog,也是實現(xiàn)recycleview的雙列表聯(lián)動钧椰,同時應用了MVP框架到千。于是就模仿寫了一個類似的雙列表聯(lián)動與懸停。在MVP方面齐邦,我仿照的是官方的todo-mvp椎侠,感覺寫得有點不倫不類了,這里就不詳述措拇,另外在實現(xiàn)需求方面我纪,和那個大神相比,也做了許多改變丐吓,當然有些具體的難點我沒想到浅悉,參照了他的思路,然后實現(xiàn)出來了券犁。在開發(fā)中术健,也嘗試了其他的方法:
1.在點擊左邊省份時,若右邊的省份在下面不可見區(qū)域粘衬,會出現(xiàn)bug荞估,后面會詳述。
2.滑動右邊城市區(qū)域是稚新,想和左邊的省份聯(lián)動勘伺,當時也用了其他方法,但是失敗了褂删,后面也會具體講到飞醉。
最后想說一點:看別人的代碼要有耐心,剛開始看的時候也是一臉懵逼屯阀,后面靜下心來慢慢看缅帘,看到后面就覺得很簡單了轴术。多看,多寫钦无,多嘗試逗栽,多思考。

實現(xiàn)的功能

先直接上一個效果圖:

show.gif

然后再來分析實現(xiàn)的功能:
1.點擊左邊省份铃诬,省份背景改變祭陷,右邊頂部顯示省份懸停,下面顯示省份的城市趣席;
2.滑動右邊的城市兵志,頂部省份懸停,左邊隨著省份的改變而改變宣肚;
3.所有控件可點擊想罕;
4.仿官方mvpdemo的mvp框架;
5.snackbar的使用霉涨;
6.recycleview的花式使用按价。

下面在來分析下實現(xiàn)的思路:

思路

之前就說了思路是非常重要的,下面來詳細說說如果有一個這種需求笙瑟,改從何下手:
先看效果分析大概:
1:可以把左邊和右邊的布局各設置為一個Recycleview楼镐;
2:點擊左邊省份,右邊需要跟著滑動往枷,是不是可以計算需要滑動的距離框产,然后通過recycleview的方法進行指定滑動呢。只是一種猜想错洁,實際比這復雜一點秉宿;
3:滑動右邊的城市,然后左邊省份需要跟著滑動屯碴,更改背景顏色描睦,試想下,通過判斷滑動的position导而,然后計算滑動到了哪個省份忱叭,進行改變呢。

接下來我再來詳細說下實現(xiàn)的過程:

1.通過RecycleView加載左邊的省份今艺,省份是在一個String-array下定義的窑多,然后獲取資源,通過適配器加載出來洼滚;
2.通過RecycleView加載右邊的數(shù)據(jù),這個時候要注意了技潘,因為左邊數(shù)據(jù)和右邊數(shù)據(jù)是對應的遥巴,所以我們通過遍歷String-array下的省份千康,然后往不同的省份注入不同的城市,從效果可以看出來铲掐,我們右邊不只是簡單的布局拾弃,可以看出來上面有一個title(省份),下面是顯示content(城市)摆霉,我們通過在設置城市數(shù)據(jù)時候給一個isTitle來區(qū)分省份和城市豪椿,后面布局也通過isTitle來區(qū)分,我們可以看出右邊省份是一行只有一個數(shù)據(jù)携栋,而城市有三個數(shù)據(jù)搭盾。然后通過不同的布局顯示出來。到這里婉支,我們的將數(shù)據(jù)顯示出來了鸯隅。
3.點擊省份,背景顏色改變向挖,就是講點擊的item設置成你想要的顏色蝌以,沒有點擊的就是其他顏色了,通過position判斷何之,右邊的城市需要滑動跟畅,主要通過計算滑動的position。比較麻煩溶推,后面具體講徊件。
4.滑動右邊的數(shù)據(jù),前面我們在加載城市數(shù)據(jù)的時候悼潭,我們將城市和省份通過一個tag進行了綁定庇忌,當我們滑動的時候,獲取這個tag(position),讓他與左邊的position比較舰褪,不相同的話皆疹,就把tag賦值給position,有了這個position占拍,我們就可以更改背景顏色了略就;
5.由于滑動的時候會出現(xiàn)bug,我們將左邊選中的省份一直顯示在屏幕中間晃酒。通過recycleView設置下就可以表牢。
6.所有都可點擊,recycleView的點擊事件屬于RecycleView的基礎贝次,不清楚的可以看我的一個demo:

https://github.com/Simon986793021/RecycleView

實現(xiàn)過程

實現(xiàn)過程也按照思路來

1.加載左邊城市的數(shù)據(jù):

通過RecycleView加載左邊的省份崔兴,省份是在一個String-array下定義的,然后獲取資源,通過適配器加載出來敲茄;

  String [] province=getResources().getStringArray(R.array.province);//獲取省份
        final List<String> list= Arrays.asList(province);
        /*
        適配數(shù)據(jù)和設置監(jiān)聽事件
         */
        adapter=new ProvinceRvAdapter(this, list, new ItemClickListener() {
            @Override
            public void onItemClick(View view, int position) {
                Utils.showSnackBar(recycleview,list.get(position));
                mposition=position;
                startMove(position,true);
                Log.i(">>>>>>","position:"+position);
                moveToCenter(position);
            }
        });
        recycleview.setAdapter(adapter);

2.加載右邊城市數(shù)據(jù):

通過RecycleView加載右邊的數(shù)據(jù)位谋,這個時候要注意了,因為左邊數(shù)據(jù)和右邊數(shù)據(jù)是對應的堰燎,所以我們通過遍歷String-array下的省份掏父,然后往不同的省份注入不同的城市,從效果可以看出來秆剪,我們右邊不只是簡單的布局赊淑,可以看出來上面有一個title(省份),下面是顯示content(城市)仅讽,我們通過在設置城市數(shù)據(jù)時候給一個isTitle來區(qū)分省份和城市陶缺,后面布局也通過isTitle來區(qū)分,我們可以看出右邊省份是一行只有一個數(shù)據(jù)何什,而城市有三個數(shù)據(jù)组哩。然后通過不同的布局顯示出來。到這里处渣,我們的將數(shù)據(jù)顯示出來了伶贰。

1.先看下bean對象:

public class CityBean {
    public String city;
    public boolean isTitle;//判斷是否為省份,來進行加載數(shù)據(jù)
    public String province;
    public String tag;//一個position罐栈,同時將城市與省份綁定
    public void setTitle(boolean title)
    {
        isTitle=title;
    }
    public void setProvince (String province)
    {
        this.province=province;

    }
    public String getProvince()
    {
        return province;
    }
    public boolean isTitle()
    {
        return  isTitle;
    }
    public void setCity(String city)
    {
        this.city=city;
    }
    public String getCity()
    {
        return city;
    }
    public void setTag(String tag)
    {
        this.tag=tag;
    }
    public String getTag()
    {
        return tag;
    }
}

2.獲取數(shù)據(jù)源:
通過遍歷省份黍衙,給省份添加城市數(shù)據(jù);

  for (int i=0;i<province.length;i++)
            {
                CityBean titleBean=new CityBean();
                titleBean.setProvince(province[i]);
                titleBean.setTitle(true);//設置為title
                titleBean.setTag(String.valueOf(i));//設置tag荠诬,方便獲取position
                list.add(titleBean);

                for (int j=0;j<citylist.get(i).length;j++)
                {
                    CityBean cityBean=new CityBean();
                    cityBean.setCity(citylist.get(i)[j]);
                    cityBean.setTag(String.valueOf(i));//設置成和省份一樣的tag琅翻,將省份與城市綁定。
                    list.add(cityBean);
                }


            }

3.通過設置的isTitle與否來加載數(shù)據(jù):

 int itemViewTtpe=CityRvAdapter.this.getItemViewType(position);
            switch (itemViewTtpe)
            {
                case 0://省份
                    title.setText(list.get(position).getProvince());
                    break;
                case 1://城市
                    city.setText(list.get(position).getCity());
                    break;
                case 2:
                    break;
            }

具體就不詳細講柑贞,代碼中也有方椎。

左邊聯(lián)動右邊(省份聯(lián)動城市)

點擊省份,背景顏色改變钧嘶,就是講點擊的item設置成你想要的顏色棠众,沒有點擊的就是其他顏色了,通過position判斷有决,右邊的城市需要滑動闸拿,主要通過計算滑動的position。

1.背景的改變:

獲取點擊的position书幕,傳到adapter中新荤,然后進行判斷,進行背景改變:

貼出關(guān)鍵代碼:

 if (position==clickPositon)
            {
                view.setBackgroundColor(Color.parseColor("#9EABF4"));
                textView.setTextColor(Color.parseColor("#ffffff"));
            }
            else {
                view.setBackgroundColor(Color.parseColor("#00FFFFFF"));//設置為透明的台汇,因為白色會覆蓋分割線
                textView.setTextColor(Color.parseColor("#1e1d1d"));
            }
            textView.setText(s);
        }

2.左邊聯(lián)動右邊:

建議先看這篇blog苛骨,滑動定位的解決方案:
http://blog.csdn.net/tyzlmjj/article/details/49227601

通過計算需要滑動的距離來進行滑動

我們先計算需要滑動的position:

 for (int i=0;i<position;i++)//position 為點擊的position
        {
            Log.i("<<<<<<",i+":"+cityFragment.citylist.get(i).length);
            counts+=cityFragment.citylist.get(i).length;//計算需要滑動的城市數(shù)目
        }
        if (isLeft)
        {
            cityFragment.setCounts(counts+position);//加上title(省份)數(shù)目
        }

官方提供了兩種滑動方案:
1.scrollToPosition(int)
滑動到指定的item

2.scrollBy(int x,int y)
滑動到指定的距離

一開始用的scrollToPosition(int)篱瞎,但是在實際開發(fā)終于到了問題

scrollToposition 只能將item顯示出來,至于顯示在哪里他就不管了智袭,不過有一點可以肯定的奔缠,若item從不可見滑動到可見,一般會出現(xiàn)在最底部吼野,而我們需要的是在最頂部,顯然是不行的两波。我們可以通過用scrollToPosition()和scrollBy 結(jié)合使用瞳步。

我們可以將滑動分為三種情況:
第一種:從上往下滑動(目標item不可見),這種最復雜腰奋,需要scrollToPosition()和scrollBy 結(jié)合使用单起,監(jiān)聽scroll接口;

第二種:從上往下滑動(目標item可見)劣坊,scrollBy就可以解決嘀倒;

第三種:從下往上滑動,目標可見不可見一樣局冰,調(diào)用scrollToPosition()都會顯示在頂部;

貼出具體代碼與我嘗試的其他方法(在注釋中)

 int firstItem=gridLayoutManager.findFirstVisibleItemPosition();//獲取屏幕可見的第一個item的position
        int lastItem=gridLayoutManager.findLastVisibleItemPosition();//獲取屏幕可見的最后一個item的position
        if (moveCounts<firstItem)
        {
            recyclerView.scrollToPosition(moveCounts);
        }
        else if (moveCounts<lastItem)
        {
            View aimsView=recyclerView.getChildAt(moveCounts-firstItem);
            int top =aimsView.getTop();
            recyclerView.scrollBy(0,top);
        }
        else {
            /*
            當往下滑動的position大于可見的最后一個item的時候测蘑,調(diào)用 recyclerView.scrollToPosition(moveCounts);
            只能講item滑動到屏幕的底部。
             */
            /*
            第一種方案:先將item移動到底部康二,然后在調(diào)用scrollBy移動到頂部碳胳。不可行,不能講item滑動到頂部沫勿,
            離上面還有一小段距離挨约;
             recyclerView.scrollToPosition(moveCounts);
            int top=recyclerView.getHeight();
            recyclerView.scrollBy(0,top);

            第二種方案:直接計算要滑動的距離。程序崩潰产雹,報空指針诫惭。看系統(tǒng)源碼可知蔓挖,當
            滑動的距離大于ChildCount(可見的item數(shù)目)夕土,將返回空。
            int top=recyclerView.getChildAt(moveCounts-firstItem).getTop();
            recyclerView.scrollBy(0,top);

            第三種解決方案:先將目標item滑動到底部时甚,然后進行異步處理隘弊。調(diào)用滾動監(jiān)聽方法RecyclerViewListener,滑動到頂部荒适。

             */

//            int top=recyclerView.getHeight();
//            recyclerView.scrollBy(0,top);
//            int childcount=recyclerView.getChildCount();
//            Log.i("<<<<<<<<<<","childcount"+childcount);
//            int top=recyclerView.getChildAt(moveCounts-firstItem).getTop();
//            recyclerView.scrollBy(0,top);

            recyclerView.scrollToPosition(moveCounts);
            move=true;
        }

監(jiān)聽回調(diào)梨熙,從底部滑動到頂部:

class RecyclerViewListener extends RecyclerView.OnScrollListener{
        /*
        監(jiān)聽回調(diào),滑動結(jié)束回調(diào)刀诬。
         */
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //在這里進行第二次滾動(最后的100米Q噬取)
            if (move ){
                move = false;
                //獲取要置頂?shù)捻椩诋斍捌聊坏奈恢眯安疲琺oveCount是記錄的要置頂項在RecyclerView中的位置
                int n = moveCounts - gridLayoutManager.findFirstVisibleItemPosition();
                if ( 0 <= n && n < recyclerView.getChildCount()){
                    //獲取要置頂?shù)捻楉敳侩xRecyclerView頂部的距離
                    int top = recyclerView.getChildAt(n).getTop();
                    //最后的移動
                    recyclerView.scrollBy(0, top);
                }
            }
        }
            /*
            監(jiān)聽回調(diào),滑動狀態(tài)改變回調(diào)
             */
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

            if (move&&newState==RecyclerView.SCROLL_STATE_IDLE)
            {
                move=false;
                int n=moveCounts-gridLayoutManager.findFirstVisibleItemPosition();
                if (0<=n&&n<recyclerView.getChildCount())
                {
                    int top=recyclerView.getChildAt(n).getTop();
                    recyclerView.scrollBy(0,top);
                }
            }
        }

這樣我們就講左邊聯(lián)動右邊完成了质欲。

右邊聯(lián)動左邊

滑動右邊的數(shù)據(jù)树埠,前面我們在加載城市數(shù)據(jù)的時候,我們將城市和省份通過一個tag進行了綁定嘶伟,當我們滑動的時候怎憋,獲取這個tag(position),讓他與左邊的position比較,不相同的話九昧,就把tag賦值給position绊袋,有了這個position,我們就可以更改背景顏色了铸鹰;

貼出關(guān)鍵代碼:

 if (!TextUtils.equals(tag, currentTag)) {
            currentTag = tag;
            Log.i("zhangcong",currentTag);
            Integer integer = Integer.valueOf(currentTag);
            mCheckListener.check(integer, false);
        }

最后我們調(diào)用一個接口回調(diào)方法癌别,讓左邊的省份背景改變。

小bug

由于右邊滑動的時候左邊省份背景改變了蹋笼,但是不在可見view中展姐,我們將左邊選中的省份一直顯示在屏幕中間。通過recycleView設置下就可以剖毯。

代碼:

//將當前選中的item居中
    public   void moveToCenter(int position) {
        //將點擊的position轉(zhuǎn)換為當前屏幕上可見的item的位置以便于計算距離頂部的高度圾笨,從而進行移動居中
        Log.i(">>>>>>>>>",position - manager.findFirstVisibleItemPosition()+"eeeee");
        int itemPosition=position-manager.findFirstVisibleItemPosition();
        /*
        當往上滑動太快,會出現(xiàn)itemPosition為-1的情況速兔。做下判斷
         */
        if (0<itemPosition&&itemPosition<manager.getChildCount())
        {
            View childAt = recycleview.getChildAt(position - manager.findFirstVisibleItemPosition());
            Log.i("<<<<<<",position - manager.findFirstVisibleItemPosition()+"");

            int y = (childAt.getTop() - recycleview.getHeight() / 2);
            Log.i("<<<<<<",childAt.getTop()+"ssssss");
            Log.i("<<<<<<", y+"");
            recycleview.smoothScrollBy(0, y);
        }

    }

遇到的問題

在做右邊聯(lián)動左邊的時候墅拭,當時想著獲取title的position,然后將position傳過去涣狗,進行背景的改變谍婉,這種方法是可行的,但是體驗非常的不好镀钓,因為當你滑動很快的時候穗熬,是獲取不到中間經(jīng)過的省份的position,就感覺不連貫丁溅,直接跳到了最后你滑動的位置唤蔗,所以換了一種解決思路(如果你以為是我想出來的你就太年輕了)。

總結(jié)

總結(jié)的話就沒有窟赏,前面思路講的已經(jīng)夠清楚了妓柜。最后給大家一點看代碼的興趣,這里面還缺少了許多的城市和省份涯穷,大家看懂了這些代碼的話可以在里面加上自己的城市棍掐,或者加上圖片展示功能;然后提交pull request拷况,我這邊會幫你們merge的(請在添加代碼的地方加上你自己的注釋 如: Simon add shanghai city on 20170727)作煌。也非常歡迎大家在底下留言掘殴,探討一些問題,當然有看不明白的也可以提出問題粟誓,我會盡力解答的奏寨。大家覺得有幫助的話麻煩給我的github來一個star吧。

最后是github地址

https://github.com/Simon986793021/WindLinkRecycleView

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末鹰服,一起剝皮案震驚了整個濱河市病瞳,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌获诈,老刑警劉巖仍源,帶你破解...
    沈念sama閱讀 221,635評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異舔涎,居然都是意外死亡,警方通過查閱死者的電腦和手機逗爹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,543評論 3 399
  • 文/潘曉璐 我一進店門亡嫌,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人掘而,你說我怎么就攤上這事挟冠。” “怎么了袍睡?”我有些...
    開封第一講書人閱讀 168,083評論 0 360
  • 文/不壞的土叔 我叫張陵知染,是天一觀的道長。 經(jīng)常有香客問我斑胜,道長控淡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,640評論 1 296
  • 正文 為了忘掉前任硝烂,我火速辦了婚禮筷转,結(jié)果婚禮上梆砸,老公的妹妹穿的比我還像新娘。我一直安慰自己涧狮,他們只是感情好,可當我...
    茶點故事閱讀 68,640評論 6 397
  • 文/花漫 我一把揭開白布么夫。 她就那樣靜靜地躺著者冤,像睡著了一般。 火紅的嫁衣襯著肌膚如雪档痪。 梳的紋絲不亂的頭發(fā)上涉枫,一...
    開封第一講書人閱讀 52,262評論 1 308
  • 那天,我揣著相機與錄音钞它,去河邊找鬼拜银。 笑死殊鞭,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的尼桶。 我是一名探鬼主播操灿,決...
    沈念sama閱讀 40,833評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼泵督!你這毒婦竟也來了趾盐?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,736評論 0 276
  • 序言:老撾萬榮一對情侶失蹤小腊,失蹤者是張志新(化名)和其女友劉穎救鲤,沒想到半個月后,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秩冈,經(jīng)...
    沈念sama閱讀 46,280評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡本缠,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,369評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了入问。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片丹锹。...
    茶點故事閱讀 40,503評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖芬失,靈堂內(nèi)的尸體忽然破棺而出楣黍,到底是詐尸還是另有隱情,我是刑警寧澤棱烂,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布租漂,位于F島的核電站,受9級特大地震影響颊糜,放射性物質(zhì)發(fā)生泄漏哩治。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,870評論 3 333
  • 文/蒙蒙 一芭析、第九天 我趴在偏房一處隱蔽的房頂上張望锚扎。 院中可真熱鬧,春花似錦馁启、人聲如沸驾孔。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,340評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽翠勉。三九已至,卻和暖如春霉颠,著一層夾襖步出監(jiān)牢的瞬間对碌,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,460評論 1 272
  • 我被黑心中介騙來泰國打工蒿偎, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留朽们,地道東北人怀读。 一個月前我還...
    沈念sama閱讀 48,909評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像骑脱,于是被迫代替她去往敵國和親菜枷。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 45,512評論 2 359

推薦閱讀更多精彩內(nèi)容