Android仿微博春霍、人人Feed詳情頁吸附導(dǎo)航欄

仿微博叶眉、人人的feed詳情頁面:Listview上下滑動衅疙,導(dǎo)航欄view可吸附在頂部的效果。

一喧伞、實(shí)現(xiàn)效果

上圖:

效果圖.gif

Github: https://github.com/qizhenghao/StickyNavigationBar

歡迎拍磚絮识,拍拍更進(jìn)步次舌。

沒有對比兽愤,怎么會有傷害浅萧,下面是 微博、人人的Feed詳情頁:

微博吩案、人人Feed詳情頁.jpeg

二徘郭、實(shí)現(xiàn)原理

1、

實(shí)例化兩個(gè)一樣的導(dǎo)航欄view胧后,一個(gè)放在頁面根布局頂部的view1抱环,另一個(gè)放在ListView的headerView中的view2,在OnScrollListener的onScroll方法中眶痰,檢測view2在屏幕中的位置是不是滑動到了頂部凛驮,決定頂部view1的顯示與隱藏条辟,以達(dá)到看起來只有一個(gè)導(dǎo)航欄view顯示的效果羽嫡;

2、

為了保持兩個(gè)導(dǎo)航欄view的狀態(tài)同步婚惫,使用了觀察者模式先舷;

3滓侍、

導(dǎo)航欄中的Tab切換,即切換ListView的adapter捺球,并且記錄滑動的位置信息氮兵。


三歹鱼、UML圖

UML圖.png

StickyNavHostSubject:它把所有的自定義導(dǎo)航欄view的引用保存到一個(gè)list里。AbstractSubject提供了接口篙悯,可以增加和刪除觀察者對象。

StickyNavHost:自定義的導(dǎo)航欄view螺捐,繼承自ViewGroup,可以根據(jù)具體需求自行更改顯示的布局赔癌、樣式等澜沟。

NavListViewScollListener:需要為ListView設(shè)置的滑動事件,封裝了對吸附導(dǎo)航欄顯示刊苍、隱藏的邏輯正什。

MainActivity:用于演示demo婴氮,包含了對導(dǎo)航欄view的初始化主经,以及切換tab的操作等罩驻。


三蜈块、具體細(xì)節(jié)

1百揭、

NavListViewScollListener的onScroll()方法:


    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        //處理了root導(dǎo)航欄的顯示與隱藏, 本質(zhì)上只是控制root導(dǎo)航欄的顯示
        //而在listView的headerView中的導(dǎo)航欄不做處理,因?yàn)樗鼤S著listView的滑動自行滑出頁面
        if (NavBean.IS_NEED_ATTACH && rootView != null && nav != null) {
            rootView.getLocationOnScreen(rootLocation);
            headView.getLocationOnScreen(headLocation);
            //根據(jù)兩者在屏幕中的location位置信息课锌,決定root導(dǎo)航欄的顯示與隱藏
            if (rootLocation[1] > headLocation[1]) {
                rootView.setVisibility(View.VISIBLE);
            } else {
                rootView.setVisibility(View.INVISIBLE);
            }

            //記錄當(dāng)前l(fā)istView的滑動位置
            nav.setFirstVisibleItem(firstVisibleItem);
            nav.setTopDistance((view.getChildAt(0) == null) ? 0 : view.getChildAt(0).getTop());
        }
    }
2雏胃、

