一、Canvas
1、创建画笔
创建画笔并初始化
public BasePieChart(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initPaint(); } /** * 初始化画笔 */ private void initPaint(){ mPaint=new Paint(); mPaint.setStyle(Paint.Style.FILL);//设置画笔填充 mPaint.setAntiAlias(true);//抗锯齿 };
2、绘制坐标轴
使用onsizeChanged方法,获取根据父布局等因素确认的View宽度
private int mWidth; private int mHeight; @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth=w; mHeight=h; }
把原点移动到屏幕中心
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(mWidth/2,mHeight/2); }
绘制坐标原点
/** * 初始化画笔 */ private void initPaint(){ mPaint=new Paint(); mPaint.setStyle(Paint.Style.FILL);//设置画笔填充 mPaint.setAntiAlias(true);//抗锯齿 mPaint.setColor(Color.RED);//设置画笔颜色 mPaint.setStrokeWidth(10);//设置画笔宽度 }; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(mWidth/2,mHeight/2); canvas.drawPoint(0,0,mPaint); }
中心原点.png
绘制坐标系的4个端点,一次绘制多个点
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(mWidth/2,mHeight/2); canvas.drawPoint(0,0,mPaint); float pWidth = mWidth / 2 * 0.8f;//X轴边缘原点到原点到距离 float pHeight = mHeight/ 2 * 0.8f;//Y轴边缘原点到原点到距离 canvas.drawPoints(new float[]{pWidth,0,0,pHeight,-pWidth,0,0,-pHeight},mPaint); }
边缘原点.png
绘制坐标轴
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(mWidth/2,mHeight/2); mPaint.setStrokeWidth(10);//设置原点宽度 canvas.drawPoint(0,0,mPaint);//绘制原点 canvas.drawPoints(new float[]{pWidth,0,0,pHeight,-pWidth,0,0,-pHeight},mPaint);//绘制边缘点 mPaint.setStrokeWidth(1);//重新设置画笔到宽度 canvas.drawLine(-pWidth,0,pWidth,0,mPaint);//绘制X轴 canvas.drawLine(0,- pHeight,0, pHeight,mPaint);//绘制Y轴 } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth=w; mHeight=h; pWidth = mWidth / 2 * 0.8f;//X轴边缘原点到原点到距离 pHeight = mHeight/ 2 * 0.8f;//Y轴边缘原点到原点到距离 }
坐标轴.png
绘制坐标箭头,绘制多条线
mPaint.setStrokeWidth(3);//设置箭头宽度 canvas.drawLines(new float[]{pWidth,0,pWidth*0.95f,-pWidth*0.05f,pWidth,0,pWidth*0.95f,pWidth*0.05f},mPaint);//X轴箭头 canvas.drawLines(new float[]{0,pHeight,-pHeight*0.05f,pHeight*0.95f,0,pHeight,pHeight*0.05f,pHeight*0.95f},mPaint);//Y轴箭头
箭头.png
如果觉得不舒服,一定要箭头向上的话,可以在绘制Y轴箭头之前翻转坐标系
canvas.scale(1,-1);//翻转Y轴
3、画布变换
绘制矩形
//绘制矩形 mPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8,mPaint);
矩形.png
平移,同时使用new Rect方法设置矩形
//平移画布 canvas.translate(150,150); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);
平移.png
缩放
//缩放 canvas.scale(0.5f,0.5f); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint)
缩放.png
旋转
//旋转 canvas.rotate(90); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);
旋转.png
错切
//错切 canvas.skew(1,0.5f); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);
错切.png
4、画布的保存和恢复
save():用于保存canvas的状态,之后可以调用canvas的平移、旋转、缩放、错切、裁剪等操作
float r = Math.min(mWidth, mHeight) * 0.06f / 2; mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.GREEN); canvas.save(); canvas.rotate(90); canvas.drawCircle(200,0,r,mPaint); canvas.restore(); mPaint.setColor(Color.BLUE); canvas.drawCircle(200,0,r,mPaint);
save.png
保存画布,旋转90°,绘制一个圆,之后恢复画布,使用相同参数再绘制一个圆。可以看到在恢复画布前后,相同参数绘制的圆,分别显示在了坐标系的不同位置。
二、加载动画
绘制2个点和一个半圆弧
mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.GREEN); mPaint.setStrokeWidth(10); float point = Math.min(mWidth, mHeight) / 2 * 0.2f; float r = point*(float) Math.sqrt(2); RectF rectF=new RectF(-r,-r,r,r); canvas.drawArc(rectF,0,180,false,mPaint); canvas.drawPoints(new float[]{-point,-point,point,-point},mPaint);
点半圆.png
绘制连接两点到270度到圆弧
mPaint.setStrokeWidth(10); float point = Math.min(mWidth, mHeight) / 2 * 0.2f; float r = point*(float) Math.sqrt(2); RectF rectF=new RectF(-r,-r,r,r); // canvas.drawArc(rectF,0,180,false,mPaint); // canvas.drawPoints(new float[]{-point,-point,point,-point},mPaint); canvas.drawArc(rectF,-180,270,false,mPaint);
270圆弧.png
通过ValueAnimator类将笑脸和半圆进行连贯动画看效果
animatiorGIF.gif
ValueAnimator类简单介绍
API | 描述 |
---|---|
ofFloat(float'''values) | 构建ValueAnimator,设置动画到浮点值,需要设置2个以上到值 |
setDuration(long duration) | 设置动画时长,默认到持续时间为300毫秒 |
setInterpolator(Timelnterpolator value) | 设置动画到线性非线性运动,默认AccelerateDecelerateinterpolator |
addUpdateListener(ValueAnimator.AnimatorUpdateListener listener) | 监听动画属性每一帧的变化 |
动画部分
public ValueAnimator animator; private float animatedValue; private TimeInterpolator timeInterpolator = new DecelerateInterpolator(); private void initAnimator(long duration){ if(animator!=null&&animator.isRunning()){ animator.cancel(); animator.start(); }else{ animator=ValueAnimator.ofFloat(0,855).setDuration(duration); animator.setInterpolator(timeInterpolator); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { animatedValue =(float) animation.getAnimatedValue(); // Log.d("DXDD",animatedValue+""); invalidate(); } }); } }
表情部分:在绘制前最好使用sava()方法保存当前的画布状态,在结束后使用restore()恢复之前保存的状态。角度计算慢慢顺顺就明白了
//设置动画 private void setAnimator(Canvas canvas,Paint mPaint){ mPaint.setStyle(Paint.Style.STROKE);//描边 mPaint.setStrokeCap(Paint.Cap.ROUND);//圆角笔触 mPaint.setColor(Color.parseColor("#F84B25")); mPaint.setStrokeWidth(15); float point = Math.min(mWidth,mHeight)*0.06f/2; float r = point*(float) Math.sqrt(2); RectF rectF = new RectF(-r,-r,r,r); canvas.save(); if(animatedValue>=135){ canvas.rotate(animatedValue-135); } float startAngle=0, sweepAngle=0; if (animatedValue<135){ startAngle = animatedValue +5; sweepAngle = 170+animatedValue/3; }else if (animatedValue<270){ startAngle = 135+5; sweepAngle = 170+animatedValue/3; }else if (animatedValue<630){ startAngle = 135+5; sweepAngle = 260-(animatedValue-270)/5; }else if (animatedValue<720){ startAngle = 135-(animatedValue-630)/2+5; sweepAngle = 260-(animatedValue-270)/5; }else{ startAngle = 135-(animatedValue-630)/2-(animatedValue-720)/6+5; sweepAngle = 170; } Log.d("DXDD","startAngle:"+startAngle+"sweepAngle:"+sweepAngle); canvas.drawArc(rectF,startAngle,sweepAngle,false,mPaint); canvas.drawPoints(new float[]{ -point,-point ,point,-point },mPaint); canvas.restore(); }
以下贴出源码
xml
··· <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.hedan.hedan_pie_char.MainActivity"> <com.hedan.hedan_pie_char.BasePieChart android:id="@+id/basePieChart" android:layout_width="match_parent" android:layout_height="match_parent" /> <Button android:id="@+id/button" android:layout_width="100dp" android:layout_height="50dp" android:text="开始" android:layout_alignParentBottom="true" android:layout_centerHorizontal="true" /> </RelativeLayout> ····
MainActivity
public class MainActivity extends AppCompatActivity { private BasePieChart pieChart; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); pieChart = (BasePieChart) findViewById(R.id.basePieChart); findViewById(R.id.button).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { pieChart.animator.start(); } }); } }
BasePieChart.java
/** * Created by DengXiao on 2017/2/13. */ public class BasePieChart extends View { private Paint mPaint; private float pWidth; private float pHeight; private int mWidth; private int mHeight; public BasePieChart(Context context) { this(context,null); } public BasePieChart(Context context, AttributeSet attrs) { this(context,attrs,0); } public BasePieChart(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initPaint(); } /** * 初始化画笔 */ private void initPaint(){ mPaint=new Paint(); mPaint.setStyle(Paint.Style.FILL);//设置画笔填充 mPaint.setAntiAlias(true);//抗锯齿 mPaint.setColor(Color.RED);//设置画笔颜色 }; @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.translate(mWidth/2,mHeight/2); // canvas.scale(1,-1); /* mPaint.setStrokeWidth(10);//设置原点宽度 canvas.drawPoint(0,0,mPaint);//绘制原点 canvas.drawPoints(new float[]{pWidth,0,0,pHeight,-pWidth,0,0,-pHeight},mPaint);//绘制边缘点*/ /* mPaint.setStrokeWidth(1);//重新设置画笔到宽度 canvas.drawLine(-pWidth,0,pWidth,0,mPaint);//绘制X轴 canvas.drawLine(0,- pHeight,0, pHeight,mPaint);//绘制Y轴*/ /* mPaint.setStrokeWidth(3);//设置箭头宽度 canvas.drawLines(new float[]{pWidth,0,pWidth*0.95f,-pWidth*0.05f,pWidth,0,pWidth*0.95f,pWidth*0.05f},mPaint);//X轴箭头 canvas.drawLines(new float[]{0,pHeight,-pHeight*0.05f,pHeight*0.95f,0,pHeight,pHeight*0.05f,pHeight*0.95f},mPaint);//Y轴箭头 */ /*//绘制矩形 mPaint.setStyle(Paint.Style.STROKE); canvas.drawRect(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8,mPaint);*/ /*//平移画布 canvas.translate(150,150); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);*/ /* //缩放 canvas.scale(0.5f,0.5f); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);*/ /* //旋转 canvas.rotate(90); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);*/ /* //错切 canvas.skew(1,0.5f); mPaint.setColor(Color.BLUE); canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);*/ /* float r = Math.min(mWidth, mHeight) * 0.06f / 2; mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.GREEN); canvas.save(); canvas.rotate(90); canvas.drawCircle(200,0,r,mPaint); canvas.restore(); mPaint.setColor(Color.BLUE); canvas.drawCircle(200,0,r,mPaint);*/ //绘制2个点和一个半圆弧 /* mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(Color.GREEN); mPaint.setStrokeWidth(10); float point = Math.min(mWidth, mHeight) / 2 * 0.2f; float r = point*(float) Math.sqrt(2); RectF rectF=new RectF(-r,-r,r,r);*/ // canvas.drawArc(rectF,0,180,false,mPaint); // canvas.drawPoints(new float[]{-point,-point,point,-point},mPaint); // canvas.drawArc(rectF,-180,270,false,mPaint); setAnimator(canvas,mPaint); } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); mWidth=w; mHeight=h; pWidth = mWidth / 2 * 0.8f;//X轴边缘原点到原点到距离 pHeight = mHeight/ 2 * 0.8f;//Y轴边缘原点到原点到距离 } public ValueAnimator animator; private float animatedValue; private TimeInterpolator timeInterpolator = new DecelerateInterpolator(); private void initAnimator(long duration){ if(animator!=null&&animator.isRunning()){ animator.cancel(); animator.start(); }else{ animator=ValueAnimator.ofFloat(0,855).setDuration(duration); animator.setInterpolator(timeInterpolator); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { animatedValue =(float) animation.getAnimatedValue(); // Log.d("DXDD",animatedValue+""); invalidate(); } }); } } //设置动画 private void setAnimator(Canvas canvas,Paint mPaint){ mPaint.setStyle(Paint.Style.STROKE);//描边 mPaint.setStrokeCap(Paint.Cap.ROUND);//圆角笔触 mPaint.setColor(Color.parseColor("#F84B25")); mPaint.setStrokeWidth(15); float point = Math.min(mWidth,mHeight)*0.06f/2; float r = point*(float) Math.sqrt(2); RectF rectF = new RectF(-r,-r,r,r); canvas.save(); if(animatedValue>=135){ canvas.rotate(animatedValue-135); } float startAngle=0, sweepAngle=0; if (animatedValue<135){ startAngle = animatedValue +5; sweepAngle = 170+animatedValue/3; }else if (animatedValue<270){ startAngle = 135+5; sweepAngle = 170+animatedValue/3; }else if (animatedValue<630){ startAngle = 135+5; sweepAngle = 260-(animatedValue-270)/5; }else if (animatedValue<720){ startAngle = 135-(animatedValue-630)/2+5; sweepAngle = 260-(animatedValue-270)/5; }else{ startAngle = 135-(animatedValue-630)/2-(animatedValue-720)/6+5; sweepAngle = 170; } Log.d("DXDD","startAngle:"+startAngle+"sweepAngle:"+sweepAngle); canvas.drawArc(rectF,startAngle,sweepAngle,false,mPaint); canvas.drawPoints(new float[]{ -point,-point ,point,-point },mPaint); canvas.restore(); } @Override public void onWindowFocusChanged(boolean hasWindowFocus) { super.onWindowFocusChanged(hasWindowFocus); initAnimator(3000); } }