第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

為了賬號安全,請及時綁定郵箱和手機立即綁定

PieChart扇形圖的實現(xiàn)

標(biāo)簽:
Android

一、数据需求

来分析下,用户需要提供怎样的数据,首先要有数据的值,然后还需要对应的数据名称,以及颜色。绘制PieChart需要什么呢,由图可以看出,需要百分比值,扇形角度,色块颜色。所以总共属性有:

[代码]java代码:

?

1

2

3

4

5

6

7

public class PieData {

    private String name;

    private float value;

    private float percentage;

    private int color = 0;

    private float angle = 0;

}

 

各属性的set与get请自行增加。

二、构造函数

构造函数中,增加一些xml设置,创建一个attrs.xml

[代码]xml代码:

?

1

2

3

4

5

6

7

8

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

<resources>

    <declare-styleable name="PieChart">

        <attr   name="name" format="string"/>

        <attr   name="percentDecimal" format="integer"/>

        <attr   name="textSize" format="dimension"/>

    </declare-styleable>

</resources>

 

这是只设置了一部分属性,如果你有强迫症希望全部设置的话,可以自行增加。在PieChart中使用TypedArray进行属性的获取。建议使用如下的写法,可以避免在没有设置属性时,也运行getXXX方法

[代码]java代码:

?

1

2

3

TypedArray array =   context.obtainStyledAttributes(attrs, R.styleable.PieChart,   defStyleAttr,defStyleRes);

int n = array.getIndexCount();

for (int i=0; i<n; i++){="" int=""   attr="array.getIndex(i);" switch="" (attr){="" case=""   r.styleable.piechart_name:="" name="array.getString(attr);"   break;="" r.styleable.piechart_percentdecimal:="" percentdecimal="array.getInt(attr,percentDecimal);"   r.styleable.piechart_textsize:="" percenttextsize="array.getDimensionPixelSize(attr,percentTextSize);"   }="" array.recycle();<="" pre=""></n;>

 

三、动画函数

绘制一个完整的圆,旋转的角度为360,动画时间为可set参数,默认5秒,监听animatedValue参数,用于与绘制时进行计算。

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

private void initAnimator(long duration){

    if (animator !=null &&animator.isRunning()){

        animator.cancel();

        animator.start();

    }else {

        animator=ValueAnimator.ofFloat(0,360).setDuration(duration);

        animator.setInterpolator(timeInterpolator);

        animator.addUpdateListener(new   ValueAnimator.AnimatorUpdateListener() {

            @Override

            public   void onAnimationUpdate(ValueAnimator animation) {

                animatedValue   = (float) animation.getAnimatedValue();

                invalidate();

            }

        });

        animator.start();

    }

}

 

四、onMeasure

View默认的onMeasure方法中,并没有根据测量模式,对布局宽高进行调整,所以为了适应wrap_content的布局设置,需要对onMeasure方法进行重写。

[代码]java代码:

?

1

2

3

4

5

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)   {

    int width =   measureDimension(widthMeasureSpec);

    int height =   measureDimension(heightMeasureSpec);

    setMeasuredDimension(width,height);

}

 

重写的onMeasure方法,调用了自定义的measureDimension方法处理数据,完成后交给系统的setMeasuredDimension方法。接下来看下自定义的measureDimension方法。

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

private int measureDimension(int measureSpec){

    int size = measureWrap(mPaint);

    int specMode =   MeasureSpec.getMode(measureSpec);

    int specSize =   MeasureSpec.getSize(measureSpec);

    switch (specMode){

        case MeasureSpec.UNSPECIFIED:

            size   = measureWrap(mPaint);

            break;

        case MeasureSpec.EXACTLY:

            size   = specSize;

            break;

        case MeasureSpec.AT_MOST:

            //合适尺寸不得大于View的尺寸

            size   = Math.min(specSize,measureWrap(mPaint));

            break;

    }

    return size;

}

 

