BottomBar手动切换Tab总结

最近在使用 BottomBar 的时候遇到一个问题,解决过程颇费周章,幸运的是还好解决了,因此写下自己解决问题的方法以及解决的思路。

问题:在点击BottomBar的指定tab的时候,跳转到另一个tab的页面,并手动切换tab。

如果听不明白,那咱们就看图说话,如图:
这里写图片描述

还是没有看懂?那我解释一下,当我们点击次数大于或等于5的时候(替代条件判断),当点击到 nearbyTab 的时候,需要切换到 favoritesTab 。
初看问题,觉得很好简单,在点击事件中切换一下默认选择的 Tab 即可,虽然对 BottomBar 不熟悉,但是看看 README 以及百度一下还是能很快解决问题,代码如下:

final BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            int clickCount = 0;
            @Override
            public void onTabSelected(@IdRes int tabId) {

                messageView.setText(TabMessage.get(tabId, false));
                switch (tabId){

                    case R.id.tab_nearby:
                        bottomBar.selectTabAtPosition(1);
                        break;
                }
                clickCount++;
            }
        });

结果发现事与愿违,并不能达到预期效果,咱们看看这个未达预期的效果是什么样?

这里写图片描述
这是为什么呢?
我们跟着断点看看源码里面是否产生了什么异常?
先看看默认切换 tab 的方法:

    /**
     * Select a tab at the specified position.
     *
     * @param position the position to select.
     */
    public void selectTabAtPosition(int position) {
        selectTabAtPosition(position, false);
    }

这个方法其实是调用了另一个方法 void selectTabAtPosition(int position, boolean animate) 方法,咱们跟进去看看:

    /**
     * Select a tab at the specified position.
     *
     * @param position the position to select.
     * @param animate  should the tab change be animated or not.
     */
    public void selectTabAtPosition(int position, boolean animate) {
        if (position > getTabCount() - 1 || position < 0) {
            throw new IndexOutOfBoundsException("Can't select tab at position " +
                    position + ". This BottomBar has no items at that position.");
        }

        BottomBarTab oldTab = getCurrentTab();
        BottomBarTab newTab = getTabAtPosition(position);

        oldTab.deselect(animate);
        newTab.select(animate);

        updateSelectedTab(position);
        shiftingMagic(oldTab, newTab, animate);
        handleBackgroundColorChange(newTab, animate);
    }

方法执行完毕也并未产生任何异常,且发现 oldTab 以及 newTab 也是正确的,oldTab 是第一个Tab,newTab 是第二个(手动切换的 Tab 确实是第二个),这就奇了怪了!
这里写图片描述
但是我们知道,手动切换不行,点击 tab 的自动切换却木有任何问题,因此我们跟踪源码,发现 BottomBar 有一个 void onClick(View target)

  @Override
    public void onClick(View target) {
        if (!(target instanceof BottomBarTab)) return;
        handleClick((BottomBarTab) target);
    }

既然如此,那么我们能不能通过这个 Click 事件来操作 tab 切换呢?问谁都不如问代码,毕竟代码是不会骗人的,我们修改代码,再来看看效果:

final BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            int clickCount = 0;
            @Override
            public void onTabSelected(@IdRes int tabId) {

                messageView.setText(TabMessage.get(tabId, false));
                switch (tabId){

                    case R.id.tab_nearby:
//                        bottomBar.selectTabAtPosition(1);
                        bottomBar.onClick(bottomBar.getTabAtPosition(1));
                        break;
                }
                clickCount++;
            }
        });

结果发现效果与上面的图一样,这个时候就更加奇怪了!
这里写图片描述
好,到这里咱们就直接公布答案了!


即通过Handler或者Runnable切换线程来执行tab的切换操作:


