BadgeView源码分析

最近做项目时用到了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 font
		setTextColor(Color.WHITE);
		setTypeface(Typeface.DEFAULT_BOLD);
		setTextSize(TypedValue.COMPLEX_UNIT_SP, 11);
		setPadding(dip2Px(5), dip2Px(1), dip2Px(5), dip2Px(1));

		// set default background
		setBackground(9, Color.parseColor("#d3321b"));

		setGravity(Gravity.CENTER);

		// default values
		setHideOnNull(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)
	 */
	@Override
	public 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 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的实现原理了。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);
    }
看到了吧,你完全可以改为其他形状,尽管其他形状很丑~









评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值