参考资料:
ButtonNavigationViewEx
https://blog.csdn.net/qq_35064774/article/details/54177702
反射机制
https://blog.csdn.net/ju_362204801/article/details/90578678
1.
最近学习了ButtonNavigationView,发现用起来很方便但是有一定的局限性,然后去网络查资料看人家是怎么修改的。利用反射官方存在的View去构造自己自定义个的"View",这样子就可以突破这个限制性。所以用博客记录一下。
ButtonNavigationView
问题1:ButtonNavigationView是什么?
回答1:ButtonNavigationView是Android谷歌官方中的底部导航栏的组件,也比较容易实现自己的功能,但是存在一定的局限性,只能使用其内部的方法和定义。若想改变样式/文字大小,如果没有方法放出则无法修改。
Badge
问题2:Badge是啥?
问题2:Badge就是角标,Android的View如何添加角标去告诉用户这有变化之类的操作。比如APP有的更新了,那就给这个View添加一个角标。
反射
问题3:反射是什么?
回答3:反射就是把Java类中的各个部分,映射成一个个的Java对象,拿到这些对象后可以做一些事情。简单点说:反射就是能把类中的属性,方法,字段映射成一个对象。
好了,了解了三个基本概念之后,WTF?这这这这,之间有什么联系吗?
当然有联系了,来,我们来利用第三方库去给你的ButtonNavigationView中的底部栏某个 mune来个角标。
~~
- 这里留个疑问,那我自建一个BadgeView如何弄,讲道理,个人认为和自定义View应该是一个尿性。有空可以去人家的项目里面去解析。
~~
Badge库:
https://github.com/qstumn/BadgeView
发现方法:
new QBadgeView(context).bindTarget(textview).
这不是很简单吗?
只要bindTarget内知道相应的View就可以了!
那我这样写:
//这是错误的写法!!!!!!!!!!!!!
BottomNavigationView mbnView;
new QBadgeView(context).bindTarget(mbnView).
如果比较熟悉的人,应该一眼就看出来问题了。要的在ButtonNavigationView下的mune出现角标,而不是整个ButtonNavigationView出现角标。
那我赶紧到ButtonNavigationView类中找公开的方法,可以获取到其中的mune项,但是最后发现只有setOnNavigationItemSelectedListener,只用来设定点击监听器的。 好了~需求解决不了,我放弃了GG.
作为积极向上的社会主义接班人,怎么能埋没了胸前那如此靓丽的红领巾呢,不行,我要解决这个问题!!!不就是找到ButtonNavigationView里面的View嘛,打开源码,我取出来不就完了。打开一看,里面有个属性BottomNavigationMenuView,这不就是我想要的嘛?但是转眼一看,不好意思,全是private,谷歌并不想让你修改。虽然上帝把你门关上了,但是窗户还是不会忘记焊上的。
(那简单啊,那我复制一份,当做我的自定义控件,再把我需要的View全部改为public不就完了。—作者没试过,不过想想好像是可以的吧。
不过大佬说的好,缺点不少:需要把整个控件的代码都复制一份,每次官方对控制做出修改后,无法享受新特性。每次更新,你都要修改代码,迭代累死你啊!)
那这时候,反射的用处就出来了,还记得吗?
反射就是把Java类中的各个部分,映射成一个个的Java对象,拿到这些对象后可以做一些事情。 拥有反射的我,可以为所欲为,管你是不是private,老子是开发者,我是上帝!(至于原理啥的,去参考资料去看。这里不解释了。)
这时候思路就清楚多了吧,先分析ButtonNavigationView的源码,发现里面又特定的方法和子View,但是全都是private.那我又要修改,那怎么办呢,那当然是通过反射就可以获取这个类,从而去构造出我们自己的View。
最后,我们看看大佬是怎么实现的,只做分析和想法,个人作为初学者还没那么强大的实力,只能讲一下想法和思路仅供参考。
第一步:
//反正根据这个getBottomNavigationItemView方法获得mute子项
return new QBadgeView(this)
.setBadgeNumber(number)
.bindTarget(bind.bnve.getBottomNavigationItemView(position))
第二步:(大佬有一个接口类作为防代码冗余,这就不贴出来了)
public BottomNavigationItemView getBottomNavigationItemView(int position) {
return getBottomNavigationItemViews()[position];
}
//再跟踪进入getBottomNavigationItemViews()
public BottomNavigationItemView[] getBottomNavigationItemViews() {
if (null != mButtons)
return mButtons;
BottomNavigationMenuView mMenuView = getBottomNavigationMenuView();
mButtons = getField(mMenuView.getClass(), mMenuView, "buttons");
return mButtons;//private BottomNavigationItemView[] mButtons
}
第三步: 可以看到,getBottomNavigationMenuView 和getField两个方法则返回了原本private的BottomNavigationItemView.
这才是我们想要的,而为什么要得到这个,是根据源码分析出来的,然后再看这个字段是在藏哪里的。
BottomNavigationView–>BottomNavigationMenuView–>BottomNavigationItemView
而得到这个字段的手段就是反射,getBottomNavigationMenuView实质上也是使用方法getField.代码如下:
private <T> T getField(Class targetClass, Object instance, String fieldName) {
try {
Field field = targetClass.getDeclaredField(fieldName);
field.setAccessible(true);
return (T) field.get(instance);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
我给出的资料,用的是Class.ForName.而这里用的是getDeclaredField.为什么是这个方法,可以去官方文档去查看。这样子,通过反射得到了我们想要的View.
这样我们就可以解决我们的问题了。
思路总结:
一些官方控件,如果满足不了你的需求,就改造它!获取不到的资源就改造它!反正差不多就是自定义View.通过反射可以使用它的内部方法。
好嘞,学习总结结束!