measureDimension根据测量的类型,分别计算尺寸的长度。EXACTLY是在xml中定义match_parent以及具体的数值是使用,而AT_MOST则是在wrap_content时使用,measureWrap方法用于计算当前PieChart的最小合适长度,接下来看看这个方法。

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

protected void onSizeChanged(int w, int h, int oldw,   int oldh) {

    super.onSizeChanged(w, h, oldw,   oldh);

    mWidth =   w-getPaddingLeft()-getPaddingRight();//适应padding设置

    mHeight =   h-getPaddingTop()-getPaddingBottom();//适应padding设置

    mViewWidth = w;

    mViewHeight = h;

    //标准圆环

    //圆弧

    r = (float)   (Math.min(mWidth,mHeight)/2*widthScaleRadius);// 饼状图半径

    // 饼状图绘制区域

    rectF.left = -r;

    rectF.top = -r;

    rectF.right =r;

    rectF.bottom = r;

    //白色圆弧

    //透明圆弧

    rTra = (float)   (r*radiusScaleTransparent);

    rectFTra.left = -rTra;

    rectFTra.top = -rTra;

    rectFTra.right = rTra;

    rectFTra.bottom = rTra;

    //白色圆

    rWhite = (float) (r*radiusScaleInside);

 

    //浮出圆环

    //圆弧

    // 饼状图半径

    rF = (float)   (Math.min(mWidth,mHeight)/2*widthScaleRadius*offsetScaleRadius);

    // 饼状图绘制区域

    rectFF.left = -rF;

    rectFF.top = -rF;

    rectFF.right = rF;

    rectFF.bottom = rF;

    ...

}

 

测量宽高的方式类似于TextView,根据PieChart中的图名与百分比文本的宽度进行计算的。其中stringId是在处理数据的过程中,计算出的拥有最长字符的区域Id。

从代码中可以看出,wrap_content情况下的,PieChart的宽高就等于百分比字符长度的4倍,加上图名的长度。

五、onSizeChanged

在此函数中,获取当前View的宽高以及根据padding值计算出的实际绘制区域的宽高,同时进行PieChart绘制所需的半径以及布局位置设置。

说明: onSizeChange

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

protected void onSizeChanged(int w, int h, int oldw,   int oldh) {

    super.onSizeChanged(w, h, oldw,   oldh);

    mWidth =   w-getPaddingLeft()-getPaddingRight();//适应padding设置

    mHeight =   h-getPaddingTop()-getPaddingBottom();//适应padding设置

    mViewWidth = w;

    mViewHeight = h;

    //标准圆环

    //圆弧

    r = (float)   (Math.min(mWidth,mHeight)/2*widthScaleRadius);// 饼状图半径

    // 饼状图绘制区域

    rectF.left = -r;

    rectF.top = -r;

    rectF.right =r;

    rectF.bottom = r;

    //白色圆弧

    //透明圆弧

    rTra = (float)   (r*radiusScaleTransparent);

    rectFTra.left = -rTra;

    rectFTra.top = -rTra;

    rectFTra.right = rTra;

    rectFTra.bottom = rTra;

    //白色圆

    rWhite = (float)   (r*radiusScaleInside);

 

    //浮出圆环

    //圆弧

    // 饼状图半径

    rF = (float)   (Math.min(mWidth,mHeight)/2*widthScaleRadius*offsetScaleRadius);

    // 饼状图绘制区域

    rectFF.left = -rF;

    rectFF.top = -rF;

    rectFF.right = rF;

    rectFF.bottom = rF;

    ...

}

 

六、onDraw

onDraw分为绘制扇形,绘制文本,绘制图名三个部分。绘制扇形和文本时需要与Valueanimator的监听值进行计算,完成动画;另外还要在Touch时进行交互,完成浮出动画。
在进行具体的绘制之前,需要坐标原点平移至中心位置,并且判断数据是否为空。

1、绘制扇形

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

float currentStartAngle = 0;// 当前起始角度

canvas.save();

