一患蹂、Canvas
1、創(chuàng)建畫筆
創(chuàng)建畫筆并初始化
public BasePieChart(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initPaint();
}
/**
* 初始化畫筆
*/
private void initPaint(){
mPaint=new Paint();
mPaint.setStyle(Paint.Style.FILL);//設(shè)置畫筆填充
mPaint.setAntiAlias(true);//抗鋸齒
};
2砸紊、繪制坐標(biāo)軸
使用onsizeChanged方法传于,獲取根據(jù)父布局等因素確認(rèn)的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;
}
把原點(diǎn)移動(dòng)到屏幕中心
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mWidth/2,mHeight/2);
}
繪制坐標(biāo)原點(diǎn)
/**
* 初始化畫筆
*/
private void initPaint(){
mPaint=new Paint();
mPaint.setStyle(Paint.Style.FILL);//設(shè)置畫筆填充
mPaint.setAntiAlias(true);//抗鋸齒
mPaint.setColor(Color.RED);//設(shè)置畫筆顏色
mPaint.setStrokeWidth(10);//設(shè)置畫筆寬度
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mWidth/2,mHeight/2);
canvas.drawPoint(0,0,mPaint);
}
中心原點(diǎn).png
繪制坐標(biāo)系的4個(gè)端點(diǎn),一次繪制多個(gè)點(diǎn)
@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軸邊緣原點(diǎn)到原點(diǎn)到距離
float pHeight = mHeight/ 2 * 0.8f;//Y軸邊緣原點(diǎn)到原點(diǎn)到距離
canvas.drawPoints(new float[]{pWidth,0,0,pHeight,-pWidth,0,0,-pHeight},mPaint);
}
邊緣原點(diǎn).png
繪制坐標(biāo)軸
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mWidth/2,mHeight/2);
mPaint.setStrokeWidth(10);//設(shè)置原點(diǎn)寬度
canvas.drawPoint(0,0,mPaint);//繪制原點(diǎn)
canvas.drawPoints(new float[]{pWidth,0,0,pHeight,-pWidth,0,0,-pHeight},mPaint);//繪制邊緣點(diǎn)
mPaint.setStrokeWidth(1);//重新設(shè)置畫筆到寬度
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軸邊緣原點(diǎn)到原點(diǎn)到距離
pHeight = mHeight/ 2 * 0.8f;//Y軸邊緣原點(diǎn)到原點(diǎn)到距離
}
坐標(biāo)軸.png
繪制坐標(biāo)箭頭醉顽,繪制多條線
mPaint.setStrokeWidth(3);//設(shè)置箭頭寬度
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軸箭頭之前翻轉(zhuǎn)坐標(biāo)系
canvas.scale(1,-1);//翻轉(zhuǎn)Y軸
3徽鼎、畫布變換
繪制矩形
//繪制矩形
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawRect(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8,mPaint);
矩形.png
平移盛末,同時(shí)使用new Rect方法設(shè)置矩形
//平移畫布
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
旋轉(zhuǎn)
//旋轉(zhuǎn)
canvas.rotate(90);
mPaint.setColor(Color.BLUE);
canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);
旋轉(zhuǎn).png
錯(cuò)切
//錯(cuò)切
canvas.skew(1,0.5f);
mPaint.setColor(Color.BLUE);
canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);
錯(cuò)切.png
4、畫布的保存和恢復(fù)
save():用于保存canvas的狀態(tài)否淤,之后可以調(diào)用canvas的平移悄但、旋轉(zhuǎn)、縮放石抡、錯(cuò)切檐嚣、裁剪等操作
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);
````

保存畫布,旋轉(zhuǎn)90°啰扛,繪制一個(gè)圓嚎京,之后恢復(fù)畫布,使用相同參數(shù)再繪制一個(gè)圓隐解“暗郏可以看到在恢復(fù)畫布前后,相同參數(shù)繪制的圓煞茫,分別顯示在了坐標(biāo)系的不同位置帕涌。
####二摄凡、加載動(dòng)畫
繪制2個(gè)點(diǎn)和一個(gè)半圓弧
````
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);
````

繪制連接兩點(diǎn)到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);
````

通過ValueAnimator類將笑臉和半圓進(jìn)行連貫動(dòng)畫看效果

####ValueAnimator類簡單介紹
|API|描述|
|-----|-----|
|ofFloat(float'''values)|構(gòu)建ValueAnimator,設(shè)置動(dòng)畫到浮點(diǎn)值,需要設(shè)置2個(gè)以上到值|
|setDuration(long duration)|設(shè)置動(dòng)畫時(shí)長蚓曼,默認(rèn)到持續(xù)時(shí)間為300毫秒|
|setInterpolator(Timelnterpolator value)|設(shè)置動(dòng)畫到線性非線性運(yùn)動(dòng)亲澡,默認(rèn)AccelerateDecelerateinterpolator|
|addUpdateListener(ValueAnimator.AnimatorUpdateListener listener)|監(jiān)聽動(dòng)畫屬性每一幀的變化|
####動(dòng)畫部分
````
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()方法保存當(dāng)前的畫布狀態(tài),在結(jié)束后使用restore()恢復(fù)之前保存的狀態(tài)纫版。角度計(jì)算慢慢順順就明白了
````
//設(shè)置動(dòng)畫
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);//設(shè)置畫筆填充
mPaint.setAntiAlias(true);//抗鋸齒
mPaint.setColor(Color.RED);//設(shè)置畫筆顏色
};
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.translate(mWidth/2,mHeight/2);
// canvas.scale(1,-1);
/* mPaint.setStrokeWidth(10);//設(shè)置原點(diǎn)寬度
canvas.drawPoint(0,0,mPaint);//繪制原點(diǎn)
canvas.drawPoints(new float[]{pWidth,0,0,pHeight,-pWidth,0,0,-pHeight},mPaint);//繪制邊緣點(diǎn)*/
/* mPaint.setStrokeWidth(1);//重新設(shè)置畫筆到寬度
canvas.drawLine(-pWidth,0,pWidth,0,mPaint);//繪制X軸
canvas.drawLine(0,- pHeight,0, pHeight,mPaint);//繪制Y軸*/
/* mPaint.setStrokeWidth(3);//設(shè)置箭頭寬度
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);*/
/* //旋轉(zhuǎn)
canvas.rotate(90);
mPaint.setColor(Color.BLUE);
canvas.drawRect(new RectF(-mWidth/8,-mHeight/8,mWidth/8,mHeight/8),mPaint);*/
/* //錯(cuò)切
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個(gè)點(diǎn)和一個(gè)半圓弧
/* 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軸邊緣原點(diǎn)到原點(diǎn)到距離
pHeight = mHeight/ 2 * 0.8f;//Y軸邊緣原點(diǎn)到原點(diǎn)到距離
}
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();
}
});
}
}
//設(shè)置動(dòng)畫
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);
}
}
````
####如有疑問歡迎交流床绪!