流程:
- 创建MyView继承View/ViewGroup,需要三个构造方法
- 重写onDraw()方法
- 在布局xml中引用
如果需要添加自定义view的自定义属性:
- 在values包下的attrs文件中添加属性id
- 在 MyView的构造方法中解析这个属性值, 在onDraw()中设置这个值
- 在布局xml中引用
自定义View需要注意的坑
- 支持特殊属性,warp_content,padding & margin
如果不在onMeasur()中设置warp_content属性,那么设置wrap_content则会失效,至于为什么以后再补吧 - 多线程直接使用post(),view本生提供了post,不需要自己创建handler
- 注意内存泄漏,动画需要及时停止
- 处理好事件冲突
demo:
创建MyView,继承View/ViewGroup
public class CircularCoverView extends View
在values下的attrs文件创建CircularCoverView的自定义属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--RoundImageView-->
<declare-styleable name="roundedimageview">
<attr name="border_thickness" format="dimension" />
<attr name="border_inside_color" format="color" />
<attr name="border_outside_color" format="color"/>
<attr name="border_radius" format="dimension"/>
</declare-styleable>
</resources>
重写三个构造方法,在构造方法中解析自定义属性
public CircularCoverView(Context context) {
this(context, null, 0);
}
public CircularCoverView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularCoverView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularCoverView);
leftTopRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_left_top_radius, leftTopRadians);
leftBottomRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_left_bottom_radius, leftBottomRadians);
rightTopRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_right_top_radius, rightTopRadians);
rightBottomRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_right_bottom_radius, rightBottomRadians);
coverColor = typedArray.getColor(R.styleable.CircularCoverView_cover_color, coverColor);
}
重写onDraw()
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
Paint paint = new Paint();
paint.setFilterBitmap(false);
paint.setStyle(Paint.Style.FILL);
//create a canvas layer to show the mix-result
@SuppressLint("WrongConstant") int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
//draw sector-dst-bitmap at first.
canvas.drawBitmap(drawSector(getWidth(), getHeight()), 0, 0, paint);
//set Xfermode of paint.
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
//then draw rect-src-bitmap
canvas.drawBitmap(drawRect(getWidth(), getHeight()), 0, 0, paint);
paint.setXfermode(null);
//restore the canvas
canvas.restoreToCount(sc);
}catch (Error e){
}
}
在xml引用并且设置自定义属性值
<com.jszc.mobile.videostream.ui.CircularCoverView
android:id="@+id/head_cover"
android:layout_width="56dp"
android:layout_height="56dp"
android:layout_centerInParent="true"
app:left_top_radius="28dp"/>
完整的代码
public class CircularCoverView extends View {
private int leftTopRadians = 30; //leftTopRadians
private int leftBottomRadians = 30; //leftBottomRadians
private int rightTopRadians = 30; //rightTopRadians
private int rightBottomRadians = 30; //rightBottomRadians
private int border = 0;
private int coverColor = 0xffeaeaea; //color of cover.
public CircularCoverView(Context context) {
this(context, null, 0);
}
public CircularCoverView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public CircularCoverView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircularCoverView);
leftTopRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_left_top_radius, leftTopRadians);
leftBottomRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_left_bottom_radius, leftBottomRadians);
rightTopRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_right_top_radius, rightTopRadians);
rightBottomRadians = typedArray.getDimensionPixelSize(R.styleable.CircularCoverView_right_bottom_radius, rightBottomRadians);
coverColor = typedArray.getColor(R.styleable.CircularCoverView_cover_color, coverColor);
}
/**
* set radians of cover.
*/
public void setRadians(int leftTopRadians, int rightTopRadians, int leftBottomRadians, int rightBottomRadians,int border) {
this.leftTopRadians = leftTopRadians;
this.rightTopRadians = rightTopRadians;
this.leftBottomRadians = leftBottomRadians;
this.rightBottomRadians = rightBottomRadians;
this.border = border;
}
/**
* set color of cover.
*
* @param coverColor cover's color
*/
public void setCoverColor(@ColorInt int coverColor) {
this.coverColor = coverColor;
}
/**
* create a sector-bitmap as the dst.
*
* @param w width of bitmap
* @param h height of bitmap
* @return bitmap
*/
private Bitmap drawSector(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(0xFFFFCC44);//notice:cannot set transparent color here.otherwise cannot clip at final.
c.drawArc(new RectF(border, border, leftTopRadians * 2+border, leftTopRadians * 2+border), 180, 90, true, p);
c.drawArc(new RectF(border, getHeight() - leftBottomRadians * 2-border, leftBottomRadians * 2+border, getHeight()-border), 90, 90, true, p);
c.drawArc(new RectF(getWidth() - rightTopRadians * 2-border, border, getWidth()-border, rightTopRadians * 2+border), 270, 90, true, p);
c.drawArc(new RectF(getWidth() - rightBottomRadians * 2-border, getHeight() - rightBottomRadians * 2-border, getWidth()-border, getHeight()-border), 0, 90, true, p);
return bm;
}
/**
* create a rect-bitmap as the src.
*
* @param w width of bitmap
* @param h height of bitmap
* @return bitmap
*/
private Bitmap drawRect(int w, int h) {
Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
Canvas c = new Canvas(bm);
Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);
p.setColor(coverColor);
c.drawRect(new RectF(border, border, leftTopRadians+border, leftTopRadians+border), p);
c.drawRect(new RectF(border, getHeight() - leftBottomRadians-border, leftBottomRadians+border, getHeight()-border), p);
c.drawRect(new RectF(getWidth() - rightTopRadians-border, border, getWidth()-border, rightTopRadians+border), p);
c.drawRect(new RectF(getWidth() - rightBottomRadians-border, getHeight() - rightBottomRadians-border, getWidth()-border, getHeight()-border), p);
c.drawRect(new RectF(0, 0, getWidth(), border), p);
c.drawRect(new RectF(0, 0, border, getHeight()), p);
c.drawRect(new RectF(getWidth()-border, 0, getWidth(), getHeight()), p);
c.drawRect(new RectF(0, getHeight()-border, getWidth(), getHeight()), p);
return bm;
}
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
try {
Paint paint = new Paint();
paint.setFilterBitmap(false);
paint.setStyle(Paint.Style.FILL);
//create a canvas layer to show the mix-result
@SuppressLint("WrongConstant") int sc = canvas.saveLayer(0, 0, getWidth(), getHeight(), null);
//draw sector-dst-bitmap at first.
canvas.drawBitmap(drawSector(getWidth(), getHeight()), 0, 0, paint);
//set Xfermode of paint.
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_OUT));
//then draw rect-src-bitmap
canvas.drawBitmap(drawRect(getWidth(), getHeight()), 0, 0, paint);
paint.setXfermode(null);
//restore the canvas
canvas.restoreToCount(sc);
}catch (Error e){
}
}
}