1. 程式人生 > >TabLayout設置下劃線(Indicator)寬度

TabLayout設置下劃線(Indicator)寬度

csdn lac 獲取 .net print 原因 通過 nsh gin

再戰TabLayout之下劃線寬度

這周的需求搞定之後,想到之前有一個小瑕疵,反正沒什麽事,索性較量較量

如圖官方原版就是小瑕疵,反射版本就是最終技術分享圖片

解決方案-Demo源碼

先講解決方案。直接貼代碼(要在tabLayout添加完所有的tab後調用)

[java] view plain copy
  1. public void reflex(final TabLayout tabLayout){
  2. //了解源碼得知 線的寬度是根據 tabView的寬度來設置的
  3. tabLayout.post(new Runnable() {
  4. @Override
  5. public void run() {
  6. try {
  7. //拿到tabLayout的mTabStrip屬性
  8. LinearLayout mTabStrip = (LinearLayout) tabLayout.getChildAt(0);
  9. int dp10 = dip2px(tabLayout.getContext(), 10);
  10. for (int i = 0; i < mTabStrip.getChildCount(); i++) {
  11. View tabView = mTabStrip.getChildAt(i);
  12. //拿到tabView的mTextView屬性 tab的字數不固定一定用反射取mTextView
  13. Field mTextViewField = tabView.getClass().getDeclaredField("mTextView");
  14. mTextViewField.setAccessible(true);
  15. TextView mTextView = (TextView) mTextViewField.get(tabView);
  16. tabView.setPadding(0, 0, 0, 0);
  17. //因為我想要的效果是 字多寬線就多寬,所以測量mTextView的寬度
  18. int width = 0;
  19. width = mTextView.getWidth();
  20. if (width == 0) {
  21. mTextView.measure(0, 0);
  22. width = mTextView.getMeasuredWidth();
  23. }
  24. //設置tab左右間距為10dp 註意這裏不能使用Padding 因為源碼中線的寬度是根據 tabView的寬度來設置的
  25. LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams();
  26. params.width = width ;
  27. params.leftMargin = dp10;
  28. params.rightMargin = dp10;
  29. tabView.setLayoutParams(params);
  30. tabView.invalidate();
  31. }
  32. } catch (NoSuchFieldException e) {
  33. e.printStackTrace();
  34. } catch (IllegalAccessException e) {
  35. e.printStackTrace();
  36. }
  37. }
  38. });
  39. }




問題解決思路

第一反應是找系統的方法和屬性

發現只有設置tabIndicatorHeight的屬性 並沒有寬度的屬性

接著百度,一百度就看到幾個博客,宣稱可以解決這個問題,我們先看看他們的解決方案

傳送門這種解決方案僅限於所有的tabView的text字數都是相同字數,比如所有的圖中所有的tab字數都是2個。其實思路是錯的,沒有研究源碼詳細實現

他的思路是設置tabView的padding為0,並且設置了margin

這種方案錯誤的原因是,tablayout會強制設置tabView的寬度為 幾個tabView中最寬的寬度,比如4個字的tabview和2個字的tabview的組合,兩個tabview的寬度強制為4個字的tabview的寬度

下面會證實這一點

技術分享圖片

那只有查源碼了唄,tab的創建是 tablayout.addTab();方法構造的 具體代碼如下

[java] view plain copy
  1. tabLayout.addTab(tabLayout.newTab().setText("生鮮食品"));



直接查這個方法,通過幾個重載方法(addTab(Tab tab)->addTab(Tab tab,boolean setSelected)->addTab( Tab tab, int position, boolean setSelecte); 跳轉如下代碼


技術分享圖片

從註釋看就是添加一個tab到這個layout上 具體實現是在addTabView(Tab tab)裏面,繼續看這個方法

技術分享圖片

可以看到最後添加到mTabStrip中,我們再來看看TabView裏面有什麽東西

技術分享圖片

光從屬性可以看出TabView可以自定義的,而且並沒有發現Indicator線的痕跡,我猜測他可能放在layout(mTabStrip)裏面,因為我以前寫這樣效果 這樣寫過,那我們就來看mTabStrip

技術分享圖片

查看類中,發現mSelectedIndicatorHeight,眼睛一亮,下劃線高度!!,就是畫線的地方。追蹤mIndicatorLeft和mIndicatorRight的來路,幾經追蹤,發現如下代碼

技術分享圖片

如圖,selectedTitle就是TabView,直接獲取了左邊坐標和右邊坐標,也就說是線的寬度就是tabview的寬度,那疑問又來了,為什麽我們兩個字的tabView和4個字的tabView是一樣寬度,先去看看SlidingTabStrip的onMeasure方法,如下圖

技術分享圖片

我日,我怎麽感覺寫TabLayout這個類的人是強迫癥,有沒有?第一個for循環幹的事就是記錄下來,所有tabView中的最大寬度,第二個循環就是把所有的tabView的寬度設置為第一個循環得到的最大寬!!!

罪魁禍首是找到了,這時候能動態代理一個重寫onMeasure方法的SlidingTabStrip對象塞進去,也可以解決這個問題,你會發現SlidingTabStrip是private的!!!!!!!

思路一轉,系統是強制設置所有tabview的寬度為 最寬那個tabview的寬度,那我重新設置一遍tabView的寬度即可,解決問題(其實中間還嘗試過調用setIndicatorPosition方法,但是系統源碼,在多個時期調用這個方法,所以斃掉了)

那最上面的解決方案就來了

1、通過反射拿到SlidingTabStrip,通過遍歷拿到tabview,繼續通過反射拿到textview,然後設置Tabview的寬度為textview的寬度

2、為了美觀我們可以設置一下tabview的margin,不設置會連在一起

轉載 https://blog.csdn.net/qq_35473951/article/details/78653567

TabLayout設置下劃線(Indicator)寬度