TabLayout 踩坑之 onTabSelected沒有被回調(diào)的問題

封面.png

一、 問題描述

最近項目中有個需求:一個頁面頂部有3個tab,每一個tab分別展示一個不同的頁面,點擊tab 切換到對應(yīng)頁面槽奕。進入頁面是默認(rèn)選中第一個頁面看峻。

這不很簡單的一個需求嘛?很明顯未桥,用TabLayout 分分鐘實現(xiàn)笔刹,于是打開Android Studio ,幾分鐘后寫下了如下代碼:

public class TabActivity extends AppCompatActivity {
    private TabLayout mTabLayout;
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.tab_layout_ac2);
        mTabLayout = (TabLayout) findViewById(R.id.tab_layout2);

        mTabLayout.addTab(mTabLayout.newTab().setText("個性推薦"),true);//設(shè)置默認(rèn)選中
        mTabLayout.addTab(mTabLayout.newTab().setText("歌單"));
        mTabLayout.addTab(mTabLayout.newTab().setText("主播電臺"));

        final List<Fragment> fragments = new ArrayList<>();
        fragments.add(FirstFragment.newInstance());
        fragments.add(SecondFragment.newInstance());
        fragments.add(ThirdFragment.newInstance());

        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                Log.e("TAG","tab position:"+tab.getPosition());
                replaceFragment(fragments.get(tab.getPosition()));
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
    }

    private void replaceFragment(Fragment fragment){
        getSupportFragmentManager().beginTransaction().replace(R.id.tab_container,fragment).commit();
    }
}

寫完之后,一運行钢属,發(fā)現(xiàn)tab 顯示出來了徘熔,第一個tab也選中了(效果如下圖),但是第一頁的內(nèi)容咋沒展示出來呢淆党? 然后點擊tab切換酷师,切換到后面2個tab時讶凉,可以加載出頁面,然后再次點擊第一個tab ,第一個tab 的頁面也展示出來了山孔。

未展示頁面效果.png

第一次進來時懂讯,tab 下面的頁面內(nèi)容沒有展示出來,很明顯台颠,那就是第一次進來的時候onTabSelected 回調(diào)沒有被執(zhí)行褐望。因為我們是在onTabSelected 來加載頁面的。經(jīng)過幾次反復(fù)測試(日志和斷點調(diào)試)串前,確定了是第一次進入的時候瘫里,onTabSelected沒有被回調(diào)。

那么荡碾,為什么第一次進入的時候谨读,onTabSelected沒有被回調(diào)了?反復(fù)檢查了幾次代碼坛吁,沒有發(fā)現(xiàn)問題劳殖。既然沒有發(fā)現(xiàn)問題,那么拨脉,我們就只有去看源碼了哆姻,看一下TabLayoout 初始化完成后,在什么時候調(diào)用的onTabSelected 回調(diào)方法玫膀?

二矛缨、源碼追蹤

我們要看一下源碼中TabLayout初始化后,在什么時候調(diào)用的onTabSelected匆骗。我們注意到劳景,添加Tab的時候,有這么一個方法:

 mTabLayout.addTab(mTabLayout.newTab().setText("個性推薦"),true);

addTab 方法有2個參數(shù)碉就,第一個是要添加的Tab,第二個參數(shù)是是否設(shè)置為默認(rèn)選中盟广。上面這行代碼的意思是,添加一個Tab瓮钥,并且設(shè)置這個tab為默認(rèn)選中的Tab筋量。

接下來就走讀一下源碼,看一下在何時回調(diào)的onTabSelected方法:

1,首先看一下OnTabSelectedListener 的設(shè)置

 public void addOnTabSelectedListener(@NonNull OnTabSelectedListener listener) {
        if (!mSelectedListeners.contains(listener)) {
            mSelectedListeners.add(listener);
        }
    }

很簡單碉熄,就是將OnTabSelectedListener保存到一個列表里桨武。沒有做其他事情。

