摘要:但對(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修改完下劃線(xiàn)寬度后tab的滑動(dòng)位置錯(cuò)亂的坑(TabView的 MarginLeft & MarginRigt 導(dǎo)致的問(wèn)題)(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() } }
開(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
摘要:起因最近幾天在寫(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ì)...
摘要:起因及介紹在處理原始對(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í)存入很多表這樣可...
閱讀 3045·2021-10-12 10:12
閱讀 5349·2021-09-26 10:20
閱讀 1515·2021-07-26 23:38
閱讀 2807·2019-08-30 15:54
閱讀 1636·2019-08-30 13:45
閱讀 1953·2019-08-30 11:23
閱讀 3078·2019-08-29 13:49
閱讀 819·2019-08-26 18:23