android 自定义带动画的统计饼图

  闲来无事,发现市面上好多app都有饼图统计的功能,得空自己实现以下,菜鸟一只,求指教,轻喷!效果图如下:


基本要求:

  1. 在XML布局中可配置控件的属性。
  2. 遵守基本的安卓规范

View基本绘制原理:

1.计算View的大小,测量View的大小主要有三个:

public final void measure(int widthMeasureSpec, int heightMeasureSpec)  
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)  
protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) 

复制代码

measure()调用onMeasure(),onMeasure取得宽高然后调用setMeasureDimension()保存测量结果,nMeasure在view的子类中重写。

注意MeasureSpec这个帮助类:

(1) UPSPECIFIED :父容器对于子容器没有任何限制,子容器想要多大就多大.如wrap_content

(2) EXACTLY父容器已经为子容器设置了尺寸,子容器应当服从这些边界,不论子容器想要多大的空间.如match_parent或者具体的值50dp

(3) AT_MOST子容器可以是声明大小内的任意大小.

2.View的位置

public void layout(int l, int t, int r, int b)
protected boolean setFrame(int left, int top, int right, int bottom)
protected void onLayout(boolean changed, int left, int top, int right, int bottom)
复制代码

通过调用draw函数进行视图绘制,在View类中onDraw函数是个空函数,最终的绘制需求需要在自定义的onDraw函数中进行实现,比如ImageView完成图片的绘制,如果自定义ViewGroup这个函数则不需要重载。

3.绘制

public void draw(Canvas canvas)
protected void onDraw(Canvas canvas)复制代码

通过调用draw函数进行视图绘制,在View类中onDraw函数是个空函数,最终的绘制需求需要在自定义的onDraw函数中进行实现,比如ImageView完成图片的绘制,如果自定义ViewGroup这个函数则不需要重载。

具实例如下:

package com.example.customview.view;

import java.util.Timer;
import java.util.TimerTask;

