Android TabLayout在与viewpager AppBarLayout一起使用时出现tab选中后下划线滑动缓慢,卡顿异常解决方案

版权声明:请尊重原创,侵权必究 如果想转载或者讨论问题,可以添加微信号 WJ1118825 进行授权获取或者共同交流学习 ,转载请注明原文链接并设置超链接跳转功能,本文链接: https://blog.csdn.net/wjj1996825/article/details/83895336

今天早上刚测试发现的一个问题,之前没有注意到,特别尴尬感觉,之前经常使用TabLayout和viewpager联动切换碎片,异常的情况如下图展示:

布局代码如下:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#383d4d"
        android:orientation="vertical">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/fragment_ring_appbarlayout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <!--@dimen/dp_109-->

        <android.support.design.widget.CollapsingToolbarLayout
            android:id="@+id/ll_ring_top_view"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_60"
            android:background="#282c38"
            app:layout_scrollFlags="scroll|exitUntilCollapsed"
            app:statusBarScrim="#282c38">

            <RelativeLayout
                android:id="@+id/rl_ring_title"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_60"
                android:background="#282c38">

                <com.blossom.ripple.widget.AvenirNextRegularTextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="Ring"
                    android:textSize="@dimen/sp_16"
                    android:textColor="@color/white"
                    android:layout_marginTop="@dimen/dp_28"
                    android:layout_centerHorizontal="true"/>

            </RelativeLayout>


        </android.support.design.widget.CollapsingToolbarLayout>


        <LinearLayout
            android:id="@+id/ring_tab_outside"
            android:layout_width="match_parent"
            android:layout_height="@dimen/dp_49"
            android:layout_gravity="bottom"
            android:background="#282c38"
            android:orientation="vertical">


            <View
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_1"
                android:background="#353944"/>

            <android.support.design.widget.TabLayout
                android:id="@+id/tablayout_top_ring"
                android:layout_width="match_parent"
                android:layout_height="@dimen/dp_48"
                android:paddingLeft="@dimen/dp_3"
                android:paddingRight="@dimen/dp_3"
                app:tabIndicatorColor="@color/white"
                app:tabTextColor="@color/white"
                app:tabMode="fixed">
               
            </android.support.design.widget.TabLayout>


        </LinearLayout>


    </android.support.design.widget.AppBarLayout>



    <android.support.v4.view.ViewPager
        android:id="@+id/vp_ring"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

    </android.support.v4.view.ViewPager>

    </android.support.design.widget.CoordinatorLayout>

fragment中的代码如下:

override fun initView(inflater: LayoutInflater): View {
        var view = inflater.inflate(R.layout.fragment_ring,null)
        initFindViewById(view )
        return view
    }





fun initFindViewById(view: View) {
        rl_ring_title = view.rl_ring_title

        view.tablayout_top_ring.addTab(view.tablayout_top_ring.newTab())
        view.tablayout_top_ring.addTab(view.tablayout_top_ring.newTab())
        view.tablayout_top_ring.setupWithViewPager(view.vp_ring)

        var ringListFragment1Bundle = Bundle()
        ringListFragment1Bundle.putSerializable("ringType",1)
        var ringListFragment1 = RingListFragment()
        ringListFragment1.arguments = ringListFragment1Bundle

        var ringListFragment2Bundle = Bundle()
        ringListFragment2Bundle.putSerializable("ringType",2)
        var ringListFragment2 = RingListFragment()
        ringListFragment2.arguments = ringListFragment2Bundle

        list!!.add(ringListFragment1)
        list!!.add(ringListFragment2)

        var adapter = CustomTabPagerAdapter(childFragmentManager,list,context,null)
        view.vp_ring.adapter = adapter

        for (i in 0 until adapter.count){
            val tab = view?.tablayout_top_ring?.getTabAt(i)//获得每一个tab
            if(i==0){
                tab?.setCustomView(R.layout.tab_ringtones_item)//给每一个tab设置view
            }else{
                tab?.setCustomView(R.layout.tab_notification_item)//给每一个tab设置view
            }
        }

        var  offset = view.ll_ring_top_view.layoutParams.height
        view.fragment_ring_appbarlayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener{
            override fun onOffsetChanged(p0: AppBarLayout?, verticalOffset: Int) {
                Log.d(TAG, "OffsetChangedListener-------verticalOffset==$verticalOffset")
                view!!.rl_ring_title.alpha = (offset!!.toFloat() - (Math.abs(verticalOffset).toFloat())).div(offset!!.toFloat())


           if (Math.abs(verticalOffset)>=offset){       

                    var lp = view!!.ring_tab_outside.layoutParams
                    lp.height = DipDpUtil.dip2px(activity,75f)
                    view.ring_tab_outside.layoutParams = lp
                    var lp2 = view!!.tablayout_top_ring.layoutParams
                    lp2.height = DipDpUtil.dip2px(activity,74f)
                    view.tablayout_top_ring.layoutParams = lp2
                    view.tablayout_top_ring.invalidate()
                }else{
                   
                    var lp = view!!.ring_tab_outside.layoutParams
                    lp.height = DipDpUtil.dip2px(activity,49f)
                    view.ring_tab_outside.layoutParams = lp
                    var lp2 = view!!.tablayout_top_ring.layoutParams
                    lp2.height = DipDpUtil.dip2px(activity,48f)
                    view.tablayout_top_ring.layoutParams = lp2
                    view.tablayout_top_ring.invalidate()

                }

            }

        })

    }

代码都贴上了,下面来分享我解决问题的过程和思路,刚开始分析确定了两个可能:

1.是TabLayout这个控件使用时代码调用或者属性设置出了问题;

2.TabLayout放在CoordinatorLayout和AppBarLayout中出现了加载和滑动的异常