MainActivity中初始化操作:

    private void initNavsView() {
        initNavsData();
        stickyNavHostRoot.setTabItemClickListener(this);//設(shè)置點(diǎn)擊回調(diào)
        stickyNavHostHead.setTabItemClickListener(this);//設(shè)置點(diǎn)擊回調(diào)
        stickyNavHostRoot.setShowTopLine(false);

        stickNavHostSubject = new StickNavHostSubject();
        stickNavHostSubject.attachObserver(stickyNavHostRoot);//觀察者模式
        stickNavHostSubject.attachObserver(stickyNavHostHead);

        NavBean[] sortedNavs = new NavBean[mNavs.size()];//指定導(dǎo)航欄的排列順序
        sortedNavs[0] = mNavs.get(NavBean.TYPE_REPOST);
        sortedNavs[1] = mNavs.get(NavBean.TYPE_COMMENT);
        sortedNavs[2] = mNavs.get(NavBean.TYPE_LIKE);
        stickNavHostSubject.initTabData(sortedNavs);

        scrollListener = new NavListViewScrollListener(stickyNavHostRoot, stickyNavHostHead);
        mListView.setOnScrollListener(scrollListener);//為listView設(shè)置滑動監(jiān)聽瞭亮,內(nèi)部處理了吸附view的顯示與隱藏
    }

    protected void initNavsData() {
        mNavs = new SparseArray<>(NAV_LENGTH);
        mNavs.put(NavBean.TYPE_REPOST, new NavBean(NavBean.TYPE_REPOST, new TestAdapter(20, "我是轉(zhuǎn)發(fā)", this)));
        mNavs.put(NavBean.TYPE_COMMENT, new NavBean(NavBean.TYPE_COMMENT, new TestAdapter(20, "我是評論", this)));
        mNavs.put(NavBean.TYPE_LIKE, new NavBean(NavBean.TYPE_LIKE, new TestAdapter(20, "我是贊", this)));
    }

    private void initView() {
        mListView = (ListView) findViewById(R.id.list_view);
        stickyNavHostRoot = (StickyNavHost) findViewById(R.id.sticky_nav_layout);
        stickyNavHostRoot.setVisibility(View.INVISIBLE);

        View testHeaderView = LayoutInflater.from(this).inflate(R.layout.listview_head_view_test_layout, null);
        mListView.addHeaderView(testHeaderView);

        View inflateView = LayoutInflater.from(this).inflate(R.layout.sticky_nav_host_layout, null);
        stickyNavHostHead = (StickyNavHost) inflateView.findViewById(R.id.sticky_nav_layout);
        stickyNavHostHead.setVisibility(View.VISIBLE);
        mListView.addHeaderView(stickyNavHostHead);
        STICKY_POSITION_IN_HEADER = mListView.getHeaderViewsCount();
    }
3统翩、

MainActivity中點(diǎn)擊切換導(dǎo)航欄Tab的回調(diào):

    @Override
    public void onTabItemSelected(@NavBean.TYPE int type) {
        NavBean currNav = mNavs.get(type);
        stickNavHostSubject.setSelectedType(type);//事件分發(fā)給注冊者厂汗,注冊者進(jìn)行相應(yīng)的變化
        if (currNav.type == NavBean.TYPE_CURRENT)//等于當(dāng)前選中的tab娶桦,可以屏蔽掉
            return;
        NavBean.TYPE_CURRENT = currNav.type;
        scrollListener.setNav(currNav);
        mListView.setAdapter(currNav.adapter);
        if (stickyNavHostRoot.getVisibility() == View.VISIBLE) {//吸附在頂部的rootView正在展示
            if (currNav.getFirstVisibleItem() < STICKY_POSITION_IN_HEADER)
                mListView.setSelectionFromTop(STICKY_POSITION_IN_HEADER, stickyNavHostRoot.getHeight() - 2);
            else
                mListView.setSelectionFromTop(currNav.getFirstVisibleItem(), currNav.getTopDistance());
        } else {//吸附在頂部的rootView沒有展示衷畦,說明在切換導(dǎo)航欄的時(shí)候是不需要進(jìn)行滑動的碰酝,保持上次的位置即可
            mListView.setSelectionFromTop(NavBean.firstVisibleItemUniversal, NavBean.topDistanceUniversal);
        }
    }
4、

StickyNavHostSubject做的事情就很簡單了铛嘱,和常見的觀察者模式?jīng)]區(qū)別:

public class StickNavHostSubject extends AbstractSubject<IStickyNavHostObserver> {

    private List<IStickyNavHostObserver> observers;

    public StickNavHostSubject() {
        observers = new ArrayList<>();
    }

    public void attachObserver(IStickyNavHostObserver observer) {
        observers.add(observer);
    }

    public void detachObserver(IStickyNavHostObserver observer) {
        observers.remove(observer);
    }

    @Override
    public void initTabData(NavBean[] navs) {
        for (IStickyNavHostObserver observer : observers)
            observer.initTabData(navs);
    }

    @Override
    public void refreshTabData(NavBean nav) {
        for (IStickyNavHostObserver observer : observers)
            observer.refreshTabData(nav);
    }