canvas.rotate(mStartAngle);

float drawAngle;

for (int i=0; i<mpiedata.size(); i++){="" piedata=""   pie="mPieData.get(i);" if="" (math.min(pie.getangle()-1,animatedvalue-currentstartangle)="">=0){

        drawAngle   = Math.min(pie.getAngle()-1,animatedValue-currentStartAngle);

    }else {

        drawAngle   = 0;

    }

    if (i==angleId){

        drawArc(canvas,currentStartAngle,drawAngle,pie,rectFF,rectFTraF,reatFWhite,mPaint);

    }else {

        drawArc(canvas,currentStartAngle,drawAngle,pie,rectF,rectFTra,rectFIn,mPaint);

    }

    currentStartAngle +=   pie.getAngle();

}

canvas.restore();</mpiedata.size();>

 

·         根据当前的初始角度旋转画布。初始化扇形的起始角度,通过累加计算出下一次的起始角度。

·         drawArc用于绘制扇形,和上一篇最后的环形图片一样,通过一大一小两个扇形进行补集运算,获得可知半径的及宽度的圆环,只不过这里多了一个为了立体效果而增加的半透明圆弧。

·         绘制扇形时,使用当前的动画值减去起始角度与当前的扇形经过的角度对比取小,作为当前扇形的需要绘制的经过角度。减1是为了生存扇形区域之间的间隔。

·         angleId用于Touch时显示点击是哪一块扇形,具体判断会在TouchEvent中进行。

2、绘制文本

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

//扇形百分比文字

currentStartAngle = mStartAngle;

for (int i=0; i<mpiedata.size(); i++){="" piedata=""   pie="mPieData.get(i);" mpaint.setcolor(percenttextcolor);=""   mpaint.settextsize(percenttextsize);="" mpaint.settextalign(paint.align.center);=""   numberformat="" numberformat.setminimumfractiondigits(percentdecimal);=""   根据paint的textsize计算y轴的值="" if="" (animatedvalue="">pieAngles[i]-pie.getAngle()/2&&percentFlag)   {

        if (i   == angleId) {

            drawText(canvas,pie,currentStartAngle,numberFormat,true);

        } else {

            if   (pie.getAngle() > minAngle) {

                drawText(canvas,pie,currentStartAngle,numberFormat,false);

            }

        }

        currentStartAngle   += pie.getAngle();

    }

}</mpiedata.size();>

 

文本是有方向的,无法在画布旋转后绘制,所以初始化当前扇形的起始角度为PieChart的起始角度。
* 然后循环绘制文本,当扇形绘制到当前区域的1/2时,开始绘制当前区域的文字。为了防止文本遮挡视线,在绘制前需要判断此扇形经过的角度是否大于最小显示角度。
* angleId用于Touch时显示点击是哪一块扇形,具体判断会在TouchEvent中进行。

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

private void drawText(Canvas canvas, PieData pie ,float   currentStartAngle, NumberFormat numberFormat,boolean flag){

    int textPathX = (int)   (Math.cos(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r +   rTra) / 2);

    int textPathY = (int)   (Math.sin(Math.toRadians(currentStartAngle + (pie.getAngle() / 2))) * (r +   rTra) / 2);

    mPoint.x = textPathX;

    mPoint.y = textPathY;

    String[] strings;

    if (flag){

        strings   = new String[]{pie.getName() + "",   numberFormat.format(pie.getPercentage()) + ""};

    }else {

        strings   = new String[]{numberFormat.format(pie.getPercentage()) + ""};

    }

    textCenter(strings, mPaint,   canvas, mPoint, Paint.Align.CENTER);

}



drawText函数的主要作用就是根据传入的Pie,获取大小扇形的半径合除以2,角度取一半,计算出扇形中心点,然后进行文本绘制。最后累加当前扇形的起始角度,用于下一个扇形使用。

3、绘制图名

[代码]java代码:

?

1

2

3

4

5

6

7

8

9

//饼图名

mPaint.setColor(centerTextColor);

mPaint.setTextSize(centerTextSize);

mPaint.setTextAlign(Paint.Align.CENTER);

//根据Paint的TextSize计算Y轴的值

mPoint.x=0;

mPoint.y=0;

String[] strings = new String[]{name+""};

textCenter(strings,mPaint,canvas,mPoint, Paint.Align.CENTER);

 

绘制图名的部分就比较简单了,和之前绘制单个Pie时类似,获取x,y坐标为(0,0),然后使用textCenter多行文本绘制函数进行文本绘制。

七、onTouchEvent

onTouchEvent用于处理当前的点击事件,具体内容在第一篇文章中已经进行了说明,这里使用其中的ACTION_DOWNACTION_UP事件。

[代码]java代码:

?

01

02

03

04

05

06

07

08

09

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

public boolean onTouchEvent(MotionEvent event) {

    if (touchFlag&&mPieData.size()>0){

        switch (event.getAction()){

            case   MotionEvent.ACTION_DOWN:

                float   x = event.getX()-(mWidth/2);

                float   y = event.getY()-(mHeight/2);

                float   touchAngle = 0;

                if   (x<0&&y<0){

                    touchAngle   += 180;

                }else   if (y<0&&x>0){

                    touchAngle   += 360;

                }else   if (y>0&&x<0){

                    touchAngle   += 180;

                }

                touchAngle   +=Math.toDegrees(Math.atan(y/x));

                touchAngle   = touchAngle-mStartAngle;

                if   (touchAngle<0){

                    touchAngle   = touchAngle+360;

                }

                float   touchRadius = (float) Math.sqrt(y*y+x*x);

                if   (rTra< touchRadius && touchRadius< r){

                    angleId   = -Arrays.binarySearch(pieAngles,(touchAngle))-1;

                    invalidate();

                }

                return   true;

            case   MotionEvent.ACTION_UP:

                angleId   = -1;

                invalidate();

                return   true;

        }

    }

    return super.onTouchEvent(event);

}

 

·         运行之前需要判断PieChart是否开启了点击效果,同时需要判断数据不为空。

·         在用户点击下的时候,获取当前的坐标,计算出这个点与原点的距离以及角度。通过距离可以判断出是否点击在了扇形区域上,而通过角度可以判断出点击了哪一个区域。将判断出的区域Id传递给angleId值,就像我们之前在onDraw中说的那样,重新绘制,根据angleId浮出指定的扇形区域。

·         用户手指离开屏幕时,重置angleId为默认值,并使用invalidate()函数,重新绘制onDraw中变化的部分。


八、小结

经过之前4篇的知识准备,终于迎来了本章的PieChart的具体实现。在本文中重温了之前的绘制流程的各个函数,VlaueAnimator函数,以及Canvas、Path的使用方法,并使用这些方法完成了一个自定义饼图的绘制。

原文链接:http://www.apkbus.com/blog-705730-61814.html

點擊查看更多內(nèi)容
TA 點贊

若覺得本文不錯,就分享一下吧!

評論

作者其他優(yōu)質(zhì)文章

正在加載中
  • 推薦
  • 評論
  • 收藏
  • 共同學(xué)習(xí),寫下你的評論
感謝您的支持,我會繼續(xù)努力的~
掃碼打賞,你說多少就多少
贊賞金額會直接到老師賬戶
支付方式
打開微信掃一掃,即可進(jìn)行掃碼打賞哦
今天注冊有機會得

100積分直接送

付費專欄免費學(xué)

大額優(yōu)惠券免費領(lǐng)

立即參與 放棄機會
微信客服

購課補貼
聯(lián)系客服咨詢優(yōu)惠詳情

幫助反饋 APP下載

慕課網(wǎng)APP
您的移動學(xué)習(xí)伙伴

公眾號

掃描二維碼
關(guān)注慕課網(wǎng)微信公眾號

舉報

0/150
提交
取消