美文网首页
Android 最简单的饼状图(转)

Android 最简单的饼状图(转)

作者: Amy_LuLu__ | 来源:发表于2019-03-12 15:18 被阅读0次

原文:android 最简单的饼状图

要做这么一个效果,我们应该分几步来写,
1.先做一个静态的饼状图
2.然后加上属性动画,有一个绘制的过程(这里有个难点)
3.加上选中效果

第一步:绘制一个饼状图,用canvas的drawArc来绘制圆弧,代码如下

private void drawColor(Canvas canvas, int color, float startAngle, float sweepAngle) {
        mChartPaint.setColor(color);
        mChartPaint.setAlpha(255);
        canvas.drawArc(mRectF, startAngle, sweepAngle, true, mChartPaint);
    }

这里是封装出来的方法,mRectF是绘制的矩形区域,startAngle是开始角度,sweepAngle是扫过的角度也就是你要绘制多少度,true为useCenter,这是你是否采用填充的形式,这里可以自己设为为false看看效果,mChartPaint是你的画笔,需要绘制什么颜色啊采用什么绘制样式啊等。

第二步:加上属性动画,有一个绘制的过程,代码如下

public void startAnima() {
        final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0f, 360f);
        mValueAnimator.setDuration(3 * 1000);
 
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 
            @Override public void onAnimationUpdate(ValueAnimator animation) {
                mAnimaAngle = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mValueAnimator.start();
    }
这里的mAnimaAngle是执行动画的时候当前的角度在0到360之间取值。动画是加上了,怎么加到绘制饼状图的过程中呢?怎么才能有一个绘制的动画过程呢?看下边代码

@Override protected void onDraw(Canvas canvas) {
        if (mPieModelList == null || mPieModelList.isEmpty()) {
            return;
        }
        for (int i = 0; i < mPieModelList.size(); i++) {
            if (mPieModelList.get(i).percent > 0) {
                if (mAnimaAngle >= mPieModelList.get(i).startAngle && mAnimaAngle <= (mPieModelList.get(i).startAngle + mPieModelList.get(i).sweepAngle)) {
                    drawColor(canvas, mPieModelList.get(i).color, mPieModelList.get(i).startAngle, mAnimaAngle - mPieModelList.get(i).startAngle);
                } else if (mAnimaAngle >= (mPieModelList.get(i).startAngle + mPieModelList.get(i).sweepAngle)) {
                    drawColor(canvas, mPieModelList.get(i).color, mPieModelList.get(i).startAngle, mPieModelList.get(i).sweepAngle);
                }
                if (mPieModelList.get(i).selected) {
                    drawSelectedView(canvas, mPieModelList.get(i).color, mPieModelList.get(i).startAngle, mPieModelList.get(i).sweepAngle);
                }
            }
        }
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredWidth() / 2, padding, mCirclePaint);
    }
 
    private void drawColor(Canvas canvas, int color, float startAngle, float sweepAngle) {
        mChartPaint.setColor(color);
        mChartPaint.setAlpha(255);
        canvas.drawArc(mRectF, startAngle, sweepAngle, true, mChartPaint);
    }
 
    private void drawSelectedView(Canvas canvas, int color, float startAngle, float sweepAngle) {
        mChartPaint.setColor(color);
        mChartPaint.setAlpha(150);
        canvas.drawArc(mSelectedRectF, startAngle, sweepAngle, true, mChartPaint);
    }

drawSelectedView这个方法暂时忽略,canvas.drawCircle这是绘制中心的一个白色的圆,你也可以去掉看效果,mPieModeList这个集合是装载的弧形对象的,我们把每一个弧形写成一个model这样有利于做选中和切换颜色的效果,model的代码后边贴,我们可以看到在draw方法里,有一个for循环,这个for循环绘制每一个弧形(每一个弧形都有不同的颜色和开始角度和扫过的角度),但是在for循环里有两个判断,第一个判断是:mAnimaAngle>=当前model的startAngle并且<=当前model的终点角度,也就是说只能在当前model的开始角度和终点角度之间绘制它自己特有的颜色。第二个判断是mAnimaAngle>=当前model的终点角度,如果动画执行的mAnimaAngle大于当前model的终点角度,那么该是什么颜色就绘制什么颜色,如果把这个判断去掉的话,会导致只会绘制最后一个model的弧形,前边的都会消失。