import com.example.customview.R;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Point;
import android.graphics.RectF;
import android.support.v4.app.TaskStackBuilder;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class CirclePercentView extends View {
    private final static String TAG = CirclePercentView.class.getSimpleName();
    private Paint mPaint;
    private RectF oval;
    // 总数
    private int max;

    private int value;
    // 背景圆的颜色
    private int backColor;
    // 圆环的颜色
    private int frontColor;

    private float tempValue;
    // 画圆环的速度
    private int step;
    private float maxAngle;
    private Timer timer;
    // 字体大小
    private float textSize;
    // 字体颜色
    private int textColor;
    // 统计数值与统计描述的上下间距
    private float margin;
    // 园环宽度
    private float borderWidth;
    // 统计描述文本
    private String descripe;

    public CirclePercentView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public CirclePercentView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        // 自定义属性
        TypedArray ta = context.obtainStyledAttributes(attrs,
                R.styleable.CirclePercentView);
        max = ta.getInt(R.styleable.CirclePercentView_maxValue, 0);
        value = ta.getInt(R.styleable.CirclePercentView_value, 0);
        backColor = ta.getColor(R.styleable.CirclePercentView_backgroudColor,
                Color.GRAY);
        frontColor = ta
                .getColor(R.styleable.CirclePercentView_frontColor, Color.BLUE);
        textColor = ta.getColor(R.styleable.CirclePercentView_textColor, Color.BLACK);
        textSize = ta.getDimension(R.styleable.CirclePercentView_textFont, 16);
        margin = ta.getDimension(R.styleable.CirclePercentView_textMargin, 16);
        borderWidth = ta.getDimension(R.styleable.CirclePercentView_borderWidth, 8);
        descripe = ta.getString(R.styleable.CirclePercentView_descripe);
        ta.recycle();
        // 计算角度
        maxAngle = value * 360f / max;
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        oval = new RectF();
        timer = new Timer();
        startAnim(100, 5000);
    }

    /**
     * 
     * @param t1
     *            间隔时长
     * @param t2
     *            动画总时长
     */
    private void startAnim(long t1, long t2) {
        step = (int) (maxAngle / t2 * t1);
        startAnim();
    }

    private void startAnim() {
        timer.scheduleAtFixedRate(new TimerTask() {

            @Override
            public void run() {
                Log.e("tempValuetempValuetempValue", tempValue + "maxAngle"
                        + maxAngle + "maxAngle" + maxAngle);
                if (tempValue + step >= maxAngle) {
                    tempValue = maxAngle;
                    timer.cancel();
                } else {
                    tempValue += step;
                }
                // 注意此处postInvalidate()与invalidate()的区别
                postInvalidate();

            }
        }, 100, 100);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        Log.e(TAG, "onMeasure--widthMode-->" + widthMode);
        switch (widthMode) {
        case MeasureSpec.EXACTLY:
            Log.e(TAG, "EXACTLY-->EXACTLY" + widthSize);
            setMeasuredDimension(widthSize, heightSize);
            break;
        case MeasureSpec.AT_MOST:
            Log.e(TAG, "AT_MOST-->AT_MOST" + widthSize);
            break;
        case MeasureSpec.UNSPECIFIED:
            Log.e(TAG, "UNSPECIFIED-->UNSPECIFIED" + widthSize);
            break;
        }
        Log.e(TAG, "onMeasure--widthSize-->" + widthSize);
        Log.e(TAG, "onMeasure--heightMode-->" + heightMode);
        Log.e(TAG, "onMeasure--heightSize-->" + heightSize);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right,
            int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        Log.e(TAG, "onLayout");
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        mPaint.setColor(backColor);
        // FILL填充, STROKE描边,FILL_AND_STROKE填充和描边
        mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
        int with = getWidth();
        int height = getHeight();
        // 取最小值作为圆的直径
        int size = Math.min(with, height);
        Log.e(TAG, "onDraw---->" + with + "*" + height);
        // 计算圆的半径
        float radius = (size - 2 * borderWidth) / 2;
        // 画背景圆
        canvas.drawCircle(size / 2, size / 2, radius, mPaint);
        // 画文本
        mPaint.setStrokeWidth(0);
        mPaint.setColor(textColor);
        mPaint.setTextSize(textSize);
        canvas.drawText(descripe, radius - mPaint.measureText(descripe) * 0.5f,
                size / 2 + textSize + margin, mPaint);
        float textHalfWidth = mPaint
                .measureText((int) (tempValue / 360 * 100 + 0.5f) + "%") * 0.5f;
        canvas.drawText((int) (tempValue / 360 * 100 + 0.5f) + "%", radius
                - textHalfWidth, size / 2, mPaint);
        // 画圆环
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setColor(frontColor);
        mPaint.setStrokeWidth(borderWidth);
        // 放圆的矩形
        oval.set(size / 2 - radius, size / 2 - radius, size / 2 + radius, size
                / 2 + radius);
        // 注意第三个参数
        canvas.drawArc(oval, 0, tempValue, false, mPaint); // 根据进度画圆弧

    }
}
复制代码

attrs.xml如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="CirclePercentView">
        <attr name="maxValue" format="integer" />
        <attr name="value" format="integer" />
        <attr name="backgroudColor" format="color|reference" />
        <attr name="frontColor" format="color|reference" />
        <attr name="textFont" format="dimension|reference" />
        <attr name="textColor" format="color|reference" />
        <attr name="textMargin" format="dimension|reference" />
        <attr name="borderWidth" format="dimension|reference" />
        <attr name="descripe" format="string|reference" />
    </declare-styleable>

</resources>
复制代码

具体使用如下:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <com.example.customview.view.CirclePercentView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_margin="10dp"
        app:backgroudColor="#cccccc"
        app:borderWidth="12dp"
        app:frontColor="#ff00ff"
        app:maxValue="360"
        app:textColor="#ececcc"
        app:textFont="24sp"
        app:textMargin="0dp"
        app:value="270" 
        app:descripe="参与人数"/>

</LinearLayout>
复制代码

也可以在代码中通过暴露方法对各个属性的值进行设置,这里就不举例了

码字不易,期待各位的赞赏!!!

    




评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值