2, 以 addTab 方法為入口锈津,順藤摸瓜呀酸。

    // 添加一個Tab ,并且設(shè)置為是否選中
    // 實際調(diào)用方法 addTab(@NonNull Tab tab, int position, boolean setSelected) 
    public void addTab(@NonNull Tab tab, boolean setSelected) {
        addTab(tab, mTabs.size(), setSelected);
    }

 //1, 首先將Tab 保存到一個列表中,記錄位置
 //2, 將tab 添加到TabLayout 中
 //3, 判斷時候選中(這就是我們要的)
 public void addTab(@NonNull Tab tab, int position, boolean setSelected) {
        if (tab.mParent != this) {
            throw new IllegalArgumentException("Tab belongs to a different TabLayout.");
        }
        configureTab(tab, position);
        addTabView(tab);
        // 是否選中
        if (setSelected) {
            tab.select();
        }
    }

如上代碼琼梆,如果setSelectedtrue,就調(diào)用了tab 的select()方法性誉,我們看一下select()方法:

 public void select() {
            if (mParent == null) {
                throw new IllegalArgumentException("Tab not attached to a TabLayout");
            }
            mParent.selectTab(this);
        }

調(diào)用了Parent的select方法窿吩,Parent 是誰?當(dāng)然是TabLayout 啦错览。所以繼續(xù)深入纫雁,看看TabLayout的select方法。

  void selectTab(Tab tab) {
       //實際調(diào)用 selectTab(final Tab tab, boolean updateIndicator) 
        selectTab(tab, true);
    }

 // 
  void selectTab(final Tab tab, boolean updateIndicator) {
        final Tab currentTab = mSelectedTab;

        if (currentTab == tab) { // 如果新選中的Tab 和當(dāng)前Tab 相同倾哺,回調(diào)onTabReselected 方法
            if (currentTab != null) {
                dispatchTabReselected(tab);
                animateToTab(tab.getPosition());
            }
        } else { // 如果不同轧邪,則回調(diào) onTabSelected方法
            final int newPosition = tab != null ? tab.getPosition() : Tab.INVALID_POSITION;
            if (updateIndicator) {
                if ((currentTab == null || currentTab.getPosition() == Tab.INVALID_POSITION)
                        && newPosition != Tab.INVALID_POSITION) {
                    // If we don't currently have a tab, just draw the indicator
                    setScrollPosition(newPosition, 0f, true);
                } else {
                    animateToTab(newPosition);
                }
                if (newPosition != Tab.INVALID_POSITION) {
                    setSelectedTabView(newPosition);
                }
            }
            if (currentTab != null) {
                dispatchTabUnselected(currentTab);
            }
            mSelectedTab = tab; // 記錄選中的Tab 
            if (tab != null) {
                dispatchTabSelected(tab); // 處理選中Tab 
            }
        }
    }

TabLayout 的selectTab方法中最終調(diào)用了dispatchTabSelected方法:

 private void dispatchTabSelected(@NonNull final Tab tab) {
        for (int i = mSelectedListeners.size() - 1; i >= 0; i--) {
            mSelectedListeners.get(i).onTabSelected(tab); // 找到了,在這里循環(huán)列表羞海,分別調(diào)用onTabSelected 方法忌愚。
        }
    }

好了,到這里就找到了onTabSelected回調(diào)的地方了扣猫,有沒有很熟悉 mSelectedListeners?當(dāng)然了菜循,在我們調(diào)用addOnTabSelectedListener設(shè)置監(jiān)聽器的時候翘地,就是保存到mSelectedListeners的申尤。我們來捋一捋:

源碼流程.png

如上圖:
當(dāng)我們執(zhí)行addTab方法添加Tab的時候,最后會調(diào)用到 dispatchTabSelected方法衙耕,在dispatchTabSelected方法里面調(diào)用addOnTabSelectedListeneronTabSelected()方法昧穿。但是這個時候,mSelectedListeners 為空(因為這個時候我們還沒有設(shè)置OnTabSelectedListener)橙喘,因此时鸵,就沒有回調(diào)到onTabSelected

分析到此我們也就明白了厅瞎,第一次沒有回調(diào)到 onTabSelected 方法饰潜,是因為我們寫的程序的順序問題,應(yīng)該在添加Tab 之前 添加OnTabSelectedListener 監(jiān)聽和簸。

三彭雾、解決方案