第三步:绘制选中的区域,并且让选中的弧形突出。代码如下

private void drawSelectedView(Canvas canvas, int color, float startAngle, float sweepAngle) {
        mChartPaint.setColor(color);
        mChartPaint.setAlpha(150);
        canvas.drawArc(mSelectedRectF, startAngle, sweepAngle, true, mChartPaint);
    }

这个效果我想了半天,怎么让选中的弧形突出呢?最后还是借鉴了MPAndroidChart库的实现方式,给弧形的绘制区域多加30或者50不就突出了么,而且选中不是有一个透明度的效果么,就直接设置画笔的透明度值就好。那么在哪里设置选中的区域位置呢?

@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        padding = w / 6;
        mRectF = new RectF(padding, padding, w - padding, w - padding);
        mSelectedRectF.set(mRectF);
        mSelectedRectF.inset(-30, -30);
    }

这里有个mSelectedRectF.inset(-30,-30)这里为什么要设置为负数,因为设置的参数如果为正数,则矩形会向内移动,使矩形变窄,而设置为负数,则矩形向外移动,矩形会变宽。

OK,饼状图全部分析完毕,最后是全部代码

package cms.chart.demo.view;
 
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RectF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
 
import java.util.List;
 
import cms.chart.demo.bean.PieModel;
 
/**
 * 自定义饼状图 第一:可能需要绘制多个颜色的图
 * <p>
 * Created by 54966 on 2018/2/27.
 */
 
public class PieChartView extends View {
 
    private Paint           mChartPaint;
 
    private Paint           mCirclePaint;                   // 中心圆
 
    private RectF           mRectF;
 
    private int             padding;
 
    private List<PieModel>  mPieModelList;
 
    private float           mAnimaAngle;
 
    private RectF           mSelectedRectF  = new RectF();
 
    public PieChartView(Context context) {
        this(context, null);
    }
 
    public PieChartView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
 
