通过分析CircleImageView掌握自定义View流程
来源:互联网 发布:网络文学评论停刊 编辑:程序博客网 时间:2024/06/09 14:39
自定义View的几种方式:
自定义View的基本框架:
言归正传,我们开始分析CircleImageView源码:
CircleImageView源码地址:CircleImageView源码
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事件分发
关于这些知识我会在后续进行不断地补充。
阅读全文
0 0
- 通过分析CircleImageView掌握自定义View流程
- 自定义View之CircleImageView
- 自定义View 篇一--------《自定义View流程分析》
- 自定义view流程(结合源码分析)
- 自定义View:自定义CircleImageView实现及图形渲染
- CircleImageView分析
- Android 中CircleImageView自定义View(圆形头像)
- 自定义圆形头像CircleImageView的使用和源码分析
- 自定义圆形头像CircleImageView的使用和源码分析
- 自定义圆形头像CircleImageView的使用和源码分析
- AndroidView绘制流程分析及自定义View、ViewGroup进阶
- Android 自定义View流程
- 自定义流程节点View
- Android自定义View流程
- 自定义view简单流程
- Android自定义View流程
- 自定义View流程解析
- 自定义view流程
- html 如何引入一个公共的头部和底部
- 类图中的多重性(Multiplicity)
- Android常用的Gradle配置和加速编译
- short s1 = 1; s1 = s1 + 1; short s1 = 1; s1 += 1;
- Java 读取Excel表格数据日期类型转换
- 通过分析CircleImageView掌握自定义View流程
- JS动态创建form表单提交参数
- DVP interface CAM500B DEBIAN MJPG-STREAMER
- linux内核模块编写方法
- spring配置datasource数据源的三种方式
- Golang GBK转UTF-8
- meta标签及其含义大全
- 花了 4 个月整理了 50 篇 Android 干货文章
- Struts2日期转换器