android 最简单的饼状图

录制的gif图有点渣,但是实际效果还是可以的。

因为项目需要绘制饼状图,就很简单的饼状图带一个选中的效果,本来也想使用MPAndroidChart这个库的,但是觉得就实现一个效果引用整个库,对于一个有态度的程序员来说,内心是拒绝的(我不是装逼的人),所以自己自定义了一个,写的不好仅供参考。

要做这么一个效果,我们应该分几步来写,

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>

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

猜你喜欢

转载自blog.csdn.net/u010648159/article/details/79629052
今日推荐