    public PieChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }
 
    private void init() {
        mChartPaint = new Paint();
        mChartPaint.setAntiAlias(true);
        mChartPaint.setDither(true);
        mChartPaint.setStrokeWidth(100);
        mChartPaint.setStyle(Paint.Style.FILL);
 
        mCirclePaint = new Paint();
        mCirclePaint.setAntiAlias(true);
        mCirclePaint.setStyle(Paint.Style.FILL);
        mCirclePaint.setColor(Color.WHITE);
    }
 
    @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 
    @Override protected void onDraw(Canvas canvas) {
        if (mPieModelList == null || mPieModelList.isEmpty()) {
            return;
        }
        for (int i = 0; i < mPieModelList.size(); i++) {
            if (mPieModelList.get(i).percent > 0) {
                if (mAnimaAngle >= mPieModelList.get(i).startAngle &&
                        mAnimaAngle <= (mPieModelList.get(i).startAngle + mPieModelList.get(i).sweepAngle)) {
 
                    drawColor(canvas, mPieModelList.get(i).color, mPieModelList.get(i).startAngle, mAnimaAngle - mPieModelList.get(i).startAngle);
 
                } else if (mAnimaAngle >= (mPieModelList.get(i).startAngle + mPieModelList.get(i).sweepAngle)) {
                    drawColor(canvas, mPieModelList.get(i).color, mPieModelList.get(i).startAngle, mPieModelList.get(i).sweepAngle);
                }
                if (mPieModelList.get(i).selected) {
                    drawSelectedView(canvas, mPieModelList.get(i).color, mPieModelList.get(i).startAngle, mPieModelList.get(i).sweepAngle);
                }
            }
        }
        canvas.drawCircle(getMeasuredWidth() / 2, getMeasuredWidth() / 2, padding, mCirclePaint);
    }
 
    private void drawColor(Canvas canvas, int color, float startAngle, float sweepAngle) {
        mChartPaint.setColor(color);
        mChartPaint.setAlpha(255);
        canvas.drawArc(mRectF, startAngle, sweepAngle, true, mChartPaint);
    }
 
    private void drawSelectedView(Canvas canvas, int color, float startAngle, float sweepAngle) {
        mChartPaint.setColor(color);
        mChartPaint.setAlpha(150);
        canvas.drawArc(mSelectedRectF, startAngle, sweepAngle, true, mChartPaint);
    }
 
    public void startAnima() {
        final ValueAnimator mValueAnimator = ValueAnimator.ofFloat(0f, 360f);
        mValueAnimator.setDuration(3 * 1000);
 
        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
 
            @Override public void onAnimationUpdate(ValueAnimator animation) {
                mAnimaAngle = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mValueAnimator.start();
    }
 
    public void setData(List<PieModel> pieModelList) {
        this.mPieModelList = pieModelList;
        for (int i = 0; i < mPieModelList.size(); i++) {
            PieModel model = mPieModelList.get(i);
            if (i == 0) {
                model.startAngle = 0;
            } else {
                model.startAngle = mPieModelList.get(i - 1).startAngle + mPieModelList.get(i - 1).sweepAngle;
            }
            model.sweepAngle = (model.percent * 360);
        }
    }
 
    @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        padding = w / 6;
        mRectF = new RectF(padding, padding, w - padding, w - padding);
        mSelectedRectF.set(mRectF);
        mSelectedRectF.inset(-30, -30);
    }
 
}
package cms.chart.demo;
 
import java.util.ArrayList;
import java.util.List;
 
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.TextView;
 
import cms.chart.demo.bean.PieModel;
import cms.chart.demo.utils.ColorRandom;
import cms.chart.demo.view.PieChartView;
 
public class MainActivity extends AppCompatActivity {
 
    private PieChartView    id_pie_chart;
 
    private TextView        id_tv_1, id_tv_2, id_tv_3, id_tv_4;
 
    private List<PieModel>  pieModelList    = new ArrayList<>();
 
    private List<Integer>   colorList       = new ArrayList<>();
 
    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
        id_tv_1 = findViewById(R.id.id_tv_1);
        id_tv_2 = findViewById(R.id.id_tv_2);
        id_tv_3 = findViewById(R.id.id_tv_3);
        id_tv_4 = findViewById(R.id.id_tv_4);
        id_pie_chart = findViewById(R.id.id_pie_chart);
 
        ColorRandom colorRandom = new ColorRandom(10);
        for (int i = 0; i < 5; i++) {
            int colors = (int) colorRandom.getColors().get(i);
            if (i == 0) {
                pieModelList.add(new PieModel(colors, 0.1f));
            } else {
                pieModelList.add(new PieModel(colors, 0.3f));
            }
        }
 
        id_pie_chart.setData(pieModelList);
        id_pie_chart.startAnima();
 
        List<Double> mList = new ArrayList<>();
        mList.add(0.25);
        mList.add(0.35);
        mList.add(0.15);
        mList.add(0.05);
        mList.add(0.20);
 
        id_tv_1.setOnClickListener(new View.OnClickListener() {
 
            @Override public void onClick(View v) {
                if (pieModelList.get(0).selected) {
                    pieModelList.get(0).selected = false;
                } else {
                    pieModelList.get(0).selected = true;
                }
                id_pie_chart.setData(pieModelList);
                id_pie_chart.invalidate();
            }
        });
 
        id_tv_2.setOnClickListener(new View.OnClickListener() {
 
            @Override public void onClick(View v) {
                if (pieModelList.get(1).selected) {
                    pieModelList.get(1).selected = false;
                } else {
                    pieModelList.get(1).selected = true;
                }
                id_pie_chart.setData(pieModelList);
                id_pie_chart.invalidate();
            }
        });
 
