学习资源:http://blog.youkuaiyun.com/lmj623565791/article/details/24555655
好了 ,以前对圆角图片一致觉得不解。到底是怎么做的啊,好高端啊。废话不多说了。先上代码
1 自定义属性集:
<attr name="borderRadius" format="dimension"></attr>
<attr name="type">
<enum name="circle" value="0"></enum>
<enum name="round" value="1"></enum>
</attr>
<attr name="src" format="reference"></attr>
<declare-styleable name="CustomImageView">
<attr name="borderRadius"></attr>
<attr name="type"></attr>
<attr name="src"></attr>
</declare-styleable>
以前对自定义属性集很是不解,为什么这个属性集都没有值,只有属性有毛用?今天才恍然大悟。先往后看吧。
2.自定义控件
private int type;
private static final int TYPE_CIRCLE = 0;
private static final int TYPE_ROUND = 1;
/**
* 图片
*/
private Bitmap mSrc;
/**
* 圆角大小
*/
private int mRadius;
/**
* 控件高度
*/
private int mWidth;
/**
* 控件高度
*/
private int mHeight;
public CustomImageView(Context context) {
this(context,null);
}
public CustomImageView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
/**
* 初始化自定义参数
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyleAttr, 0);
int n = array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.CustomImageView_src:
mSrc = BitmapFactory.decodeResource(getResources(),array.getResourceId(attr,0));
break;
case R.styleable.CustomImageView_borderRadius:
mRadius = array.getDimensionPixelSize(attr, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10f,getResources().getDisplayMetrics()));
break;
case R.styleable.CustomImageView_type:
type = array.getInt(attr,0);
break;
}
}
array.recycle();
}
/**
* 计算控件的宽度和高度
* @param widthMeasureSpec
* @param heightMeasureSpec
*/
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 设置宽度
*/
int specMode = MeasureSpec.getMode(widthMeasureSpec);
int specSize = MeasureSpec.getSize(widthMeasureSpec);
if(specMode== MeasureSpec.EXACTLY){
mWidth = specSize;
}else{
int desireByImg = getPaddingLeft()+mSrc.getWidth()+getPaddingRight();
if(specMode==MeasureSpec.AT_MOST){
mWidth = Math.min(desireByImg,specSize);
}else{
mWidth = desireByImg;
}
}
/**
* 设置高度
*/
specMode = MeasureSpec.getMode(heightMeasureSpec);
specSize = MeasureSpec.getSize(heightMeasureSpec);
if(specMode==MeasureSpec.EXACTLY){
mHeight = specSize;
}else{
int desireByImg = getPaddingTop()+mSrc.getHeight()+getPaddingBottom();
if(specMode==MeasureSpec.AT_MOST){
mHeight = Math.min(desireByImg,specSize);
}else{
mHeight = desireByImg;
}
}
}
/**
* 绘制
* @param canvas
*/
@Override
protected void onDraw(Canvas canvas) {
switch (type){
case TYPE_CIRCLE:
int min = Math.min(mWidth,mHeight);
/**
* 如果长度不一致 按小的进行压缩
*/
mSrc = Bitmap.createScaledBitmap(mSrc,mWidth,mHeight,false);
canvas.drawBitmap(createCircleImage(mSrc,min),0,0,null);
break;
case TYPE_ROUND:
canvas.drawBitmap(createRoundCornerBitmap(mSrc),0,0,null);
break;
}
}
/**
* 根据原图和变长绘制圆形
*/
private Bitmap createCircleImage(Bitmap source,int min){
Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap target = Bitmap.createBitmap(min,min, Bitmap.Config.ARGB_8888);
/**
* 产生一个同样大小的画布
*/
Canvas canvas = new Canvas(target);
/**
* 首先绘制圆形
*/
canvas.drawCircle(min/2,min/2,min/2,paint);
/**
* 使用SRC_IN
*/
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
/**
* 绘制图片
*/
canvas.drawBitmap(source,0,0,paint);
return target;
}
private Bitmap createRoundCornerBitmap(Bitmap source){
final Paint paint = new Paint();
paint.setAntiAlias(true);
Bitmap target = Bitmap.createBitmap(mWidth,mHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(target);
RectF rect = new RectF(0,0,source.getWidth(),source.getHeight());
canvas.drawRoundRect(rect,mRadius,mRadius,paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(source,0,0,paint);
return target;
}
上面先整体把代码贴出来,接下来在解释。
!构造方法最终会调用第三个构造方法,这个以前还是没有用到过的。
public CustomImageView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyleAttr, 0);
int n = array.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.CustomImageView_src:
mSrc = BitmapFactory.decodeResource(getResources(),array.getResourceId(attr,0));
break;
case R.styleable.CustomImageView_borderRadius:
mRadius = array.getDimensionPixelSize(attr, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10f,getResources().getDisplayMetrics()));
break;
case R.styleable.CustomImageView_type:
type = array.getInt(attr,0);
break;
}
}
array.recycle();
}
在此构造方法中首先是获取所有的属性集合
TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomImageView, defStyleAttr, 0);
然后遍历属性集合中的属性
for (int i = 0; i < n; i++) {
int attr = array.getIndex(i);
switch (attr) {
case R.styleable.CustomImageView_src:
mSrc = BitmapFactory.decodeResource(getResources(),array.getResourceId(attr,0));
break;
case R.styleable.CustomImageView_borderRadius:
mRadius = array.getDimensionPixelSize(attr, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,10f,getResources().getDisplayMetrics()));
break;
case R.styleable.CustomImageView_type:
type = array.getInt(attr,0);
break;
}
}
注意这里只是把属性值取出来,真正的赋值操作在这里
<com.bbdtek.circle_imageview.CustomImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
customview:src="@mipmap/icon"
customview:type="circle"
customview:borderRadius="50dp"/>
额,这个我感觉还是很好用的,以前真没有这么用过。别忘了引入自定义的命名空间。
xmlns:customview="http://schemas.android.com/apk/res-auto"
这里的res-auto听说是在studio中才是这么用,如果是eclipse就要写成全类名。
好吧,我这里只是重点说一下这个属性集合,以前没有用过。关于圆形的onMeasure 和onDraw方法就不多说了。主要还是一句代码
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
这句代码的意思就是要去bitmap和圆形的交集的部分 关于Mode的更多属性,参考下图
有一点比较迷惑的就是我自定义的控件里写的明明是wrap_content ,但是这个空间为什么会充满屏幕。好奇怪。我这里背景色写成红色是为了容易看出控件的大小。
这个是运行效果: