Android 扇形进度框 android画扇形

您所在的位置:网站首页 怎么让扇形图显示百分比 Android 扇形进度框 android画扇形

Android 扇形进度框 android画扇形

2023-06-24 01:01| 来源: 网络整理| 查看: 265

前言:继上次写了自定义圆形进度条后,今天给大家带来自定义扇形饼状图。先上效果图:

Android 扇形进度框 android画扇形_Math

是不是很炫?看上去还有点立体感。下面带大家一起来瞧一瞧吧。

一、定义成员变量,重写构造方法

看着这个效果图,我们可以想象下接下来暂时会需要用到以下属性:

/** * 存放事物的品种与其对应的数量 */ private Map kindsMap = new LinkedHashMap(); /** * 存放颜色 */ private ArrayList colors = new ArrayList(); private Paint mPaint;//饼状画笔 private Paint mTextPaint; // 文字画笔 private static final int DEFAULT_RADIUS = 200; private int mRadius = DEFAULT_RADIUS; //外圆的半径 private String centerTitle; //中间标题

然后重写父类的构造方法,初始化画笔:

public PieChatView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); mPaint = new Paint(); mTextPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.FILL); mTextPaint.setColor(Color.BLACK); mTextPaint.setAntiAlias(true); mTextPaint.setStyle(Paint.Style.STROKE); mTextPaint.setTextAlign(Paint.Align.CENTER); } public PieChatView(Context context) { this(context, null, 0); } public PieChatView(Context context, AttributeSet attrs) { this(context, attrs, 0); }

一般都是一个参数和两个参数的全部调用第三个参数的。我三个参数的构造方法中没有去从xml文件中去获取属性了。我完全就用代码实现了。也可以将一些属性放在attrs.xml文件中,然后去获取。自行选择吧。

二、onMeasure()

这个方法,只要你以前写过自定义View,基本上就是一样的套路:

@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int wideSize = MeasureSpec.getSize(widthMeasureSpec); int wideMode = MeasureSpec.getMode(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); int width, height; if (wideMode == MeasureSpec.EXACTLY) { //精确值 或matchParent width = wideSize; } else { width = mRadius * 2 + getPaddingLeft() + getPaddingRight(); if (wideMode == MeasureSpec.AT_MOST) { width = Math.min(width, wideSize); } } if (heightMode == MeasureSpec.EXACTLY) { //精确值 或matchParent height = heightSize; } else { height = mRadius * 2 + getPaddingTop() + getPaddingBottom(); if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(height, heightSize); } } setMeasuredDimension(width, height); mRadius = (int) (Math.min(width - getPaddingLeft() - getPaddingRight(), height - getPaddingTop() - getPaddingBottom()) * 1.0f / 2); }

就是获得系统测量好的宽高,和模式后分三种模式去讨论。最终通过setMeasuredimension()确定宽高的值。宽高确定好了,那就可以确定下整个饼状图的半径 mRadius了。取宽高中的较小的那个。

三、onDraw();我们需要画一个一个的扇形,还有将扇形从零到360的动画效果,还有扇形中的文字,中间的文字,还有实现立体感的效果。1.画扇形。画一个扇形还是蛮容易的:通过画布调用drawArc()方法话一个60度的扇形:mPaint.setStyle(Paint.Style.FILL); mTextPaint.setColor(Color.RED); RectF oval = new RectF(-mRadius, -mRadius, mRadius, mRadius); mCanvas.drawArc(oval, 0, 60, true, mPaint);

前面设置下画笔的颜色,style为实心,就这几行代码的事情。第一个参数:是一个正方形,表明在这个区域内画扇形。第二个参数:从哪个角度开始画,第三个参数:就是画的角度度数,而不是画到哪个角度去。第四个参数:是否以正方形中心为圆心。我这样一说,你可能就会有点懵逼了。你自己改为false,看效果,就理解我说的了。效果如下:

Android 扇形进度框 android画扇形_自定义View_02

目前画了一个扇形,如此多的扇形需要用户去设置数据,来确定每个扇形的角度.

public ArrayList getColors() { return colors; } public void setColors(ArrayList colors) { this.colors = colors; } public void setDataMap(LinkedHashMap map) { this.kindsMap = map; } public String getCenterTitle() { return centerTitle; } public void setCenterTitle(String centerTitle) { this.centerTitle = centerTitle; }

然后通过遍历,一 一画出这些扇形:

@Override protected void onDraw(Canvas mCanvas) { super.onDraw(mCanvas); mCanvas.translate((getWidth() + getPaddingLeft() - getPaddingRight()) / 2, (getHeight() + getPaddingTop() - getPaddingBottom()) / 2); paintPie(mCanvas); } private void paintPie(final Canvas mCanvas) { if (kindsMap != null) { Set entrySet = kindsMap.entrySet(); Iterator iterator = entrySet.iterator(); int i = 0; float currentAngle = 0.0f; while (iterator.hasNext()) { Map.Entry entry = iterator.next(); int num = entry.getValue(); float needDrawAngle = num * 1.0f / sum * 360; mPaint.setColor(colors.get(i)); mCanvas.drawArc(oval, currentAngle, needDrawAngle - 1, true, mPaint); currentAngle = currentAngle + needDrawAngle; i++; } } }

记得,要先将画笔移到画布中心,currentAngle:当前的角度值,needDrawAngle :需要画多少角度。sum:数据的总个数,为每一个数据相加的和,计算百分比。效果如图:

Android 扇形进度框 android画扇形_Android 扇形进度框_03

2、动画效果实现

咋一看离我们的预期效果还差好远。只有画完以后的样子,动画都没有。别急别急,动画马上就来:

private void initAnimator() { ValueAnimator anim = ValueAnimator.ofFloat(0, 360); anim.setDuration(10000); anim.setInterpolator(new AccelerateDecelerateInterpolator()); anim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { animatedValue = (float) valueAnimator.getAnimatedValue(); invalidate(); } }); anim.start(); }