通過上面的源碼分析,我們知道了锁保,第一次沒有執(zhí)行OnTabSelected回調(diào)薯酝,是因為我們的代碼順序問題(這個是Google 的坑,設(shè)計得有問題八狻)吴菠,因此,要想第一次進入的時候回調(diào)到OnTabSelected方法浩村,我們應(yīng)該先設(shè)置
addOnTabSelectedListener 監(jiān)聽器做葵,再添加Tab
,我們原來的程序順序調(diào)整一下:

        // 1, 設(shè)置 addOnTabSelectedListener
        // 設(shè)置 addOnTabSelectedListener 必須在 addTab 之前心墅。
        mTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() {
            @Override
            public void onTabSelected(TabLayout.Tab tab) {
                Log.e("TAG","tab position:"+tab.getPosition());
                replaceFragment(fragments.get(tab.getPosition()));
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab) {

            }

            @Override
            public void onTabReselected(TabLayout.Tab tab) {

            }
        });
        // 2.添加Tab 
        mTabLayout.addTab(mTabLayout.newTab().setText("個性推薦"),true);
        mTabLayout.addTab(mTabLayout.newTab().setText("歌單"));
        mTabLayout.addTab(mTabLayout.newTab().setText("主播電臺"));

通過如上調(diào)整酿矢,再運行程序重挑,完美解決,第一次進入如下:

展示了頁面內(nèi)容效果.png

四棠涮、總結(jié)

本篇文章是對TabLayout 使用過程中遇到的一個的坑記錄和總結(jié)谬哀,可能有的人不會遇到,也沒有人會注意到(如果你一開始就把順序?qū)憣α说脑挘┭戏荆鞘芳澹绻龅降脑挘丛创a才知道原因驳糯。這也是我們解決問題的一種思路篇梭。很多時候,我們需要去了解一下原理和源碼的實現(xiàn)酝枢,這樣才能幫助我們更好的理解恬偷,以免使用的時候踩坑。本文沒有介紹TabLayout的詳細(xì)使用方法和使用場景帘睦,要了解更多請看我前面的文章Material Design 之 TabLayout 使用

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末袍患,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子竣付,更是在濱河造成了極大的恐慌诡延,老刑警劉巖,帶你破解...
    沈念sama閱讀 219,039評論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件古胆,死亡現(xiàn)場離奇詭異肆良,居然都是意外死亡,警方通過查閱死者的電腦和手機逸绎,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,426評論 3 395
  • 文/潘曉璐 我一進店門惹恃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人棺牧,你說我怎么就攤上這事巫糙。” “怎么了陨帆?”我有些...
    開封第一講書人閱讀 165,417評論 0 356
  • 文/不壞的土叔 我叫張陵曲秉,是天一觀的道長。 經(jīng)常有香客問我疲牵,道長承二,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,868評論 1 295
  • 正文 為了忘掉前任纲爸,我火速辦了婚禮亥鸠,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己负蚊,他們只是感情好神妹,可當(dāng)我...
    茶點故事閱讀 67,892評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著家妆,像睡著了一般鸵荠。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上伤极,一...
    開封第一講書人閱讀 51,692評論 1 305
  • 那天蛹找,我揣著相機與錄音,去河邊找鬼哨坪。 笑死庸疾,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的当编。 我是一名探鬼主播届慈,決...
    沈念sama閱讀 40,416評論 3 419
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼忿偷!你這毒婦竟也來了金顿?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,326評論 0 276
  • 序言:老撾萬榮一對情侶失蹤牵舱,失蹤者是張志新(化名)和其女友劉穎串绩,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體芜壁,經(jīng)...
    沈念sama閱讀 45,782評論 1 316
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,957評論 3 337
  • 正文 我和宋清朗相戀三年高氮,在試婚紗的時候發(fā)現(xiàn)自己被綠了慧妄。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,102評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡剪芍,死狀恐怖塞淹,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情罪裹,我是刑警寧澤饱普,帶...
    沈念sama閱讀 35,790評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站状共,受9級特大地震影響套耕,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜峡继,卻給世界環(huán)境...
    茶點故事閱讀 41,442評論 3 331
  • 文/蒙蒙 一冯袍、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦康愤、人聲如沸儡循。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,996評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽择膝。三九已至,卻和暖如春检激,著一層夾襖步出監(jiān)牢的瞬間调榄,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,113評論 1 272
  • 我被黑心中介騙來泰國打工呵扛, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留每庆,地道東北人。 一個月前我還...
    沈念sama閱讀 48,332評論 3 373
  • 正文 我出身青樓今穿,卻偏偏與公主長得像缤灵,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子蓝晒,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,044評論 2 355

推薦閱讀更多精彩內(nèi)容