final BottomBar bottomBar = (BottomBar) findViewById(R.id.bottomBar);
        bottomBar.setOnTabSelectListener(new OnTabSelectListener() {
            int clickCount = 0;
            @Override
            public void onTabSelected(@IdRes int tabId) {

                messageView.setText(TabMessage.get(tabId, false));
                switch (tabId){

                    case R.id.tab_nearby:

                        bottomBar.post(new Runnable() {
                            @Override
                            public void run() {
                                runOnUiThread(new Runnable() {
                                    @Override
                                    public void run() {
                                        bottomBar.onClick(bottomBar.getTabAtPosition(1));
                                    }
                                });
                            }
                        });

                        break;
                }
                clickCount++;
            }
        });

到这里就结束了吗?不,当然不!按照郭神的指导精神(不要问我是怎么知道的,毕竟整天混 GayGayUp 群度日不是白混了的!),学会写 HelloWorld 之后想要进阶,就得在 Helloworld 显示不出来的时候,得查看源码,知道其所以然。

我是通过 Click 事件查看源码,然后发现其思路逻辑是这样:设置 tab 之间的切换——调用 tab 的点击事件——更新 currentTabPosition 标志——执行切换动画(调用 onLayout 方法)。
这个时候我们明白了为什么在 tab 点击事件执行 BottomBar 内的 UI 操作会发生有意外惊喜,因为执行切换动画的时候原来的 oldTab 、newTab 属于一个过程,然后在修改的过程中,又重新修改了 tab 的 UI 内容,因此会造成上面的意外情况!

不知道大家会不会跟我当时一样觉得奇怪?
为什么 tab 之间的切换设置了并未立即生效,而是在 tab 的点击事件执行才会才能生效?

为了验证上面的判断,我还另外写了一个自定义 TextView ,然后在设置 text 之后将 text 作为 log 输出,运行效果发现却是先输出 text 的 log ,然后输出 TextView 的 onLayout 方法内的 log ,证明了上面的判断:

UI 操作并不是立即生效,而是在执行完其它业务之后才执行UI操作,此过程并非固定的先后顺序。

后来经过请教大佬,才得以醍醐灌顶,大佬的原话如此:

所有的安卓事件分发,页面绘制之类的,都是通过底层的 handler 发送的一次又一次的消息进行刷新的,不是说都是同步运行的。你 setText 和打印 log 在当前这个消息内。然后更新 text 的操作又是另一个新的消息。
嗯,理解起来其实就是事件驱动。通过一个又一个具体的 msg 上层做对应的操作

说到 Handler 的时候,我脑海里马上蹦出来了两个知识片段

  1. 关于事件分发的时候,听视频里面的讲师说——所有的点击事件都是硬件将点击事件生成消息,发送给系统,系统然后分发给对应的 Activity 并继续分发……
  2. 另一个知识片段是底层系统在在页面刷新的时候依稀记得是16毫秒页面会刷新一次

这个知识点其实是很基础的,Handler 的 Message、Message Queue以及 Looper 都是老生常谈了,但是一涉及到具体业务情景的时候,却不能及时想到,这便是我们菜鸟和大佬的差距吧!
最后还有一个坑,就是我们上面已经实现了业务,但是实现的方法依然是通过点击事件,而 BottomBar 有专门提供 void selectTabAtPosition(int position) 方法,我们尝试使用这个方法的时候依然有意外情况发生,这个是怎么回事呢?

以下内容仅为我自己的理解,未得到验证


原来我们在切换tab的过程中使用了动画(150毫秒时长),而在动画切换过程中我们直接覆盖了设置的内容,因此最后看到的是动画后的内容。因为我们在tab切换过程中——oldTab.deselect(animate);newTab.select(animate);
并未并未发现取消之前的动画,此为其一;其二是我通过在 BottomBar 的 OnTabSelectListener 事件回调中,另修改了 Activity 页面上的 title ,肉眼看上去是先修改的 title 而后修改 tab 之间的切换。


由于上面的验证方法并不科学,因此还请大佬指出不足!
最后回到上面的主题,我们还是可以通过上面的方法来实现 tab 的切换,只是此方法默认不使用动画,而我们再此过程中加上动画效果即可!

bottomBar.selectTabAtPosition(1,true);

鸣谢:

豪哥
blue

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值