国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專(zhuān)欄INFORMATION COLUMN

TabLayout使用遇到的坑及方案

baishancloud / 529人閱讀

摘要:但對(duì)于我們的對(duì)于界面還原度要求較高,對(duì)于之間的間距也有一些要求,所以也要處理,對(duì)于間距部分的處理可以按照之前的方式通過(guò)反射來(lái)完成。注意,這種方式因?yàn)樾枰?jì)算的文字寬度,所以要放到設(shè)置完所有的后調(diào)用。

修改下劃線(xiàn)寬度的坑

效果如下:

代碼實(shí)現(xiàn)方式:

如果想要實(shí)現(xiàn)這種效果,最主要控制的就是下劃線(xiàn)部分,現(xiàn)在網(wǎng)上有很多通過(guò)反射的方式來(lái)修改下劃線(xiàn)寬度的文章,但這段代碼如果實(shí)現(xiàn)我們想要的效果是不可能的,因?yàn)槿绻芯窟^(guò)源碼就知道Indicator的寬度跟Tab的寬度一致的,不能讓指示器的寬度小于Tab的寬度,所以我們只能另辟蹊徑:通過(guò)CustomView自己繪制線(xiàn),通過(guò)添加OnTabSelectedListener來(lái)展示隱藏下劃線(xiàn),讓原生的Indicaqtor高度為0也不會(huì)影響我們的展示。

OK! Talk is cheap, show me the code.

先隱藏原來(lái)的下劃線(xiàn),讓其不顯示:
    
其次設(shè)置CustomView效果:

再手動(dòng)控制切換時(shí)Tab的下劃線(xiàn):
mTabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabReselected(tab: TabLayout.Tab?) {
                val view = tab?.customView?.findViewById(R.id.tab_indicator)
                val textView = tab?.customView?.findViewById(R.id.tab_tv_date)
                textView?.setTextColor(mTabSelectedColor)
                view?.visibility = View.VISIBLE
            }

            override fun onTabUnselected(tab: TabLayout.Tab?) {
                val view = tab?.customView?.findViewById(R.id.tab_indicator)
                val textView = tab?.customView?.findViewById(R.id.tab_tv_date)
                textView?.setTextColor(mTabUnSelectedColor)
                view?.visibility = View.INVISIBLE
            }

            override fun onTabSelected(tab: TabLayout.Tab?) {
                val view = tab?.customView?.findViewById(R.id.tab_indicator)
                val textView = tab?.customView?.findViewById(R.id.tab_tv_date)
                textView?.setTextColor(mTabSelectedColor)
                view?.visibility = View.VISIBLE
            }

        })
        
間距部分

只是這樣的話(huà),下劃線(xiàn)的問(wèn)題解決了。但對(duì)于我們的UI對(duì)于界面還原度要求較高,對(duì)于Tab之間的間距也有一些要求,所以也要處理,對(duì)于間距部分的處理可以按照之前的方式通過(guò)反射來(lái)完成。注意,這種方式因?yàn)樾枰?jì)算TextView的文字寬度,所以要放到設(shè)置完所有的customView后調(diào)用。

    private fun customTabWidth(tabLayout: TabLayout) {
        try {
            //拿到tabLayout的mTabStrip屬性
            val mTabStripField = tabLayout.javaClass.getDeclaredField("mTabStrip")
            mTabStripField.isAccessible = true

            val mTabStrip = mTabStripField.get(tabLayout) as LinearLayout

            val dp2 = DensityUtils.dp2px(context, 2f)
            val dp30 = DensityUtils.dp2px(context, 30f)


            for (i in 0 until mTabStrip.childCount) {
                // 此時(shí)獲取到tabView其實(shí)是我們的CustomView
                val tabView = mTabStrip.getChildAt(i)
                // 找到我們的TextView
                val mTextView = tabView.findViewById(R.id.tab_tv_date)

                // 測(cè)量出TextView文字的寬度
                var width = 0
                width = mTextView.width
                if (width == 0) {
                    mTextView.measure(0, 0)
                    width = mTextView.measuredWidth
                }

                // PDL = padding left    ---   PDR = padding right
                // |PDL30 CONTENT PDR30|  |PDL2 CONTENT PDR30| |PDL2 CONTENT PDR30| |PDL2 CONTENT PDR30|

                
                val params = tabView.layoutParams as LinearLayout.LayoutParams
                // 如果是第一個(gè)View,則View左側(cè)還要30dp的空間需要padding
                // 有了TextView的寬度,我們可以根據(jù)自己想要的效果去設(shè)置Tab的寬度,Tab的實(shí)際間距其實(shí)是0,但我們可以
                // 通過(guò)改變Padding的方式做出Tab間距的效果
                // 對(duì)于為什么用**padding**而不是**margin**的原因,請(qǐng)向下看
                if (i == 0) {
                    params.width = width + dp30 + dp30
                    tabView.layoutParams = params
                    tabView.setPadding(dp30, 0, dp30, 0)
                } else {
                    params.width = width + dp2 + dp30
                    tabView.layoutParams = params
                    tabView.setPadding(dp2, 0, dp30, 0)
                }

                tabView.invalidate()
            }
        } catch (e: NoSuchFieldException) {
            e.printStackTrace()
        } catch (e: IllegalAccessException) {
            e.printStackTrace()
        }
    }
