android 自定义带动画的统计饼图
来源:互联网 发布:linux如何创建文件夹 编辑:程序博客网 时间:2024/05/22 03:04
达人科技 2016-10-16 15:17
闲来无事,发现市面上好多app都有饼图统计的功能,得空自己实现以下,菜鸟一只,求指教,轻喷!
基本要求:
- 在XML布局中可配置控件的属性。
- 遵守基本的安卓规范
View基本绘制原理:
首先计算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)
layout通过调用setFrame(l,t,r,b),子视图在父视图中的具体位置,onLayout一般只会在自定义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); // 根据进度画圆弧 }}
View Code
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>
View Code
<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>
View Code
也可以在代码中通过暴露方法对各个属性的值进行设置,这里就不举例了
运行结果,如下
0 0
- android 自定义带动画的统计饼图
- Android自定义带动画的饼图PieChart
- android 自定义dialog 带动画的
- Android 带动画的自定义Toast
- android自定义带动画的柱状图控件
- Android 自定义带动画的 CheckBox
- Android 自定义带动画的dialog
- android 带旋转动画的饼图
- Android自定义Dialog带Dialog的显示消失动画(一)
- 【Android进阶】自定义带动画切换效果的ViewPager
- Android 自定义带动画效果的开关按钮
- Android实现带动画效果的自定义View
- Android Dialog使用、自定义带动画的Dialog使用
- Android 自定义带动画效果的开关按钮
- Android自定义一个带缩放动画的倒计时View
- android自定义View(带旋转动画的饼状图)
- android自定义环形统计图(带动画)
- Android自定义带动画圆环进度条
- 干货:Java几种线程池的分析和使用。
- 避免在Java接口中使用数组的3个理由
- 整数排序
- 永远要设定Deadline(最后期限),完成比完美更重要
- 程序员笑话系列(1)
- android 自定义带动画的统计饼图
- canvas-基础
- CentOS通过删除旧内核解决/boot空间不足的问题
- Android APK反编译教程
- MySQL中索引的创建与使用
- 扩展ElasticSearch:实现分片并可用于存储亿万文档的实践
- nodejs简介
- 入职必备,Android 真实面试题(内有答案)
- linux网络编程之TCP/IP基础(二):利用ARP和ICMP协议解释ping命令