设置一个动画,在10秒内,随着时间的推移,animatedValue从0到360度发生变化,每一次监听到animatedValue的值得改变,就去刷新View,执行ondraw();这个时候我们来修改下前面的paintPie();

if (Math.min(needDrawAngle-1, animatedValue - currentAngle) >= 0) { mPaint.setColor(colors.get(i)); mCanvas.drawArc(oval, currentAngle, Math.min(needDrawAngle - 1, animatedValue - currentAngle), true, mPaint); }

先看效果:

Android 扇形进度框 android画扇形_Android_04

我在这个地方遇到了一个坑----ondraw()方法多次执行时,每一次执行完毕后会抹去上一次画过的图形.也就是说,我没用动画的时候,ondraw()方法就调用了一次,调用了while循环后,把所有的扇形画出来了。用了动画之后,animatedValue的值一改变,就会执行一次while循环。每次while循环都会重头开始画,我们只要保证每次while循环所画的角度正好是animatedValue,就ok了。所以,在needDrawAngle-1小于animatedValue - currentAngle时,就把该部分扇形画出来。在needDrawAngle-1大于animatedValue - currentAngle时,那就只画animatedValue - currentAngle的角度。

之所以减一,是因为扇形之间留一条白白的缝隙。

3.添加文字,中间标题,实现立体感

当某个扇形角度,不够文字的宽度时,我们就会将文字画在圆的外面。所以这个我们得添加一个属性minAngle,角度最小值,添加getter和setter方法,让使用者去设置。当needDrawAngle小于这个角度值时,就画在外面。大于这个值就画在扇形中央。

//画文字 private void drawText(Canvas mCanvas, float textAngle, String kinds, float needDrawAngle) { Rect rect = new Rect(); mTextPaint.setTextSize(sp2px(15)); mTextPaint.getTextBounds(kinds, 0, kinds.length(), rect); if (textAngle >= 0 && textAngle 90 && textAngle 180 && textAngle = 0) { mPaint.setColor(colors.get(i)); mCanvas.drawArc(oval, currentAngle, Math.min(needDrawAngle - 1, animatedValue - currentAngle), true, mPaint); mPaint.setColor(Color.WHITE); mPaint.setAlpha(10); mCanvas.drawCircle(0, 0, mRadius / 2 + dp2px(10), mPaint); mPaint.setAlpha(255); mCanvas.drawCircle(0, 0, mRadius / 2, mPaint); drawCenterText(mCanvas, centerTitle, 0, 0, mTextPaint); drawText(mCanvas, textAngle, kinds, needDrawAngle); } currentAngle = currentAngle + needDrawAngle; i++; }

该画的画完了。接下来就是一些优化和修改了。

四、优化1.添加dp,sp,px转化工具/** * dp 2 px * * @param dpVal */ protected int dp2px(int dpVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } /** * sp 2 px * * @param spVal * @return */ protected int sp2px(int spVal) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics()); }

因为图中有很多地方接触到文字,如果设置文字的时候用的是px,那么在不同的机器上,显示的文字大小将会差距很大。最好也提供一些方法让使用者去设置这些字体的大小。

2.让使用者去开启动画

因为数据是使用者设置上去的,什么时候开启动画,还得提供一个公开方法:

public void startDraw() { if (kindsMap != null && colors != null && centerTitle != null) { initAnimator(); } }3.设置数据优化

可以知道,我用了两个容器来装数据。有没有感觉到浪费资源?其实我们完全可以只用一个ArrayList即可。对吧。将所有数据定义为一个实体的bean类。说到这,又遇到一个坑,我最开始使用hashMap去存数据的,优越hashMap存放数据是无序的,所以每次画出来的扇形结构不一样。你也许又会问我,怎么颜色总是飘忽不定啊?因为我是用了个for循环随机生成的颜色,哈哈。

@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); PieChatView pieChatView = (PieChatView) findViewById(R.id.pie); kindsMap.put("苹果", 10); kindsMap.put("梨子", 30); kindsMap.put("香蕉", 10); kindsMap.put("葡萄", 30); kindsMap.put("哈密瓜", 10); kindsMap.put("猕猴桃",30); kindsMap.put("草莓", 10); kindsMap.put("橙子", 30); kindsMap.put("火龙果", 10); kindsMap.put("椰子", 20); for (int i = 1; i 10&&Math.abs(r-b)>10&&Math.abs(b-g)>10){ colors.add(color); } } pieChatView.setCenterTitle("水果大拼盘"); pieChatView.setDataMap(kindsMap); pieChatView.setColors(colors); pieChatView.setMinAngle(50); pieChatView.startDraw();

到这里,文章就结束了。最后附上代码的github地址: https://github.com/Demidong/ClockView 我写的博客还不是很多,欢迎大家参与讨论,留言,指正我的不足。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3