Copy weibo, Renren feed details page: Listview sliding up and down, navigation bar view can be attached to the top of the effect.

One, achieve the effect

Above:





2. GIF

Making: github.com/qizhenghao/…

Welcome clap brick, clap more progress.

There is no comparison, how can there be harm, the following is weibo, Renren Feed details page:





Weibo, Renren Feed details page. Jpeg

Two, the implementation principle

1.

Instantiate two identical navigation views, view1 at the top of the page root layout and View2 at the headerView of the ListView. In OnScrollListener’s onScroll method, check whether view2 is sliding to the top of the screen. Determine the top view1 to show and hide so that it looks like only one navigation view is displayed.

2,

To keep the states of the two navigation views in sync, the observer mode is used.

3,

Tab switch in the navigation bar, that is, switch the Adapter of the ListView, and record the sliding position information.


Third, UML diagrams





UML diagrams. PNG

StickyNavHostSubject: It saves all the references to the custom navigation view in a list. AbstractSubject provides interfaces to add and remove observer objects.

StickyNavHost: a customized navigation view inherited from the ViewGroup. You can change the layout and style as required.

NavListViewScollListener: The slide event that needs to be set for ListView, encapsulating the logic for showing and hiding the adsorption navigation bar.

MainActivity: used for demo, including initialization of navigation bar view, and TAB switching operation, etc.


Third, specific details

1.

NavListViewScollListener onScroll() :

    @Override
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        // Handles the display and hiding of the root navigation bar, essentially controlling the display of root navigation bar
        // The navigation bar in the listView's headerView is not handled because it slides out of the page as the listView slides
        if(NavBean.IS_NEED_ATTACH && rootView ! =null&& nav ! =null) {
            rootView.getLocationOnScreen(rootLocation);
            headView.getLocationOnScreen(headLocation);
            // The root navigation bar is displayed and hidden according to their location information in the screen
            if (rootLocation[1] > headLocation[1]) {
                rootView.setVisibility(View.VISIBLE);
            } else {
                rootView.setVisibility(View.INVISIBLE);
            }

            // Record the current slide position of the listView
            nav.setFirstVisibleItem(firstVisibleItem);
            nav.setTopDistance((view.getChildAt(0) = =null)?0: view.getChildAt(0).getTop()); }}Copy the code
2,

Initialization actions in MainActivity:

    private void initNavsView() {
        initNavsData();
        stickyNavHostRoot.setTabItemClickListener(this);// Set click callback
        stickyNavHostHead.setTabItemClickListener(this);// Set click callback
        stickyNavHostRoot.setShowTopLine(false);

        stickNavHostSubject = new StickNavHostSubject(a); stickNavHostSubject.attachObserver(stickyNavHostRoot);// Observer mode
        stickNavHostSubject.attachObserver(stickyNavHostHead);

        NavBean[] sortedNavs = new NavBean[mNavs.size()];// Specify the navigation order
        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);// Set slide listener for listView, internal handle adsorption view show and hide
    }

    protected void initNavsData() {
        mNavs = new SparseArray<>(NAV_LENGTH);
        mNavs.put(NavBean.TYPE_REPOST, new NavBean(NavBean.TYPE_REPOST, new TestAdapter(20."I'm forwarding.".this)));
        mNavs.put(NavBean.TYPE_COMMENT, new NavBean(NavBean.TYPE_COMMENT, new TestAdapter(20."I'm a critic.".this)));
        mNavs.put(NavBean.TYPE_LIKE, new NavBean(NavBean.TYPE_LIKE, new TestAdapter(20."I'm Zan.".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();
    }Copy the code
3,

MainActivity click to switch navigation Tab callback:

    @Override
    public void onTabItemSelected(@NavBean.TYPE int type) {
        NavBean currNav = mNavs.get(type);
        stickNavHostSubject.setSelectedType(type);// Events are distributed to registrants, who make changes accordingly
        if (currNav.type= =NavBean.TYPE_CURRENT)// is equal to the currently selected TAB, can be masked
            return;
        NavBean.TYPE_CURRENT = currNav.type;
        scrollListener.setNav(currNav);
        mListView.setAdapter(currNav.adapter);
        if (stickyNavHostRoot.getVisibility() == View.VISIBLE) {// The rootView attached to the top is being displayed
            if (currNav.getFirstVisibleItem() < STICKY_POSITION_IN_HEADER)
                mListView.setSelectionFromTop(STICKY_POSITION_IN_HEADER, stickyNavHostRoot.getHeight() - 2);
            else
                mListView.setSelectionFromTop(currNav.getFirstVisibleItem(), currNav.getTopDistance());
        } else {// The rootView attached to the top is not displayed, indicating that there is no need to slide when switching the navigation bar, just keep the last position
            mListView.setSelectionFromTop(NavBean.firstVisibleItemUniversal, NavBean.topDistanceUniversal); }}Copy the code
4,

What StickyNavHostSubject does is very simple, no different from the usual observer pattern:

public class StickNavHostSubject extends AbstractSubject<IStickyNavHostObserver> {

    private List<IStickyNavHostObserver> observers;

    public StickNavHostSubject(a) {
        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);
    }Copy the code