Android自定义View(ImageView画圆)
通常情况下
- 首先想想生活中画圆的过程:
为了绘图所需,请准备工具:包括一个圆规、一张白纸以及其中必须带有铅笔或作图工具。有了这些工具后即可开始绘制圆形图形。
编写的代码功能显然为服务生活而存在,这必须源自于生活的具体需求。与之结合使用,则不会显得繁琐。
然后就是将图片圆角的思路:
(1)我们要画出一个圆
(2)我们用一张图片去填充这个圆就行了
(3)细节,我们需要控制圆心坐标,计算圆的半径等等问题
再然后接下来就是该清楚系统绘制过程,和系统绘制的机制
(1)用什么去画?
(2)怎么画?
(3)我们怎么控制,让他画出我们想要的东西?
(4)在每个生命周期方法中都做了些什么?
带着思路解决上面提出的问题,在深入理解理论基础后向下探索,在研究过程中要特别关注以下几点:首先深入分析模块之间的交互逻辑;其次重点掌握核心算法的具体实现;最后通过详细阅读注释理解关键组件的设计意图
- 自定义视图的过程或者系统绘图机制通常包括:
(1)使用一支绘图工具(Paint),它决定了对象的视觉效果。
例如颜色(例如颜色即用于填充绘制出的形状...),以及风格等因素涉及图形渲染的问题。
(2)有一个绘图板(Canvas)。根据我们的设计意图,在绘图板上绘制图像即通过 canvas 对象来进行绘图操作。该对象决定了所绘图形的形态特征及其坐标属性等其他参数。这些属性和参数均需要通过调用相关API来实现获取和控制。
在系统绘制视图至屏幕全过程中,在ImageView绘图阶段其相关操作流程包括以下几个关键步骤:首先,在进行图像设置时(即调用setImageDrawabke方法),会依次执行一系列图像处理操作。这些操作能够获取到当前ImageView对象中的bitmap数据或对应的资源标识符/路径信息。
(2)调用构造方法:为实现自定义属性的需求,在此之前均需在此处先关联attr.xml文件,并获取布局文件中预先设置好的属性信息,在后续绘制过程中使用。值得注意的是,在此之前系统尚未获取到view的宽度和高度信息,请避免在此阶段进行初始化画笔等操作,存在潜在风险。
size变化事件处理函数:在这一部分代码位置上,在这里我们可以获取当前视图的宽度与高度值。因此,在此处开始初始化绘图笔等相关操作也是恰到好处。
(4)onDraw方法:该方法确实实现了视图的呈现,在我们眼前展示出来。需要注意的是是否需要调用父类中的onDraw方法?因为如果不希望出现某些特定视图,则完全由本体实现绘图过程;否则可能会导致 unintended views的出现,并且会引发双层 rendering的情况发生。
3.上源代码,来点实际的
在values目录下创建,自定义属性,attr.xml
_<?_**xml version="1.0" encoding="utf-8"**_? >
_**resources** _<!-- 如果是用Android studio开发,注意styleable中的name需要跟自定义的View类名相同,否则,在布局文件中无法引用下面的属性 -->
_**declare-styleable name="CircleImage"**_<!-- 是否有外边框 -->
_**attr name="hasBorder" format="boolean"**_<!-- 有外边框的情况下,外边框的颜色 -->
_**attr name="borderColor" format="color"**_<!-- 有外边框的情况下,外边框的厚度 -->
_**attr name="borderWidth" format="dimension"****declare-styleable****resources**
编写xml布局文件文件引用
_<?_**xml version="1.0" encoding="utf-8"**_? >
_**RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:dragon="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#0000FF"**_<!-- xmlns:dragon="http://schemas.android.com/apk/res-auto" 注意看上面的这句话,是用于引用自定义的属性,不可丢 -->
_**com.picovr.animatordemo.CircleImage
android:id="@+id/iv_animator"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:src="@mipmap/timgs"
dragon:borderColor="#000000"
dragon:borderWidth="3dp"
dragon:hasBorder="true"****RelativeLayout**
创建类,继承ImageView编写代码
**package****import****import****import****import****import****import****import****import****import****import****import****import****import****import****import****import****import** _/** * Created by PICO-USER dragon on 2017/2/28.
*/_**public class****extends** _// 缩放类型_**private static final** _BITMAP_CONFIG_ _ARGB_8888_**private static final int** _COLORDRAWABLE_DIMENSION_ 2**private float circleRadius****private****bitmap****private****bitmapPaint****private****bitmapShader****private****drawableRect****private int bitmapWidth****private int bitmapHeight****private****mShaderMatrix****private****borderPaint****private int borderColor****private int borderWidth****private****borderRect****private float borderRadius****private boolean hasBorder****public****super****public****super** _i_**"CircleImage3"****"CircleImage3 构造方法 :"**_//自定义属性,关联attr.xml中 <declare-styleable name="CircleImage">中的属性
__CircleImage_ _//获取布局文件中的自定义属性_**hasBorder** _CircleImage_hasBorder_**false****if****hasBorder****borderWidth** _CircleImage_borderWidth_ 0**borderColor** _CircleImage_borderColor_ _TRANSPARENT_ _/** * 构造方法中还不能拿到View的宽高,在这个方法中可以得到,所以在这儿实例化画圆所需要的一切配置
* * @param w
* @param h
* @param oldw
* @param oldh
*/_ @Override
**protected void****int****int****int****int****super** @Override
**protected void** _//这儿一定不要调用父类的onDraw方法,调用这个方法系统会直接将ImageView绘画出来,这就会出现两张图片
// super.onDraw(canvas);__i_**"CircleImage3"****"onDraw :"** 22**circleRadius****bitmapPaint****if****hasBorder** 22**borderRadius****borderPaint** _/** * Drawable转Bitmap
* * @param drawable
* @return
*/_**private****if****null****return null****if****instanceof** _// 通常来说 我们的代码就是执行到这里就返回了。返回的就是我们最原始的bitmap_**return****try****if****instanceof** _createBitmap_ _COLORDRAWABLE_DIMENSION_ _COLORDRAWABLE_DIMENSION_ _BITMAP_CONFIG_**else** _createBitmap_ _BITMAP_CONFIG_**new** 00**return****catch****return null** _/** * 关键代码就在这儿,画笔初始化,圆半径计算,图片压缩,构建Shader,用Bitmap填充圆
*/_**private void****if****null****bitmap****if****bitmap****null****throw new****"the bitmap of imageView is null !"****if****hasBorder** _/** * 初始化外圆画笔的属性,外圆半径的计算
*/_**borderPaint****new** _//外圆所在矩形:这儿用一个矩形去控制外圆,Android中的View都是矩形,所以画圆也是在一个矩形里面画内心圆_**borderRect****new****borderPaint** _STROKE_**borderPaint****true** _//外圆不需要用图片去填充,而是用想要的颜色去填充_**borderPaint****borderColor****borderPaint****borderWidth** _//RectF类中计算,矩形的宽 = right坐标-left坐标,高 = bottom坐标-top坐标,
// 所以这儿只需要将本ImageView的宽值赋给矩形的right坐标,高赋值给bottom坐标即可_**borderRect** 00 _//外圆的半径是算法(外圆所在矩形的宽和高这两边的短边的一半减去外圆到内圆的距离)_**borderRadius** _min_**borderRect****borderWidth** 2**borderRect****borderWidth** 2 _//初始化内圆的画笔属性,半径计算_**bitmapPaint****new** _//同外圆一样,画一个矩形的内心圆_**drawableRect****new** _//赋给shader的矩阵,用于压缩图片,让图片的中心部位去填充内圆_**mShaderMatrix****new****bitmapWidth****bitmap****bitmapHeight****bitmap** _// 构建渲染器,用mBitmap位图来填充绘制区域 ,参数值代表如果图片太小的话 就直接拉伸_**bitmapShader****new****bitmap** _CLAMP_ _CLAMP_**bitmapPaint****true** _// 设置图片画笔渲染器_**bitmapPaint****bitmapShader****if****hasBorder** _//内圆所属矩形的宽和高应该分别是外圆所属矩形的宽减去外圆到内圆的距离和高减去外圆到内圆的距离_**drawableRect** 00**borderRect****borderWidth****borderRect****borderWidth****else****drawableRect** 00**circleRadius** _min_**drawableRect** 2**drawableRect** 2 _i_**"CircleImage3"****"initPaint drawable Width :"****drawableRect****" Height "****drawableRect****" View width :"****" height :"**_//压缩并位移图片用于填充内部圆_**private void****float****float** 0**float** 0 _i_**"CircleImage3"****"setBitmapShaderMtrix drawable Width :"****drawableRect****" Height "****drawableRect** _i_**"CircleImage3"****"setBitmapShaderMtrix bitmap width :"****bitmap****" height : "****bitmap****mShaderMatrix****null** _//x方向压缩还是Y方向压缩判断_**if****bitmapWidth****drawableRect****drawableRect****bitmapHeight** _// y轴缩放 x轴平移 使得图片的y轴方向的边的尺寸缩放到图片显示区域(mDrawableRect)一样)_**drawableRect****float****bitmapHeight****drawableRect****bitmapWidth** 0.5f _i_**"CircleImage3"****"setBitmapShaderMtrix scale :"****" dx :"****else** _// x轴缩放 y轴平移 使得图片的x轴方向的边的尺寸缩放到图片显示区域(mDrawableRect)一样)_**drawableRect****float****bitmapWidth****drawableRect****bitmapHeight** 0.5f _i_**"CircleImage3"****"setBitmapShaderMtrix scale :"****" dy :"**_// shaeder的变换矩阵,我们这里主要用于放大或者缩小。_**mShaderMatrix** _// 平移_**mShaderMatrix****int** 0.5f**drawableRect****left****int** 0.5f**drawableRect****top** _// 设置变换矩阵_**bitmapShader****mShaderMatrix** @Override
**public void****super**
MainActivity
@Override
**protected void****super** _activity_main_**iv_animator** _iv_animator_
效果附上:

