自定义View
程序员文章站
2022-05-30 20:26:04
...
自定义View的基本步骤是重写onMeasure方法及测量、onlayout方法及布局、ondraw方法及绘制,这三大步骤,但是当onMeasure方法被调用完后,控件大小发生变化会执行onSizeChanged,我们可以在这个方法里面做一下计算和配置
自定义View其实真正需要重写的方法是onMeasure和ondraw方法,在自定义ViewGroup的时候才需要onlayout方法,
画一个饼状图,里面有不同的占比,旁边会有个举行图显示占比
看到这张图
第一,继承view,申明构造方法
public WxbView(Context context) { this(context, null); } public WxbView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public WxbView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); }
第二,重写onMeasure和ondraw、onSizeChanged方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int wmode = MeasureSpec.getMode(widthMeasureSpec); int wsize = MeasureSpec.getSize(widthMeasureSpec); int hmode = MeasureSpec.getMode(heightMeasureSpec); int hsize = MeasureSpec.getSize(heightMeasureSpec); mWidth=getSize(wmode,400f,wsize); mHeight=getSize(hmode,200f,hsize); //存储测量好的宽和高 setMeasuredDimension(wsize,hsize); } private int getSize(int mode, float size, int msize) { int value = 0; switch (mode) { case MeasureSpec.EXACTLY: value = msize; break; case MeasureSpec.AT_MOST: value = DpUtil.dip2px(context, size); break; case MeasureSpec.UNSPECIFIED: break; default: break; } return value; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mR=Math.min(mWidth,mHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); }
第三,定义圆的直径、view的宽和高、绘制文字的坐标、矩形的宽度和高度、圆到矩形的距离等
private List<Bean> list;//饼状圆的数据 private RectF mRectF;//圆矩形 private RectF iRect;//右边的矩形 private Paint mPaint;//画笔 private int mWidth, mHeight; private float rotateDegree;//每个圆的起始角度 private float sumValue;//所有值的和 private float mR;//圆的直径 private float textY;//绘制文字的Y坐标 private float mRectFHeight = 40;//矩形的高度 private float mRectFWidth = 80;//矩形的宽度 private float mMargin = 40;//矩形到圆的距离 private Context context;
第四,准备好Paint画笔,RectF矩形,并初始化
private void init() { list = new ArrayList<>(); mRectF = new RectF(); mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setDither(true); }
第五,绘制时计算每个区域绘制的起始位及具体绘制
//设置圆形绘制的范围 mRectF.set(0,0,mR,mR); //画布中心X坐标向右移动(控件宽度-圆直径)之差的八分之一的距离 //画布中心Y坐标向下移动(控件高度-圆直径)之差的二分之一的距离 canvas.translate((mWidth-mR)/8,(mHeight-mR)/2); if (list.size()>0&&Float.compare(sumValue,0.0f)!=0){ for (int i=0;i<list.size();i++){ Bean bean=list.get(i); //画圆弧 mPaint.setColor(bean.getColor()); canvas.drawArc(mRectF,rotateDegree,bean.getDegree(),true,mPaint); rotateDegree+=bean.getDegree(); //画矩形和文字 drawRectAntText(canvas,bean); } }
矩形的起始位置及具体绘制
private void drawRectAntText(Canvas canvas,Bean bean){ iRect=new RectF(); //设置画矩形的范围 float left=mR+mMargin; float right=mR+mMargin+mRectFWidth; float bottom=textY+mRectFHeight; iRect.set(left,textY,right,bottom); canvas.drawRect(iRect,mPaint); //设置颜色 mPaint.setColor(Color.BLACK); //设置文字大小 mPaint.setTextSize(30); //画文字 canvas.drawText(bean.getName()+"("+new DecimalFormat(".00").format(bean.getValue()/sumValue*100)+"%)",right+10,textY+30,mPaint); textY+=mRectFHeight; }
第六,设置绘制数据
public void setData(List<Bean> list){ if (list==null||list.size()<=0){ return; } for (int i=0;i<list.size();i++){ Bean bean=list.get(i); sumValue+=bean.getValue(); bean.setDegree(bean.getValue()); } for (int i=0;i<list.size();i++){ Bean bean=list.get(i); bean.setDegree(bean.getValue()/sumValue*360); this.list.add(bean); } invalidate(); }
以上一个饼状图就绘制完成了
对应的bean
public class Bean {
private String name;
private int color;
private float value;
private float degree;
public int getColor() {
return color;
}
public void setColor(int color) {
this.color = color;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public float getValue() {
return value;
}
public void setValue(float value) {
this.value = value;
}
public float getDegree() {
return degree;
}
public void setDegree(float degree) {
this.degree = degree;
}
}
对应的自定义View
public class WxbView extends View {
private List<Bean> list;//饼状圆的数据
private RectF mRectF;//圆矩形
private RectF iRect;//右边的矩形
private Paint mPaint;//画笔
private int mWidth, mHeight;
private float rotateDegree;//每个圆的起始角度
private float sumValue;//所有值的和
private float mR;//圆的直径
private float textY;//绘制文字的Y坐标
private float mRectFHeight = 40;//矩形的高度
private float mRectFWidth = 80;//矩形的宽度
private float mMargin = 40;//矩形到圆的距离
private Context context;
public WxbView(Context context) {
this(context, null);
}
public WxbView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public WxbView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init();
}
private void init() {
list = new ArrayList<>();
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int wmode = MeasureSpec.getMode(widthMeasureSpec);
int wsize = MeasureSpec.getSize(widthMeasureSpec);
int hmode = MeasureSpec.getMode(heightMeasureSpec);
int hsize = MeasureSpec.getSize(heightMeasureSpec);
mWidth=getSize(wmode,400f,wsize);
mHeight=getSize(hmode,200f,hsize);
//存储测量好的宽和高
setMeasuredDimension(wsize,hsize);
}
private int getSize(int mode, float size, int msize) {
int value = 0;
switch (mode) {
case MeasureSpec.EXACTLY:
value = msize;
break;
case MeasureSpec.AT_MOST:
value = DpUtil.dip2px(context, size);
break;
case MeasureSpec.UNSPECIFIED:
break;
default:
break;
}
return value;
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mR=Math.min(mWidth,mHeight);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置圆形绘制的范围
mRectF.set(0,0,mR,mR);
//画布中心X坐标向右移动(控件宽度-圆直径)之差的八分之一的距离
//画布中心Y坐标向下移动(控件高度-圆直径)之差的二分之一的距离
canvas.translate((mWidth-mR)/8,(mHeight-mR)/2);
if (list.size()>0&&Float.compare(sumValue,0.0f)!=0){
for (int i=0;i<list.size();i++){
Bean bean=list.get(i);
//画圆弧
mPaint.setColor(bean.getColor());
canvas.drawArc(mRectF,rotateDegree,bean.getDegree(),true,mPaint);
rotateDegree+=bean.getDegree();
//画矩形和文字
drawRectAntText(canvas,bean);
}
}
}
private void drawRectAntText(Canvas canvas,Bean bean){
iRect=new RectF();
//设置画矩形的范围
float left=mR+mMargin;
float right=mR+mMargin+mRectFWidth;
float bottom=textY+mRectFHeight;
iRect.set(left,textY,right,bottom);
canvas.drawRect(iRect,mPaint);
//设置颜色
mPaint.setColor(Color.BLACK);
//设置文字大小
mPaint.setTextSize(30);
//画文字
canvas.drawText(bean.getName()+"("+new DecimalFormat(".00").format(bean.getValue()/sumValue*100)+"%)",right+10,textY+30,mPaint);
textY+=mRectFHeight;
}
public void setData(List<Bean> list){
if (list==null||list.size()<=0){
return;
}
for (int i=0;i<list.size();i++){
Bean bean=list.get(i);
sumValue+=bean.getValue();
bean.setDegree(bean.getValue());
}
for (int i=0;i<list.size();i++){
Bean bean=list.get(i);
bean.setDegree(bean.getValue()/sumValue*360);
this.list.add(bean);
}
invalidate();
}
}