自定义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();
}
}