手把手实现tablayout随recycleview滚动而滚动(非官方coordinatorlayout实现)

谷歌新出的Android Design Support Library带来了新的兼容的md风格控件,其中的coordinatorlayout配合appbarlayout再指定behavior可以实现滚动的效果,然而很遗憾,在应用到项目的时候,出现各种问题,比如:

1.我所在的coordinatorlayout布局里需要用到viewstub作为网络请求异常时懒加载的布局,在coordinatorlayout如果延时20多ms再去viewstub.inflate的话布局就找不到了,在relativelayout这些布局就是可以随时调用加载显示。

2.首先向上滚动recycleview,直到tablayout跟随滚动被隐藏,切换到其他fragment再切换回coordinatorlayout所在的fragment,向下滚动recycleview回到位置0的话,tablayout不会滚回来了,需要稍微先上滑动一下再向下话才能回来,估计是tablayout设置的属性app:layout_scrollFlags=”scroll|enterAlways”问题。

3.向上滚动recycleview到一定位置,点击回到顶部的按钮,调用scrollToPosition(0)回到位置0的话,tablayout也是不会滚回来的,也是需要稍微先上滑动一下再向下话才能回来。

所以思考调试了很久还是决定自己动手,丰衣足食,不要coordinatorlayout,再tablayout的包裹布局,通过监听recycleview的onscroll事件,取每次偏移量,调用tablayout的包裹布局的scrollby(0,dy)来实现移动,并针对快速滑动时偏移量的突增进行了判断和处理,最终实现了较好的tablayout随recycleview滚动而滚动效果,这样用户滑动recycleview时能够看到更多的内容,体验又提升了。

首先看下mainactivity的布局:
viewpager和tablayout放在帧布局里面,这样做是为了,recycleview位置0的地方添加一个跟tablayout等高的view,从而tablayout不会遮住recycleview的图片item


<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:fitsSystemWindows="true"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="@color/material_primary"
        app:popupTheme="@style/Theme.AppCompat.Light">
    android.support.v7.widget.Toolbar>

    <FrameLayout
        android:layout_below="@id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <RelativeLayout
            android:id="@+id/appbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical"
            android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

            <android.support.design.widget.TabLayout
                android:id="@+id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:background="@color/material_primary"
                app:tabGravity="center"
                app:tabIndicatorColor="@color/material_white_500"
                app:tabMode="scrollable"
                app:tabSelectedTextColor="@color/material_white_500"
                app:tabTextColor="@color/material_grey_300" />

        RelativeLayout>
    FrameLayout>

RelativeLayout>

然后看下mainactivity关键代码:
toolbar设置这里添加一个单击回到recycleview顶部的textview

    private void initToolbar() {
        mToolbar = (Toolbar) findViewById(R.id.toolbar);
        mToolbar.setTitle("");
        TextView tv = new TextView(this);
        tv.setText("点击跳回顶部");
        tv.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // EventBus通知此tag对于订阅事件者返回顶部
                EventBus.getDefault().post(new EventBusElement(), "scrollToTop");
            }
        });
        // toolbar添加一个可点击返回顶部的textview
        mToolbar.addView(tv);
        setSupportActionBar(mToolbar);
    }

设置测试的标签给tablayout和创建fragment到viewpager中

 private void initData() {

        ArrayList tabTitles = new ArrayList<>();
        tabTitles.add("AAAA");
        tabTitles.add("BBBB");
        tabTitles.add("CCCC");
        tabTitles.add("DDDD");
        tabTitles.add("EEEE");
        tabTitles.add("FFFF");
        tabTitles.add("GGGG");
        tabTitles.add("HHHH");

        List fragments = new ArrayList<>();
        for (int i = 0; i < tabTitles.size(); i++) {
            // tablayout添加标签
            mTabLayout.addTab(mTabLayout.newTab().setText(tabTitles.get(i)));
            fragments.add(new HomeSimpleFragment());
        }

        FragmentAdapter adapter = new FragmentAdapter(getSupportFragmentManager(), fragments, tabTitles);
        mViewPager.setAdapter(adapter);
        mTabLayout.setupWithViewPager(mViewPager);
        mTabLayout.setTabsFromPagerAdapter(adapter);
    }

