BottomBar手动切换Tab总结
Posted Vicent_9920
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了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 的时候,我脑海里马上蹦出来了两个知识片段
- 关于事件分发的时候,听视频里面的讲师说——所有的点击事件都是硬件将点击事件生成消息,发送给系统,系统然后分发给对应的 Activity 并继续分发……
- 另一个知识片段是底层系统在在页面刷新的时候依稀记得是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);
鸣谢:
以上是关于BottomBar手动切换Tab总结的主要内容,如果未能解决你的问题,请参考以下文章
android开发:底部导航条的实现 | navigation tab
android开发:底部导航条的实现 | navigation tab | activity的创建