一、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);
}
绘制坐标系的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);
}
绘制坐标轴
@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轴边缘原点到原点到距离
}
绘制坐标箭头,绘制多条线
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轴箭头
如果觉得不舒服,一定要箭头向上的话,可以在绘制Y轴箭头之前翻转坐标系
canvas.scale(1,-1);//翻转Y轴
3、画布变换
绘制矩形
//绘制矩形
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8,mPaint);
平移,同时使用new Rect方法设置矩形
//平移画布
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);
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](http://upload-images.jianshu.io/upload_images/3523210-5b4b715e7f12b8fb.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
保存画布,旋转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](http://upload-images.jianshu.io/upload_images/3523210-73f6052df4186b92.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
绘制连接两点到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](http://upload-images.jianshu.io/upload_images/3523210-6382c29271765f8b.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
通过ValueAnimator类将笑脸和半圆进行连贯动画看效果
![animatiorGIF.gif](http://upload-images.jianshu.io/upload_images/3523210-d0bc4055c24a6245.gif?imageMogr2/auto-orient/strip)
####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);
}
}
````
####如有疑问欢迎交流!