然后开始逐步开始尝试找解决方案,逐步排除可能

 我试着在xml布局中找TabLayout中与tabIndicator相关的各个属性设置,然后发现了   app:tabIndicatorAnimationDuration=""我试着将他设置为70,然后再运行测试,发现虽然tab切换时导航条下划线滑动延迟稍微优化了一点,但还是会有出现,看来这个不是解决的最佳办法,然后发现没有别的和tabIndicator滑动卡顿或速度相关的属性了,于是开始分析第二种可能性,因为产品要求必须要有顶部标题栏向上滑动消失,tablayout置顶的效果(要求的效果如下图所示),

所以才加入了用TabLayout放在CoordinatorLayout和AppBarLayout中来实现,因为之前用recyclerview加setOnScrollListener来动态改变顶部导航的消失隐藏于透明度会出现滑动时闪和抖动的异常,当然这是题外话,继续聊我们这个bug的解决思路,但是CoordinatorLayout和AppBarLayout的配合使用里面的各种属性比较复杂,我不知该从哪里下手,后来我看我代码中appbarlayout.addOnOffsetChangedListener()方法时突然有了一个想法,因为之前发现给appbarlayout设置这个监听的时候观察过log日志中的打印,这个滑动便宜的verticalOffset会一直不停的打印,不管你是否对他进行了滑动的操作,也就是说哪怕页面是静止状态,它也会一直进行监听操作,如下图log打印gif图

然后我开始想是不是我在这个监听里面动态改变tablayout高度的代码出现了问题,因为里面的判断是这样写的:

        view.fragment_ring_appbarlayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener{
            override fun onOffsetChanged(p0: AppBarLayout?, verticalOffset: Int) {
                Log.d(TAG, "OffsetChangedListener-------verticalOffset==$verticalOffset")
                view!!.rl_ring_title.alpha = (offset!!.toFloat() - (Math.abs(verticalOffset).toFloat())).div(offset!!.toFloat())


           if (Math.abs(verticalOffset)>=offset){       

                    var lp = view!!.ring_tab_outside.layoutParams
                    lp.height = DipDpUtil.dip2px(activity,75f)
                    view.ring_tab_outside.layoutParams = lp
                    var lp2 = view!!.tablayout_top_ring.layoutParams
                    lp2.height = DipDpUtil.dip2px(activity,74f)
                    view.tablayout_top_ring.layoutParams = lp2
                    view.tablayout_top_ring.invalidate()
                }else{
                   
                    var lp = view!!.ring_tab_outside.layoutParams
                    lp.height = DipDpUtil.dip2px(activity,49f)
                    view.ring_tab_outside.layoutParams = lp
                    var lp2 = view!!.tablayout_top_ring.layoutParams
                    lp2.height = DipDpUtil.dip2px(activity,48f)
                    view.tablayout_top_ring.layoutParams = lp2
                    view.tablayout_top_ring.invalidate()

                }

            }

        })

这样就会导致一个问题,当verticalOffset的绝对值大于等于定好的offset时改变tablayout的高度,但正常情况没有进行操作的时候会走下面的判断,设置正常的高度,并且会一直调用invalidate方法强制刷新tablayout,这肯定会导致tablayout做很多没有必要且损耗性能的操作,ok,这是个需要优化的地方,有可能就是这个疏忽造成的bug,于是改成下面的代码:

var  offset = view.ll_ring_top_view.layoutParams.height
        view.fragment_ring_appbarlayout.addOnOffsetChangedListener(object : AppBarLayout.OnOffsetChangedListener{
            override fun onOffsetChanged(p0: AppBarLayout?, verticalOffset: Int) {
                Log.d(TAG, "OffsetChangedListener-------verticalOffset==$verticalOffset")
                view!!.rl_ring_title.alpha = (offset!!.toFloat() - (Math.abs(verticalOffset).toFloat())).div(offset!!.toFloat())


                if (Math.abs(verticalOffset)>=offset){
                    if (!(view.ring_tab_outside.height ==DipDpUtil.dip2px(activity,75f) )){
                        var lp = view!!.ring_tab_outside.layoutParams
                        lp.height = DipDpUtil.dip2px(activity,75f)
                        view.ring_tab_outside.layoutParams = lp
                    }
                    if (!(view.tablayout_top_ring.height ==DipDpUtil.dip2px(activity,74f) )){
                        var lp2 = view!!.tablayout_top_ring.layoutParams
                        lp2.height =  DipDpUtil.dip2px(activity,74f)
                        view.tablayout_top_ring.layoutParams = lp2
                        view.tablayout_top_ring.invalidate()
                    }

                }else{
                    if (!(view.ring_tab_outside.height ==DipDpUtil.dip2px(activity,49f) )){
                        var lp = view!!.ring_tab_outside.layoutParams
                        lp.height = DipDpUtil.dip2px(activity,49f)
                        view.ring_tab_outside.layoutParams = lp
                    }
                    if (!(view.tablayout_top_ring.height ==DipDpUtil.dip2px(activity,48f) )){
                        var lp2 = view!!.tablayout_top_ring.layoutParams
                        lp2.height =  DipDpUtil.dip2px(activity,48f)
                        view.tablayout_top_ring.layoutParams = lp2
                        view.tablayout_top_ring.invalidate()
                    }

                }

            }

        })

再运行发现tablayout中的tab滑动切换,下面的导航条不再有滑动延迟,卡顿的情况,ok,这个bug造成的原因找到了,看来以后给appbarlayout添加addOnOffsetChangedListener监听时,当中的操作一定要做好优化,否则会一直不断的被调用,对app性能造成不必要的损耗和浪费,这次出现的这个滑动卡顿的bug就是这个原因造成的。

猜你喜欢

转载自blog.csdn.net/wjj1996825/article/details/83895336