修改完下劃線(xiàn)寬度后tab的滑動(dòng)位置錯(cuò)亂的坑(TabView的 MarginLeft & MarginRigt 導(dǎo)致的問(wèn)題)

開(kāi)始做的時(shí)候看網(wǎng)上相關(guān)的文檔然后做出的效果如下:

可以看到當(dāng)滑過(guò)去以后,Tab又位移了一段距離,我想如果你的腦子沒(méi)有被踢過(guò)的話(huà),肯定都知道滑動(dòng)完位移后的位置是正確的位置,那么為什么會(huì)出現(xiàn)這種情況已經(jīng)如何解決??

看源碼!

因?yàn)槭俏覀冴P(guān)聯(lián)的ViewPager滑動(dòng)導(dǎo)致的Tab位移,那么就從ViewPager的移動(dòng)時(shí)開(kāi)始找線(xiàn)索,通過(guò)setupWithViewPager()方法中可以看到,TabLayout代碼中給ViewPager對(duì)象通過(guò)addOnPageChangeListener()方法添加了一個(gè)監(jiān)聽(tīng),那么我們看一下監(jiān)聽(tīng)的回調(diào)好了!

 public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener {
        // 忽略無(wú)關(guān)代碼

        // **主要看這里哦~!~!~ **
        @Override
        public void onPageScrolled(final int position, final float positionOffset,
                final int positionOffsetPixels) {
            final TabLayout tabLayout = mTabLayoutRef.get();
            if (tabLayout != null) {
                // Only update the text selection if we"re not settling, or we are settling after
                // being dragged
                final boolean updateText = mScrollState != SCROLL_STATE_SETTLING ||
                        mPreviousScrollState == SCROLL_STATE_DRAGGING;
                // Update the indicator if we"re not settling after being idle. This is caused
                // from a setCurrentItem() call and will be handled by an animation from
                // onPageSelected() instead.
                final boolean updateIndicator = !(mScrollState == SCROLL_STATE_SETTLING
                        && mPreviousScrollState == SCROLL_STATE_IDLE);
                        // 這里調(diào)用了setScrollPosition去讓TabLayout進(jìn)行滑動(dòng),穿進(jìn)去目標(biāo)下標(biāo)和偏移量
                tabLayout.setScrollPosition(position, positionOffset, updateText, updateIndicator);
            }
        }

    }

再向下看一下setScrollPosition方法!

 void setScrollPosition(int position, float positionOffset, boolean updateSelectedText,
            boolean updateIndicatorPosition) {
        final int roundedPosition = Math.round(position + positionOffset);
        if (roundedPosition < 0 || roundedPosition >= mTabStrip.getChildCount()) {
            return;
        }

        // Set the indicator position, if enabled
        // 如果更新指示器的話(huà)那么會(huì)走進(jìn)這里,我們指示器都隱藏掉了無(wú)需看這里
        if (updateIndicatorPosition) {
            mTabStrip.setIndicatorPositionFromTabPosition(position, positionOffset);
        }

        // Now update the scroll position, canceling any running animation
        if (mScrollAnimator != null && mScrollAnimator.isRunning()) {
            mScrollAnimator.cancel();
        }
        // 這里調(diào)用了scrollTo方法去移動(dòng)位置,那么我們猜測(cè)應(yīng)該是這個(gè)X位移距離算錯(cuò)了,進(jìn)去看一下
        scrollTo(calculateScrollXForTab(position, positionOffset), 0);

        
    }
    
    private int calculateScrollXForTab(int position, float positionOffset) {
        if (mMode == MODE_SCROLLABLE) {
    
            // 獲取目標(biāo)的View
            final View selectedChild = mTabStrip.getChildAt(position);
            final View nextChild = position + 1 < mTabStrip.getChildCount()
                    ? mTabStrip.getChildAt(position + 1)
                    : null;
            final int selectedWidth = selectedChild != null ? selectedChild.getWidth() : 0;
            final int nextWidth = nextChild != null ? nextChild.getWidth() : 0;

            // base scroll amount: places center of tab in center of parent
            // 注意這里的,**selectedChild.getLeft()**,getLeft()是計(jì)算相對(duì)于父布局的左邊距,那么如果設(shè)置了margin的話(huà),childView相當(dāng)于父布局的位置就會(huì)變化了,那么我們?yōu)榱吮苊膺@種情況可以使用padding來(lái)改變距離,避免使原來(lái)的邏輯收到干擾。一切真想大白了!
            int scrollBase = selectedChild.getLeft() + (selectedWidth / 2) - (getWidth() / 2);
            // offset amount: fraction of the distance between centers of tabs
            int scrollOffset = (int) ((selectedWidth + nextWidth) * 0.5f * positionOffset);

            return (ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_LTR)
                    ? scrollBase + scrollOffset
                    : scrollBase - scrollOffset;
        }
        return 0;
    }
