通过分析CircleImageView掌握自定义View流程

来源:互联网 发布:网络文学评论停刊 编辑:程序博客网 时间:2024/06/09 14:39
自定义View的几种方式:

1.直接继承View

2.直接继承ViewGroup

3.继承现有的View如(TextView、ImageView等);今天要说的CircleImageView就直接继承ImageView

4.继承现有的ViewGroup(如LinearLayout、RelativeLayout、FrameLayout等)

自定义View的基本框架:

1.创建自定义View骨架(如extends View)

2.添加自定义属性 (在res/attrs 文件声明),并在构造函数中获取属性信息

3.初始化画笔

4.实现onMeasure()方法->对View进行测量

5.实现onSizeChanged()方法->对View进行更新

6.实现onDraw()方法->绘制View

7.布局中使用自定义View


言归正传,我们开始分析CircleImageView源码:

step1 : 继承ImageView

public class CircleImageView extends ImageView {

step2 : 添加自定义属性 (在res/attrs 文件声明),并在构造函数中获取属性信息

a.添加自定义属性

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="CircleImageView">        <attr name="civ_border_width" format="dimension" />        <attr name="civ_border_color" format="color" />        <attr name="civ_border_overlay" format="boolean" />        <attr name="civ_fill_color" format="color" />    </declare-styleable></resources>
b.构造函数中获取属性信息

public CircleImageView(Context context, AttributeSet attrs, int defStyle) {        super(context, attrs, defStyle);        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CircleImageView, defStyle, 0);        mBorderWidth = a.getDimensionPixelSize(R.styleable.CircleImageView_civ_border_width, DEFAULT_BORDER_WIDTH);        mBorderColor = a.getColor(R.styleable.CircleImageView_civ_border_color, DEFAULT_BORDER_COLOR);        mBorderOverlay = a.getBoolean(R.styleable.CircleImageView_civ_border_overlay, DEFAULT_BORDER_OVERLAY);        mFillColor = a.getColor(R.styleable.CircleImageView_civ_fill_color, DEFAULT_FILL_COLOR);        a.recycle();        init();    }

step3 : 初始化画笔

//绘制头像private final Paint mBitmapPaint = new Paint();//绘制边框private final Paint mBorderPaint = new Paint();//绘制填充效果private final Paint mFillPaint = new Paint();

step4 : 实现onMeasure()方法

复用ImageView的测量流程。

step5 :实现onSizeChanged()方法

@Overrideprotected void onSizeChanged(int w, int h, int oldw, int oldh) {    super.onSizeChanged(w, h, oldw, oldh);    setup();}

注意:setup()对画笔信息进行设置、从新计算图片、边框等大小,然后调用invalidate方法对View进行重绘。

private void setup() {        if (!mReady) {            mSetupPending = true;            return;        }        if (getWidth() == 0 && getHeight() == 0) {            return;        }        if (mBitmap == null) {            invalidate();            return;        }        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);        mBitmapPaint.setAntiAlias(true);        mBitmapPaint.setShader(mBitmapShader);        mBorderPaint.setStyle(Paint.Style.STROKE);        mBorderPaint.setAntiAlias(true);        mBorderPaint.setColor(mBorderColor);        mBorderPaint.setStrokeWidth(mBorderWidth);        mFillPaint.setStyle(Paint.Style.FILL);        mFillPaint.setAntiAlias(true);        mFillPaint.setColor(mFillColor);        mBitmapHeight = mBitmap.getHeight();        mBitmapWidth = mBitmap.getWidth();        mBorderRect.set(calculateBounds());        mBorderRadius = Math.min((mBorderRect.height() - mBorderWidth) / 2.0f, (mBorderRect.width() - mBorderWidth) / 2.0f);        mDrawableRect.set(mBorderRect);        if (!mBorderOverlay && mBorderWidth > 0) {            mDrawableRect.inset(mBorderWidth - 1.0f, mBorderWidth - 1.0f);        }        mDrawableRadius = Math.min(mDrawableRect.height() / 2.0f, mDrawableRect.width() / 2.0f);        applyColorFilter();        updateShaderMatrix();        invalidate();    }

step6 : 实现onDraw()方法

@Overrideprotected void onDraw(Canvas canvas) {if (mDisableCircularTransformation) {    super.onDraw(canvas);    return;}if (mBitmap == null) {    return;}//如果填充颜色不是透明,绘制透明效果if (mFillColor != Color.TRANSPARENT) {    canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mFillPaint);}//绘制圆形图片canvas.drawCircle(mDrawableRect.centerX(), mDrawableRect.centerY(), mDrawableRadius, mBitmapPaint);//如果有边框,绘制边框if (mBorderWidth > 0) {    canvas.drawCircle(mBorderRect.centerX(), mBorderRect.centerY(), mBorderRadius, mBorderPaint);}}

step7 :  布局中使用自定义View

<de.hdodenhof.circleimageview.CircleImageView            android:layout_width="160dp"            android:layout_height="160dp"            android:layout_centerInParent="true"            android:src="@drawable/hugh"            app:civ_border_width="2dp"            app:civ_border_color="@color/dark" />
注:这里用到自定义属性,所以需要在布局文件中引入对应的命名空间

xmlns:app="http://schemas.android.com/apk/res-auto"

CircleImageView源码地址:CircleImageView源码


最后想说一下自定义View几种方式之间的区别:

直接继承View或ViewGroup()更接近于底层实现,但是难度也更高,需要对View的测量(onMeasure())、布局(onLayout())、绘制(onDraw())等进行重写,同时需要解决view之间的事件冲突,这就需要对view的事件分发机制有一定的了解。

* 理解view的工作原理:测量、布局、绘制

* 理解view事件分发

关于这些知识我会在后续进行不断地补充。

原创粉丝点击