摘要:但對于我們的對于界面還原度要求較高,對于之間的間距也有一些要求,所以也要處理,對于間距部分的處理可以按照之前的方式通過反射來完成。注意,這種方式因為需要計算的文字寬度,所以要放到設置完所有的后調(diào)用。
修改下劃線寬度的坑
效果如下:
代碼實現(xiàn)方式:
如果想要實現(xiàn)這種效果,最主要控制的就是下劃線部分,現(xiàn)在網(wǎng)上有很多通過反射的方式來修改下劃線寬度的文章,但這段代碼如果實現(xiàn)我們想要的效果是不可能的,因為如果研究過源碼就知道Indicator的寬度跟Tab的寬度一致的,不能讓指示器的寬度小于Tab的寬度,所以我們只能另辟蹊徑:通過CustomView自己繪制線,通過添加OnTabSelectedListener來展示隱藏下劃線,讓原生的Indicaqtor高度為0也不會影響我們的展示。
OK! Talk is cheap, show me the code.
先隱藏原來的下劃線,讓其不顯示:其次設置CustomView效果: 再手動控制切換時Tab的下劃線:
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 } })
只是這樣的話,下劃線的問題解決了。但對于我們的UI對于界面還原度要求較高,對于Tab之間的間距也有一些要求,所以也要處理,對于間距部分的處理可以按照之前的方式通過反射來完成。注意,這種方式因為需要計算TextView的文字寬度,所以要放到設置完所有的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) { // 此時獲取到tabView其實是我們的CustomView val tabView = mTabStrip.getChildAt(i) // 找到我們的TextView val mTextView = tabView.findViewById修改完下劃線寬度后tab的滑動位置錯亂的坑(TabView的 MarginLeft & MarginRigt 導致的問題)(R.id.tab_tv_date) // 測量出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 // 如果是第一個View,則View左側(cè)還要30dp的空間需要padding // 有了TextView的寬度,我們可以根據(jù)自己想要的效果去設置Tab的寬度,Tab的實際間距其實是0,但我們可以 // 通過改變Padding的方式做出Tab間距的效果 // 對于為什么用**padding**而不是**margin**的原因,請向下看 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() } }
開始做的時候看網(wǎng)上相關的文檔然后做出的效果如下:
可以看到當滑過去以后,Tab又位移了一段距離,我想如果你的腦子沒有被驢踢過的話,肯定都知道滑動完位移后的位置是正確的位置,那么為什么會出現(xiàn)這種情況已經(jīng)如何解決??
看源碼!
因為是我們關聯(lián)的ViewPager滑動導致的Tab位移,那么就從ViewPager的移動時開始找線索,通過setupWithViewPager()方法中可以看到,TabLayout代碼中給ViewPager對象通過addOnPageChangeListener()方法添加了一個監(jiān)聽,那么我們看一下監(jiān)聽的回調(diào)好了!
public static class TabLayoutOnPageChangeListener implements ViewPager.OnPageChangeListener { // 忽略無關代碼 // **主要看這里哦~!~!~ ** @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進行滑動,穿進去目標下標和偏移量 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 // 如果更新指示器的話那么會走進這里,我們指示器都隱藏掉了無需看這里 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方法去移動位置,那么我們猜測應該是這個X位移距離算錯了,進去看一下 scrollTo(calculateScrollXForTab(position, positionOffset), 0); }
private int calculateScrollXForTab(int position, float positionOffset) { if (mMode == MODE_SCROLLABLE) { // 獲取目標的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()是計算相對于父布局的左邊距,那么如果設置了margin的話,childView相當于父布局的位置就會變化了,那么我們?yōu)榱吮苊膺@種情況可以使用padding來改變距離,避免使原來的邏輯收到干擾。一切真想大白了! 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設置最后一個默認選中時位置錯亂
因為的TabLayout在dialog中, 而我想讓dialog展示之前就把數(shù)據(jù)設置完畢并且切換到默認的下標。
fun showPos(index: Int) { mTabLayout.getTabAt(index)?.select() show() }
切換成功了,但是位置不對!
更操蛋的是將對話框dissmiss后再重新show的時候就正確了!?。ㄟ@個是最氣的
最后debug半天多終于搞定了!現(xiàn)在!讓我們回歸當出問題時的代碼(案發(fā)現(xiàn)場
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() } } }
沒錯就是這個tabLayout.post(Runnable runnable)??!當我第一次運行的時候,TabLayout雖然已經(jīng)創(chuàng)建但是并沒有依附到任何Window中,導致runnable會被添加到運行隊列中,然后等到這個View已經(jīng)添加到Window時,再一起運行!那么會導致的現(xiàn)象就是,我第一次修改這個寬度的時候,其實并沒有真的修改,真的修改是在對話框展示后,TabLayout依附到View了,那時才會運行!所以才會出現(xiàn)第一次的位置是錯誤的,第二次的位置是正確的情況!!
關于本人使用TabLayout時的坑就介紹到這里了!希望能夠幫助的了大家??!
以上。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://www.ezyhdfw.cn/yun/74493.html
摘要:起因最近幾天在寫一個滾動加載更多數(shù)據(jù)的插件,為局部滾動寫時,遇到了很多局部滾動的坑,在這里分享一下這些坑的解決方案。約定把產(chǎn)生滾動條的元素稱之為視窗全局滾動滾動條在或者父級元素上??右粸g覽器局部滾動默認沒有彈性滾動的效果。 起因 最近幾天在寫一個滾動加載更多數(shù)據(jù)的插件(Scrollload),為局部滾動寫demo時,遇到了很多局部滾動的坑,在這里分享一下這些坑的解決方案。以下的坑只針對...
摘要:起因及介紹在處理原始對賬文件的時候,我將數(shù)據(jù)歸類后批量存入相應的表中。結(jié)論事務只能管著開啟事務的線程,其他子線程出了問題都感知不到,所以在多線程環(huán)境操作要慎重。高頻容易搞死服務器,低頻會阻塞自身程序。重試次數(shù)和超時時間根據(jù)業(yè)務情況設置。 起因及介紹 在處理原始對賬文件的時候,我將數(shù)據(jù)歸類后批量存入相應的表中。在持久化的時候,用了parallelStream(),想著同時存入很多表這樣可...
閱讀 3116·2021-10-12 10:12
閱讀 5609·2021-09-26 10:20
閱讀 1578·2021-07-26 23:38
閱讀 2869·2019-08-30 15:54
閱讀 1705·2019-08-30 13:45
閱讀 2011·2019-08-30 11:23
閱讀 3163·2019-08-29 13:49
閱讀 932·2019-08-26 18:23