TabLayout設(shè)置最后一個(gè)默認(rèn)選中時(shí)位置錯(cuò)亂

因?yàn)榈腡abLayout在dialog中, 而我想讓dialog展示之前就把數(shù)據(jù)設(shè)置完畢并且切換到默認(rèn)的下標(biāo)。

 fun showPos(index: Int) {
        mTabLayout.getTabAt(index)?.select()
        show()
    }

切換成功了,但是位置不對(duì)!

更操蛋的是將對(duì)話(huà)框dissmiss后再重新show的時(shí)候就正確了!!(這個(gè)是最氣的

最后debug半天多終于搞定了!現(xiàn)在!讓我們回歸當(dāng)出問(wèn)題時(shí)的代碼(案發(fā)現(xiàn)場(chǎng)

    private fun customTabWidth(tabLayout: TabLayout) {
        // 嫌疑人在這里
        //        ↓
        tabLayout.post {

            try {
                //拿到tabLayout的mTabStrip屬性
                val mTabStripField = tabLayout.javaClass.getDeclaredField("mTabStrip")
                mTabStripField.isAccessible = true

                val mTabStrip = mTabStripField.get(tabLayout) as LinearLayout

                
                // balabala 修改Tab間距的代碼

                    tabView.invalidate()
                }
            } catch (e: NoSuchFieldException) {
                e.printStackTrace()
            } catch (e: IllegalAccessException) {
                e.printStackTrace()
            }

        }
    }

沒(méi)錯(cuò)就是這個(gè)tabLayout.post(Runnable runnable)啊!當(dāng)我第一次運(yùn)行的時(shí)候,TabLayout雖然已經(jīng)創(chuàng)建但是并沒(méi)有依附到任何Window中,導(dǎo)致runnable會(huì)被添加到運(yùn)行隊(duì)列中,然后等到這個(gè)View已經(jīng)添加到Window時(shí),再一起運(yùn)行!那么會(huì)導(dǎo)致的現(xiàn)象就是,我第一次修改這個(gè)寬度的時(shí)候,其實(shí)并沒(méi)有真的修改,真的修改是在對(duì)話(huà)框展示后,TabLayout依附到View了,那時(shí)才會(huì)運(yùn)行!所以才會(huì)出現(xiàn)第一次的位置是錯(cuò)誤的,第二次的位置是正確的情況!!

關(guān)于本人使用TabLayout時(shí)的坑就介紹到這里了!希望能夠幫助的了大家!!

以上。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/74493.html

相關(guān)文章

  • ios局部滾動(dòng)坑及解決方案

    摘要:起因最近幾天在寫(xiě)一個(gè)滾動(dòng)加載更多數(shù)據(jù)的插件,為局部滾動(dòng)寫(xiě)時(shí),遇到了很多局部滾動(dòng)的坑,在這里分享一下這些坑的解決方案。約定把產(chǎn)生滾動(dòng)條的元素稱(chēng)之為視窗全局滾動(dòng)滾動(dòng)條在或者父級(jí)元素上。坑一瀏覽器局部滾動(dòng)默認(rèn)沒(méi)有彈性滾動(dòng)的效果。 起因 最近幾天在寫(xiě)一個(gè)滾動(dòng)加載更多數(shù)據(jù)的插件(Scrollload),為局部滾動(dòng)寫(xiě)demo時(shí),遇到了很多局部滾動(dòng)的坑,在這里分享一下這些坑的解決方案。以下的坑只針對(duì)...

    yhaolpz 評(píng)論0 收藏0
  • 事務(wù)與多線(xiàn)程坑及調(diào)優(yōu)Tips

    摘要:起因及介紹在處理原始對(duì)賬文件的時(shí)候,我將數(shù)據(jù)歸類(lèi)后批量存入相應(yīng)的表中。結(jié)論事務(wù)只能管著開(kāi)啟事務(wù)的線(xiàn)程,其他子線(xiàn)程出了問(wèn)題都感知不到,所以在多線(xiàn)程環(huán)境操作要慎重。高頻容易搞死服務(wù)器,低頻會(huì)阻塞自身程序。重試次數(shù)和超時(shí)時(shí)間根據(jù)業(yè)務(wù)情況設(shè)置。 起因及介紹 在處理原始對(duì)賬文件的時(shí)候,我將數(shù)據(jù)歸類(lèi)后批量存入相應(yīng)的表中。在持久化的時(shí)候,用了parallelStream(),想著同時(shí)存入很多表這樣可...

    wums 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<