提纲
ucrop中用到的自定义控件AspectRatioTextView解析
AspectRatioTextView
上图中每一个item是一个自定义的textview
这个是ucrop中的最简单的自定义的view了,代码量也比较少。
三个构造函数,获取attrs中定义的view的属相,所有的自定义view基本都是这个套路,之后调用init()方法
public AspectRatioTextView(Context context) {
this(context, null);
}
public AspectRatioTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public AspectRatioTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ucrop_AspectRatioTextView);
init(a);
}private void init(@NonNull TypedArray a) {
setGravity(Gravity.CENTER_HORIZONTAL);
mAspectRatioTitle = a.getString(R.styleable.ucrop_AspectRatioTextView_ucrop_artv_ratio_title);
mAspectRatioX = a.getFloat(R.styleable.ucrop_AspectRatioTextView_ucrop_artv_ratio_x, CropImageView.SOURCE_IMAGE_ASPECT_RATIO);
mAspectRatioY = a.getFloat(R.styleable.ucrop_AspectRatioTextView_ucrop_artv_ratio_y, CropImageView.SOURCE_IMAGE_ASPECT_RATIO);
if (mAspectRatioX == CropImageView.SOURCE_IMAGE_ASPECT_RATIO || mAspectRatioY == CropImageView.SOURCE_IMAGE_ASPECT_RATIO) {
mAspectRatio = CropImageView.SOURCE_IMAGE_ASPECT_RATIO;
} else {
mAspectRatio = mAspectRatioX / mAspectRatioY;
}
mDotSize = getContext().getResources().getDimensionPixelSize(R.dimen.ucrop_size_dot_scale_text_view);
mDotPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mDotPaint.setStyle(Paint.Style.FILL);
setTitle();
int activeColor = getResources().getColor(R.color.ucrop_color_widget_active);
applyActiveColor(activeColor);
a.recycle();
}在init方法中首先设置内容的gravity为水平居中,之后获取自定义的属相,包括一个title(对应上图中的“原始比例”),一个x,一个y,以及小圆点的直径和颜色。
这里我们主要关注applyActiveColor方法,一般我们设置不同状态下textView的颜色是通过在res/color文件夹下写一个xxx.xml,例如:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="@color/student_qbank_pay_config_selected" android:state_selected="true" />
<item android:color="@color/student_qbank_pay_config_normal" android:state_selected="false" />
</selector>
这里是利用ColorStateList类完成一样的功能,实质上xml解析完成之后就是ColorStateList类的一个实例。
private void applyActiveColor(@ColorInt int activeColor) {
if (mDotPaint != null) {
mDotPaint.setColor(activeColor);
}
ColorStateList textViewColorStateList = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_selected},
new int[]{0}
},
new int[]{
activeColor,
ContextCompat.getColor(getContext(), R.color.ucrop_color_widget)
}
);
setTextColor(textViewColorStateList);
}
ColorStateList的两个参数都是二维数组,状态跟颜色是一一对应的
- android.R.attr.state_selected 对应activeColor,
- 0对应ContextCompat.getColor(getContext(), R.color.ucrop_color_widget);
这样可以通过修改view的selected状态更改textview的color。
注意获取完属相之后记得回收TypedArray: a.recycle();
接着我们关注一下onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (isSelected()) {
canvas.getClipBounds(mCanvasClipBounds);
canvas.drawCircle((mCanvasClipBounds.right - mCanvasClipBounds.left) / 2.0f, mCanvasClipBounds.bottom - mDotSize,
mDotSize / 2, mDotPaint);
}
}
逻辑很简单,如果selected的状态为true时,那么绘制小圆点。canvas.getClipBounds(mCanvasClipBounds);可以获取measure完成之后该view的可绘制区域,参见上图中每一个item框出来的部分,在ucrop项目中给view的高设置的是40dp。所以小圆点的圆心(x,y)中x是居中的,y是在距离底部(size = 小圆圈直径)的位置。
这样就是实现了通过修改view的selected状态来映射view选中跟未选中的样式。