        id_tv_3.setOnClickListener(new View.OnClickListener() {
 
            @Override public void onClick(View v) {
                if (pieModelList.get(2).selected) {
                    pieModelList.get(2).selected = false;
                } else {
                    pieModelList.get(2).selected = true;
                }
                id_pie_chart.setData(pieModelList);
                id_pie_chart.invalidate();
            }
        });
 
        id_tv_4.setOnClickListener(new View.OnClickListener() {
 
            @Override public void onClick(View v) {
                if (pieModelList.get(3).selected) {
                    pieModelList.get(3).selected = false;
                } else {
                    pieModelList.get(3).selected = true;
                }
                id_pie_chart.setData(pieModelList);
                id_pie_chart.invalidate();
            }
        });
 
    }
 
}
package cms.chart.demo.bean;
 
/**
 * Created by 54966 on 2018/2/28.
 */
 
public class PieModel {
 
    public float    startAngle; // 开始绘制的角度
 
    public float    sweepAngle; // 扫过的角度
 
    public int      color;      // 显示的颜色
 
    public float    percent;    // 所占百分比
 
    public boolean  selected;   // true为选中
 
    public PieModel(int color, float percent) {
        this.color = color;
        this.percent = percent;
    }
 
    public PieModel(int color, float percent, boolean selected) {
        this.color = color;
        this.percent = percent;
        this.selected = selected;
    }
 
}
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:background="#ffffff"
    tools:context="cms.chart.demo.MainActivity">
 
    <cms.chart.demo.view.PieChartView
        android:id="@+id/id_pie_chart"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true" />
 
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/id_pie_chart"
        android:orientation="horizontal">
 
        <TextView
            android:id="@+id/id_tv_1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
 
            android:padding="10dp"
            android:text="选中1" />
 
        <TextView
            android:id="@+id/id_tv_2"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="选中2" />
 
        <TextView
            android:id="@+id/id_tv_3"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:padding="10dp"
            android:text="选中3" />
 
        <TextView
            android:id="@+id/id_tv_4"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
 
            android:padding="10dp"
            android:text="选中4" />
 
    </LinearLayout>
 
 
</RelativeLayout>
package com.qfdqc.views.demo.utils;
 
import java.util.ArrayList;
import java.util.Random;
 
import android.graphics.Color;
 
/**
 * Created by 54966 on 2018/3/8.
 */
 
public class ColorRandom {
 
    private ArrayList<Integer> colorArrays;
 
    private int                 count;
 
    public ColorRandom(int count) {
        colorArrays = new ArrayList<>(count);
        this.count = count;
        setColor();
    }
 
    private void setColor() {
        for (int i = 0; i < count; i++) {
            int color = getColor();
            colorArrays.add(color);
        }
    }
 
    private Integer getColor() {
        int color = Color.parseColor("#FFA500");
        while (colorArrays.contains(color) || "#FFFFFF".equals(color)) {
            color = getRandColorCode();
            if (!colorArrays.contains(color)) {
                break;
            }
        }
        return color;
    }
 
    public ArrayList getColors() {
        return colorArrays;
    }
 
    private Integer getRandColorCode() {
        String r, g, b;
        Random random = new Random();
        r = Integer.toHexString(random.nextInt(256)).toUpperCase();
        g = Integer.toHexString(random.nextInt(256)).toUpperCase();
        b = Integer.toHexString(random.nextInt(256)).toUpperCase();
 
        r = r.length() == 1 ? "0" + r : r;
        g = g.length() == 1 ? "0" + g : g;
        b = b.length() == 1 ? "0" + b : b;
 
        return Color.parseColor("#" + r + g + b);
    }
 
}

其实多研究别人的开源框架能学到不少封装的思想。MPAndroidChart可以去github上搜索

相关文章

网友评论

      本文标题:Android 最简单的饼状图(转)

      本文链接:https://www.haomeiwen.com/subject/rqhnpqtx.html