ucrop源码解析二

389 阅读2分钟

提纲

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选中跟未选中的样式。