TabLayout設置下劃線(Indicator)寬度
再戰TabLayout之下劃線寬度
這周的需求搞定之後,想到之前有一個小瑕疵,反正沒什麽事,索性較量較量
如圖官方原版就是小瑕疵,反射版本就是最終
解決方案-Demo源碼
先講解決方案。直接貼代碼(要在tabLayout添加完所有的tab後調用)
[java] view plain copy
- public void reflex(final TabLayout tabLayout){
- //了解源碼得知 線的寬度是根據 tabView的寬度來設置的
- tabLayout.post(new Runnable() {
- @Override
- public void run() {
- try {
- //拿到tabLayout的mTabStrip屬性
- LinearLayout mTabStrip = (LinearLayout) tabLayout.getChildAt(0);
- int dp10 = dip2px(tabLayout.getContext(), 10);
- for (int i = 0; i < mTabStrip.getChildCount(); i++) {
- View tabView = mTabStrip.getChildAt(i);
- //拿到tabView的mTextView屬性 tab的字數不固定一定用反射取mTextView
- Field mTextViewField = tabView.getClass().getDeclaredField("mTextView");
- mTextViewField.setAccessible(true);
- TextView mTextView = (TextView) mTextViewField.get(tabView);
- tabView.setPadding(0, 0, 0, 0);
- //因為我想要的效果是 字多寬線就多寬,所以測量mTextView的寬度
- int width = 0;
- width = mTextView.getWidth();
- if (width == 0) {
- mTextView.measure(0, 0);
- width = mTextView.getMeasuredWidth();
- }
- //設置tab左右間距為10dp 註意這裏不能使用Padding 因為源碼中線的寬度是根據 tabView的寬度來設置的
- LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) tabView.getLayoutParams();
- params.width = width ;
- params.leftMargin = dp10;
- params.rightMargin = dp10;
- tabView.setLayoutParams(params);
- tabView.invalidate();
- }
- } catch (NoSuchFieldException e) {
- e.printStackTrace();
- } catch (IllegalAccessException e) {
- e.printStackTrace();
- }
- }
- });
- }
問題解決思路
第一反應是找系統的方法和屬性
發現只有設置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- 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)寬度