哎 我真是太伤心了,写了好久的博客,但是因为没有保存就全没有啦 重新写!!! 嚯……
圆形的图片在APP中是非常常见的,它常用来做头像,一般的ImageView是没有什么属性显示成圆形的,所以这是一个自定义控件
这篇博客是一个底层实现原理,如果嫌麻烦的话可以找一些第三方控件
既然圆形的View是一个自定义控件 ,那么自定义View的具体步骤有哪些呢~~~
请自行百度!!
(其实我还蛮想写几篇自定义View的博客的,,,如果我知道怎么写的话哈哈哈哈哈!!)
自定义这个圆形显示的View分为一下几部
1.创建自定义View继承自View,实现两个构造方法
2.为自定义View添加背景属性
3.将自定义View上的图片显示成圆形
自定义View继承View
实现两个构造方法,构造方法的作用如下
public class MyCircleImageView extends ImageView {
//方便代码的初始化
public MyCircleImageView(Context context) {
super(context);
}
//方便在XML文件中使用
public MyCircleImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
}
为自定义View添加src属性
这里继承的是View,如果是继承自View的话,继承过来的属性只有 宽 高 背景(background)
这个background属性是填充View的背景(方形的),而我们自定义的背景属性是圆形显示的背景,不一样
这个自定义View是不带src属性的,这里呢我们为它添加一个src属性(当然既然这个控件和属性都是自定义的,名字也可以随便取了,你可以叫circleSrc)
1.在value目录之下创建下面xml文件叫 attrs.xml,里面装载着自定义控件的属性
<resources>
<!--属性的集合,所有自定义控件的属性都会写在里面-->
<!--name一般会和自定义控件名字一样-->
<declare-styleable name="MyCircleImageView">
<attr name="src" format="reference"/><!--引用类型-->
</declare-styleable>
</resources>
在attr中填写属性包括属性名称和属性取值类型
在activity的xml文件中就可以调用这个自定义控件和它的属性了
<com.xbl.view.MyCircleImageView
android:layout_width="200dp"
android:layout_height="200dp"
app:src="@drawable/co2"
/>
2.得到src属性
上面只是为View添加了这个属性,运行的话src背景是不会显示的,因为系统是不知道src是干什么的
下面就是如何得到这个属性值,并操作它
//方便在XML文件中使用
public MyCircleImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//得到所有属性的集合
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCircleImageView);
//得到src属性所附的值
srcImage = ta.getResourceId(R.styleable.MyCircleImageView_src, R.mipmap.ic_launcher);
//因为ta中有很多数据,所以回收数据
ta.recycle();
}
这样我们就可以得到 XML文件中填写的src中的图片资源了
而下面就是重头戏了,如何将这个背景以圆形的形式显示到自定义View上
将图片显示成圆形
显示成圆形是因为显示的图片是一个圆形的,而不是自定义View被切割成圆形
自定义的形状不会改变就是方形的
实现原理:
得到src背景这个Bitmap的宽高
根据这个宽高为View设置圆形视图层(Bitmap) desc层
并在desc层上添加画布
用画笔取圆形视图层和图片层重合的地方,进行绘制图片
返回desc层
用View的onDreaw方法绘制desc层
看似很麻烦,很难理解,用代码一步步实现吧~
1.得到图片的宽高(srcImage就是图片资源R.drawable.xxx)
Bitmap srcBitmap=BitmapFactory.decodeResource(getResources(), srcImage);
int w=srcBitmap.getWidth();
int h=srcBitmap.getHeight();
2.根据图片的宽高在View上设置desc视图层
//desc层,设置最终要的形状
//根据原图的宽高创建一个bitmap图片 RGB.565最小的内存保存,ARGB_4444,8888更加清楚
Bitmap descBitmap=Bitmap.createBitmap(w,h,Bitmap.Config.ARGB_4444);
3.在desc视图层上画圆形的画布
//绘制画布形状
RectF rectF = new RectF(0, 0, w, h);
//创建内切椭圆
//得到最底层desc的画布
Canvas descCanvas = new Canvas(descBitmap);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
//抗锯齿和防抖动
paint.setAntiAlias(true);
paint.setDither(true);
descCanvas.drawOval(rectF, paint);
3.用画笔得到画布和图片视图所相交的部分,将相交的部分绘制在画布上
//设置显示取相交部分
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//将内容绘制在画布上
descCanvas.drawBitmap(srcBitmap, 0, 0, paint);
4.最后将这个已经绘制好的desc层返回
//获取圆形的图片
private Bitmap getCircleImage() {
//绘制的图片内容
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), srcImage);
int w = srcBitmap.getWidth();
int h = srcBitmap.getHeight();
//desc层,设置最终要的形状
//根据原图的宽高创建一个bitmap图片
// RGB.565最小的内存保存,ARGB_4444,8888更加清楚
Bitmap descBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
//绘制画布形状
RectF rectF = new RectF(0, 0, w, h);
//创建内切椭圆
//得到最底层desc的画布
Canvas descCanvas = new Canvas(descBitmap);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
//抗锯齿和防抖动
paint.setAntiAlias(true);
paint.setDither(true);
descCanvas.drawOval(rectF, paint);
//设置显示取相交部分
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//将内容绘制在形状上
descCanvas.drawBitmap(srcBitmap, 0, 0, paint);
return descBitmap;
}
5.在onDreaw方法中绘制视图层
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(getCircleImage(), 0, 0, null);
}
这样就可以显示出圆形View的效果了
第一个是一个正常的ImageView显示,第二个是自定义View
但是细心的人可能会发现如果我为这个View设置的宽高都是wrap_content,这个图片是显示不出来的,这是因为系统不知道你的自适应的大小为多少,所以要重写测量方法,告诉系统你这个wrap_content的值是多少
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取父亲的测量结果(widthMeasureSpec 是一个32位的int值,
// 其中高2位为测量的模式,低30位为测量的大小,模式常用的为两种
// EXACTLY 精确模式 宽高为一个精确值 xxdp或者match_parent
//AT_MOST 最大值 宽高为wrap_content
// )
int wMode = MeasureSpec.getMode(widthMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//实际图片宽高
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), srcImage);
int w = bitmap.getWidth();
int h = bitmap.getHeight();
//如果是自适应,则手动为控件设置宽高
if (wMode == MeasureSpec.AT_MOST) {
width = w;
}
if (hMode == MeasureSpec.AT_MOST) {
height = h;
}
//为控件设置宽高属性,宽高为图片的宽高
setMeasuredDimension(width, height);
}
最后附上所有代码
public class MyCircleImageView extends View {
private int srcImage;
//方便代码的初始化
public MyCircleImageView(Context context) {
super(context);
}
//方便在XML文件中使用
public MyCircleImageView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
//得到所有属性的集合
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCircleImageView);
//得到src属性所附的值
srcImage = ta.getResourceId(R.styleable.MyCircleImageView_src, R.mipmap.ic_launcher);
//因为ta中有很多数据,所以回收数据
ta.recycle();
}
//获取圆形的图片
private Bitmap getCircleImage() {
//绘制的图片内容
Bitmap srcBitmap = BitmapFactory.decodeResource(getResources(), srcImage);
int w = srcBitmap.getWidth();
int h = srcBitmap.getHeight();
//desc层,设置最终要的形状
//根据原图的宽高创建一个bitmap图片
// RGB.565最小的内存保存,ARGB_4444,8888更加清楚
Bitmap descBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_4444);
//绘制画布形状
RectF rectF = new RectF(0, 0, w, h);
//创建内切椭圆
//得到最底层desc的画布
Canvas descCanvas = new Canvas(descBitmap);
Paint paint = new Paint();
paint.setColor(Color.WHITE);
//抗锯齿和防抖动
paint.setAntiAlias(true);
paint.setDither(true);
descCanvas.drawOval(rectF, paint);
//设置显示取相交部分
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
//将内容绘制在形状上
descCanvas.drawBitmap(srcBitmap, 0, 0, paint);
return descBitmap;
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(getCircleImage(), 0, 0, null);
}
//解决wramp_content不显示问题
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//获取父亲的测量结果(widthMeasureSpec 是一个32位的int值,
// 其中高2位为测量的模式,低30位为测量的大小,模式常用的为两种
// EXACTLY 精确模式 宽高为一个精确值 xxdp或者match_parent
//AT_MOST 最大值 宽高为wrap_content
// )
int wMode = MeasureSpec.getMode(widthMeasureSpec);
int hMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
//实际图片宽高
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), srcImage);
int w = bitmap.getWidth();
int h = bitmap.getHeight();
//如果是自适应,则手动为控件设置宽高
if (wMode == MeasureSpec.AT_MOST) {
width = w;
}
if (hMode == MeasureSpec.AT_MOST) {
height = h;
}
//为控件设置宽高属性,宽高为图片的宽高
setMeasuredDimension(width, height);
}
}
源码下载地址 ,源码里面还有我自定义的其他一些控件,这个圆形的控件是
MyCircleImageView
我知道没有人下载,但是还是要po上 酱酱~~