    @Override
    public void setSelectedType(@NavBean.TYPE int type) {
        for (IStickyNavHostObserver observer : observers)
            observer.setSelectedType(type);
    }

    @Override
    public void setSelectedPosition(int position) {
        for (IStickyNavHostObserver observer : observers)
            observer.setSelectedPosition(position);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末墨吓,一起剝皮案震驚了整個(gè)濱河市帖烘,隨后出現(xiàn)的幾起案子秘症,更是在濱河造成了極大的恐慌式矫,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,591評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件聪廉,死亡現(xiàn)場離奇詭異,居然都是意外死亡框全,警方通過查閱死者的電腦和手機(jī)干签,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,448評論 3 392
  • 文/潘曉璐 我一進(jìn)店門容劳,熙熙樓的掌柜王于貴愁眉苦臉地迎上來鸭蛙,“玉大人娶视,你說我怎么就攤上這事睁宰》净瘢” “怎么了孝赫?”我有些...
    開封第一講書人閱讀 162,823評論 0 353
  • 文/不壞的土叔 我叫張陵青柄,是天一觀的道長预侯。 經(jīng)常有香客問我,道長双戳,這世上最難降的妖魔是什么糜芳? 我笑而不...
    開封第一講書人閱讀 58,204評論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮塘辅,結(jié)果婚禮上邪驮,老公的妹妹穿的比我還像新娘。我一直安慰自己盘榨,他們只是感情好蟆融,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,228評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著山憨,像睡著了一般郁竟。 火紅的嫁衣襯著肌膚如雪由境。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,190評論 1 299
  • 那天讥蟆,我揣著相機(jī)與錄音纺阔,去河邊找鬼。 笑死质况,一個(gè)胖子當(dāng)著我的面吹牛玻靡,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播潭陪,決...
    沈念sama閱讀 40,078評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼依溯,長吁一口氣:“原來是場噩夢啊……” “哼瘟则!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起慷嗜,我...
    開封第一講書人閱讀 38,923評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎薇溃,沒想到半個(gè)月后沐序,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體堕绩,經(jīng)...
    沈念sama閱讀 45,334評論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,550評論 2 333
  • 正文 我和宋清朗相戀三年特姐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了到逊。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片滤钱。...
    茶點(diǎn)故事閱讀 39,727評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡件缸,死狀恐怖叔遂,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情已艰,我是刑警寧澤,帶...
    沈念sama閱讀 35,428評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站嚼吞,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏舱禽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,022評論 3 326
  • 文/蒙蒙 一罗心、第九天 我趴在偏房一處隱蔽的房頂上張望城瞎。 院中可真熱鬧,春花似錦全谤、人聲如沸认然。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,672評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽毕骡。三九已至,卻和暖如春窿撬,著一層夾襖步出監(jiān)牢的瞬間劈伴,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 32,826評論 1 269
  • 我被黑心中介騙來泰國打工跛璧, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留追城,地道東北人座柱。 一個(gè)月前我還...
    沈念sama閱讀 47,734評論 2 368
  • 正文 我出身青樓辆布,卻偏偏與公主長得像,于是被迫代替她去往敵國和親锋玲。 傳聞我的和親對象是個(gè)殘疾皇子惭蹂,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,619評論 2 354

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

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,078評論 25 707
  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點(diǎn)贊按鈕進(jìn)度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,755評論 22 665
  • 如果你是一個(gè)偵探迷媚污,也是中國古典文學(xué)愛好者廷雅,就一定不要錯(cuò)過這本由海南出版社耗美,出版的《大唐狄公案》。 隨意翻個(gè)案件航缀,...
    文心愛喝茶閱讀 489評論 0 1
  • 今年最后一天商架,收了不少紅包,這一整年的狀態(tài)都不好芥玉,感情工作蛇摸,下半年家里多了個(gè)新成員,雖然難但也蠻開心灿巧。 希望來年在...
    煙澀寒閱讀 181評論 0 0
  • 2017年9月18日 每天自問 1.今天留意時(shí)間怎么花掉的赶袄。 上午,5:35-7:40抠藕,起床饿肺,跑步10公里,早飯盾似,...
    雪狼K閱讀 137評論 0 0