fragment和mainactivity的交互我选择使用MR.SIMPLE的evenbus来进行,十分便捷,mainactivity这里订阅一个scroollTabLayout的事件,
recycleview在滚动的时候会在onscroll方法发送滚动的偏移量到这个方法,我们拿到偏移量调用mAppBarLayout.scrollBy来移动tablayout,
if (mAppBarLayout.getScrollY() < 0)和if (element.getAppBarOffset() == 12345 && mAppBarLayout.getScrollY() >= 0 && mAppBarLayout.getScrollY() != mTabLayoutHeight)是针对快速滚动后有时会不在正确的位置,这时就需要我们人为判断偏差再移动调整

    @Subscriber(tag = "scroollTabLayout")
    private void scroollTabLayout(EventBusElement element) {
        if (element.getAppBarOffset() != 12345) {

            mAppBarLayout.scrollBy(0, element.getAppBarOffset());
        }
        if (mAppBarLayout.getScrollY() < 0) {
            mAppBarLayout.scrollBy(0, -mAppBarLayout.getScrollY());
        }
        // 位置不为0的情况是快速滑动的时候,传递12345作为快速向下滚动的标志,mainactivity的scroollTabLayout会针对此情况做特殊处理
        if (element.getAppBarOffset() == 12345 && mAppBarLayout.getScrollY() >= 0 && mAppBarLayout.getScrollY() != mTabLayoutHeight) {
            mAppBarLayout.scrollBy(0, mTabLayoutHeight - mAppBarLayout.getScrollY());
        }
    }

再来看下HomeSimpleFragment的关键代码:
HomeSimpleFragment也订阅了一个返回到顶部的世界,当mainactivity的toolbar上的text被点击的时候发送通知调用这个方法,
element.setAppBarOffset(-MainActivity.mTabLayoutHeight);
EventBus.getDefault().post(element, “scroollTabLayout”);
是为了返回顶部的时候将tablayout滚动回原来的位置

    /**
     * 订阅一个返回顶部的事件
     *
     * @param element
     */
    @Subscriber(tag = "scrollToTop")
    private void scrollTotop(EventBusElement element) {
        mRecyclerView.smoothScrollToPosition(0);
        element.setAppBarOffset(-MainActivity.mTabLayoutHeight);
        EventBus.getDefault().post(element, "scroollTabLayout");
    }

mRecyclerView会设置一个滚动监听,将偏移量传回给mainactivity控制tablayout的滚动

mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                if (mManager.findFirstVisibleItemPosition() == 0) {
                    // 位置为0的时候传递偏移量给mainactivity的scroollTabLayout处理tablayout的滚动
                    element.setAppBarOffset(dy);
                    EventBus.getDefault().post(element, "scroollTabLayout");
                } else {
                    // 位置不为0的情况是快速滑动的时候,传递12345作为快速向下滚动的标志,mainactivity的scroollTabLayout会针对此情况做特殊处理
                    element.setAppBarOffset(12345);
                    EventBus.getDefault().post(element, "scroollTabLayout");
                }

            }
        });

PS:mainactivity在viewpager切换的时候
在onPageSelected这里控制tablayout滚回原位,这样是为了提高用户体验,参考的是wish切换时的做法

mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }

            @Override
            public void onPageSelected(int position) {
                // 页面切换的话,tablayout回到原位
                mAppBarLayout.scrollBy(0, -mAppBarLayout.getScrollY());
            }

            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });

至此,大功告成了!下面看动图的效果吧!


demo地址:
http://download.csdn.net/detail/oushangfeng123/8971049

你可能感兴趣的:(Android)