BadgeView源码分析

来源:互联网 发布:java web项目打war包 编辑:程序博客网 时间:2024/06/06 02:02

最近做项目时用到了BadgeView,当时没有看源码,现在闲下来了,就分析一下。

public class BadgeView extends TextView 
可以看到,BadgeView是通过继承了TextView来实现的。接下来,不要看源码,先从它的用法入手,分析哪个函数真正实现了它。它的用法很简单,如下面demo所示

public class MainActivity extends ActionBarActivity {    public final String tag = this.getClass().getName();    private BadgeView bv1;    private TextView tv;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        tv = (TextView) findViewById(R.id.tv);        bv1 = new BadgeView(this);        bv1.setBadgeCount(1);        bv1.setTargetView(tv); }
我们看到只要三条语句就可以使用BadgeView,现在再让我们返回源码去看看这三个函数。首先是它的构造函数。

public BadgeView(Context context) {this(context, null);}public BadgeView(Context context, AttributeSet attrs) {this(context, attrs, android.R.attr.textViewStyle);}public BadgeView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);init();}
里面只有一个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 fontsetTextColor(Color.WHITE);setTypeface(Typeface.DEFAULT_BOLD);setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);setPadding(dip2Px(5), dip2Px(1), dip2Px(5), dip2Px(1));// set default backgroundsetBackground(9, Color.parseColor("#d3321b"));setGravity(Gravity.CENTER);// default valuessetHideOnNull(true);setBadgeCount(0);}
看完之后,发现这个函数大多数是一些样式的设定语句,先给BadgeView设定layout_width,layout_height和布局中的位置,接下来就是其他的样式,就不一一说了。函数的末尾我们又看到了setBadgeCount函数,虽然从名字看就知道这只是用来设置显示的数字的,但我们还是去看看源码。

public void setBadgeCount(int count) {setText(String.valueOf(count));}
发现调用了setText方法

/* * (non-Javadoc) *  * @see android.widget.TextView#setText(java.lang.CharSequence, * android.widget.TextView.BufferType) */@Overridepublic void setText(CharSequence text, BufferType type) {if (isHideOnNull()&& (text == null || text.toString().equalsIgnoreCase("0"))) {setVisibility(View.GONE);} else {setVisibility(View.VISIBLE);}super.setText(text, type);}
我们看到,这个重载方法加上了条件判断来决定是否显示内容,setHideOnNull方法只是单纯用来设定是否显示BadgeView而已。那么最后就看看setTargetView函数。

/* * Attach the BadgeView to the target view *  * @param target the view to attach the BadgeView */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 badgeViewGroup 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的实现原理了。BadgeView先是将target(demo中是TextView)从原来的布局中移除,再动态创建了一个FrameLayout,并将LayoutParams设置为target原来布局的LayoutParams,然后就将创建的FrameLayout添加到target的原有布局中,最后将target和BadgeView添加到FrameLayout中。总体看来,源码不算复杂,但是从中我们可以学习到源码作者解决问题的思路,隔空膜拜一下大神。

最后让我们来找找碴。

1、为什么先添加target再添加BadgeView

ViewGroup源码中,addView是这样子的

public void addView(View child) {        addView(child, -1);    }
-1的位置下标表示将View添加的ViewGroup的最后一个位置,所以如果没有先添加target再添加BadgeView,由于这是个FrameLayout,BadgeView会被遮盖,怎么都看不到。

2、为什么要先添加badgeContainer布局再添加View

实测先添加View到badgeContainer布局,最后再添加badgeContainer布局也没影响……

3、为什么BadgeView是圆形的

 @SuppressWarnings("deprecation")    public void setBackground(int dipRadius, int badgeColor) {        int radius = dip2Px(dipRadius);        float[] radiusArray = new float[] { radius, radius, radius, radius, radius, radius, radius, radius };        //set the shape,you can change it to create other new shape        RoundRectShape roundRect = new RoundRectShape(radiusArray, null, null);        ShapeDrawable bgDrawable = new ShapeDrawable(roundRect);        bgDrawable.getPaint().setColor(badgeColor);        setBackgroundDrawable(bgDrawable);    }
看到了吧,你完全可以改为其他形状,尽管其他形状很丑~









0 0