[安卓自定义控件]BadgeView源码解析
来源:互联网 发布:宏业斯维尔软件 编辑:程序博客网 时间:2024/06/06 23:55
[安卓自定义控件]BadgeView源码解析
说明
- BadgeView主要用于实现微信未读消息数目提示之类的小红点效果。
- 源代码为github上的BadgeView项目
- 项目的使用方法为直接将代码粘贴到本地,随后即可按照官方的写法使用
BadgeView badge = new BadgeView(getActivity());badge.setTargetView(myView);badge.setBadgeCount(42);
源码解析
继承自TextView的优点
项目实现的BadgeView继承自TextView,这相比于平时编写自定义View通常直接扩展自View有几个好处:
- 不用自己编写覆盖onMeasure、onSizeChanged、onLayout和onDraw这些方法而是利用TextView本身提供的机制来设置自身布局等。
- 由于扩展自TextView,也使得能够利用TextView提供的方法简单更改设置字体风格、大小,更改文字内容和背景等。我们可以看到相关的接口都最后调用了父类的方法:
//如构造器中初始化自身的init函数public BadgeView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); init(); }private void init() { if (!(getLayoutParams() instanceof LayoutParams)) { LayoutParams layoutParams = new LayoutParams( android.view.ViewGroup.LayoutParams.WRAP_CONTENT, android.view.ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.RIGHT | Gravity.TOP); //调用父类的方法来设置布局 setLayoutParams(layoutParams); } //以下方法均实际调用了父类的方法 // set default font setTextColor(Color.WHITE); setTypeface(Typeface.DEFAULT_BOLD); setTextSize(TypedValue.COMPLEX_UNIT_SP, 11); setPadding(dip2Px(5), dip2Px(1), dip2Px(5), dip2Px(1)); // 这里调用了自定义的setBackground方法,但只是为了提供一个圆形背景的ShapeDrawable对象,最终设置背景时仍调用了父类的setBackgroundDrawable。具体见setBackground源码。 setBackground(9, Color.parseColor("#d3321b")); //调用了父类的方法 setGravity(Gravity.CENTER); // default values setHideOnNull(true); //最终调用父类的方法,见setBadgeCount源码。 setBadgeCount(0); } public void setBackground(int dipRadius, int badgeColor) { int radius = dip2Px(dipRadius); float[] radiusArray = new float[] { radius, radius, radius, radius, radius, radius, radius, radius }; RoundRectShape roundRect = new RoundRectShape(radiusArray, null, null); ShapeDrawable bgDrawable = new ShapeDrawable(roundRect); bgDrawable.getPaint().setColor(badgeColor); //调用了父类的方法 setBackgroundDrawable(bgDrawable); }//调用了父类的方法 setText(String.valueOf(count)); }
支持setTargetView这一API
- 整个项目主要难点在于支持通过setTargetView这一API直接给需要提示的View加上提示。这样的API形式使得使用非常容易灵活,值得借鉴。
- 从实现原理上,BadgeView需要一直保持自身处于一个FrameLayout的父布局中(在init方法中已经通过getLayoutParams()判断自身所使用的布局参数并强制设置自己的布局参数为FrameLayout.LayoutParams,在这一布局参数设定下它只能使用FrameLayout作为父布局)。
具体当要将自己设置到targetView上时,步骤如下:
- 当需要将自己添加到指定的targetView上时,先将自己从自己所处的父布局中取出(如果有父布局的话)。
- 再检查targetView的父布局P,如果P已经是FrameLayout则直接将自己添加到P中。
- 否则,将targetView从其自身的父布局P中取出,并记录targetView原本在P中所处的位置索引。
- 在该位置索引处插入一个新的FrameLayout,假设称为F。
- 修改targetView的布局参数为FrameLayout.LayoutParams类型,且具体宽高参数均为ViewGroup.LayoutParams.MATCH_PARENT。于是保证了在视觉上targetView在P中所处的位置不变,尽管其实从此时开始它和P之间已经插入了一层FrameLayout。
- 由于所有放在FrameLayout里的控件,都按照层次堆叠在FrameLayout的左上角,后加进来的控件覆盖前面的控件,所以为了使得BadgeView能够优先于targetView显示,需要先往F里添加targetView,再添加BadgeView自己。至此,BadgeView又处于一个FrameLayout中了。
public void setTargetView(View target) { if (getParent() != null) { ((ViewGroup) getParent()).removeView(this); } if (target == null) { return; } if (target.getParent() instanceof FrameLayout) { ((FrameLayout) target.getParent()).addView(this); } else if (target.getParent() instanceof ViewGroup) { // use a new Framelayout container for adding badge ViewGroup parentContainer = (ViewGroup) target.getParent(); int groupIndex = parentContainer.indexOfChild(target); parentContainer.removeView(target); FrameLayout badgeContainer = new FrameLayout(getContext()); ViewGroup.LayoutParams parentLayoutParams = target.getLayoutParams(); badgeContainer.setLayoutParams(parentLayoutParams); target.setLayoutParams(new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); parentContainer.addView(badgeContainer, groupIndex, parentLayoutParams); badgeContainer.addView(target); badgeContainer.addView(this); } else if (target.getParent() == null) { Log.e(getClass().getSimpleName(), "ParentView is needed"); }}
其他说明
- BadgeView中其他类似修改布局参数,转换dip到px工具函数等方法都比较简单,可以直接阅读源码,这里不再赘述。
0 0
- [安卓自定义控件]BadgeView源码解析
- BadgeView-安卓数字显示控件
- android自定义控件--BadgeView
- BadgeView自定义数字提醒控件
- 自定义安卓控件
- 安卓----自定义控件
- 安卓自定义控件
- 安卓自定义控件
- 安卓自定义控件
- 安卓自定义控件
- 安卓自定义控件
- 安卓自定义控件
- 安卓自定义控件
- 安卓自定义控件,自定义控件属性
- 安卓自定义日历控件
- 安卓自定义日历控件
- 安卓自定义控件 - 进度条
- 安卓时间控件自定义
- R 如何在同一个界面画出多张图形
- Drupal7函数之drupal_set_message()
- Additive Number -- leetcode
- 浅析数据结构与算法12--无向图相关算法基础
- Java transient关键字使用
- [安卓自定义控件]BadgeView源码解析
- 161.In your database instance, the user sessions are connected to the database server from the remot
- ex3.py
- hdu5806 NanoApe Loves Sequence Ⅱ 前缀和 + 二分
- The file “XXX.app” couldn’t be opened because you don’t have permission to view it.
- 关于SVM支持向量机的学习
- 动态规划-满减优惠
- zcmu1461
- ex4.py