自定义View实现绘制2D饼型图

       这篇文章实际上主要是介绍如何自定义View的,至于绘制什么图形只是我把以前写的View里带的功能顺便提上来的,不重要。效果图如下,作为百度地图中的覆盖物呈现。圆的大小,扇形的数量、颜色、角度都不是写死固定的,可按需求修改,好像有点扯远了。。。

    说说View吧,一般来说自定义View需要重写onMeasure()、onDraw()这两个方法。这两个方法干嘛用的呢?打个比方,你要量身定制一套衣服,那么店家肯定要先帮你测下身高、腰围什么的,其中onMeasure()就是干这事的,他要测量宽高尺寸,xml布局文件里面虽然已经指定好了宽高尺寸,但要知道所谓的指定是wrap_content、match_parent和固定尺寸,就好像有的人报身高的时候会说1米7左右吧,或是跟姚明一样就行了,还有老实报身高的。不过onMeasure的服务也很到位,测量模式也有三种UNSPECIFIED(父容器没有对View有任何限制,当前View可以任意取尺寸)、EXACTLY(当前的尺寸就是View应该取的尺寸)和AT_MOST(合身,取View能取的最大尺寸),不过不好意思,两者并不是对应关系(尴尬)。

match_parent和固定尺寸对应EXACTLY,因为match_parent实际上也是确定的。

wrap_content对应的是AT_MOST,这个也好理解,因为每次设置为这个属性时,view表现出来的总是大小刚刚好。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val measureHeightSize = View.MeasureSpec.getSize(heightMeasureSpec)
        val measureWidthtSize = View.MeasureSpec.getSize(widthMeasureSpec)

        val measureHeightModel = View.MeasureSpec.getMode(heightMeasureSpec)
        val measureWidthtModel = View.MeasureSpec.getMode(widthMeasureSpec)

        var defaultHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                30f, context.resources.displayMetrics).toInt()
        var defaultWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                30f, context.resources.displayMetrics).toInt()
        if (measureHeightModel == View.MeasureSpec.AT_MOST) {
            defaultHeight = Math.min(defaultHeight, measureHeightSize)
        } else if (measureHeightModel == View.MeasureSpec.EXACTLY) {
            defaultHeight = measureHeightSize
        }

        if (measureWidthtModel == View.MeasureSpec.AT_MOST) {
            defaultWidth = Math.min(defaultWidth, measureWidthtSize)
        } else if (measureWidthtModel == View.MeasureSpec.EXACTLY) {
            defaultWidth = measureWidthtSize
        }

        setMeasuredDimension(defaultWidth, defaultHeight)
    }

    val measureHeightSize = View.MeasureSpec.getSize(heightMeasureSpec)
    val measureWidthtSize = View.MeasureSpec.getSize(widthMeasureSpec)

    取测量尺寸

    val measureHeightModel = View.MeasureSpec.getMode(heightMeasureSpec)
    val measureWidthtModel = View.MeasureSpec.getMode(widthMeasureSpec)

    取测量模式

    注意上面得到的测量尺寸是父View提供的参考大小。

    我给他设置了默认大小为30dp,当测量模式为UNSPECIFIED时(没指定那就默认吧);当测量模式为AT_MOST时(wrap_content)取两者最小值;当测量模式为EXACTLY(固定大小),那么测量尺寸就是我要的尺寸了。最后记得setMeasureDimension(width, height)。

       好了,量完身,方形布料也剪好了,接下来要裁剪布料设计款式,穿针引线,缝点艺术图案上去,比如把衣服做成圆形和扇形,加点花花绿绿的颜色什么的。。。

      

override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        maxWidth = this.measuredWidth
        maxHeight = this.measuredHeight
        maxWidth = Math.min(maxWidth, maxHeight)

        paint.isAntiAlias = true

        var more = 0f
        if (mSectRoundable) {
            paint.color = mSectorRoundColor
            paint.style = Paint.Style.STROKE
            val rectF = RectF(mCentreX, mCentreY,
                    maxWidth + mCentreX, maxWidth + mCentreY)
            canvas.drawArc(rectF, mStartAngle, mSweepAngle, true, paint)
            more = mSectorRoundWidth
        }


        if (mRoundable) {
            more = Math.max(more, mRoundWidth)
        }
        paint.color = sectorColor
        paint.style = Paint.Style.FILL
        val rectF1 = RectF(mCentreX + more, mCentreY + more,
                maxWidth + mCentreX - more, maxWidth + mCentreY - more)
        canvas.drawArc(rectF1, mStartAngle, mSweepAngle, true, paint)

        if (mRoundable) {
            paint.style = Paint.Style.STROKE
            paint.color = mRoundColor
            paint.strokeWidth = mRoundWidth
            canvas.drawCircle((maxWidth / 2).toFloat(), (maxWidth / 2).toFloat(), (maxWidth - mRoundWidth) / 2, paint)
        }
    }

        作为裁缝,你可以在这块布上做任何事情,包括剪出你想要的形状,底色,前提是你要知道如何使用绘制工具和染色剂。说到这里我有些后悔为什么当初不比喻成裁纸和画画呢。。。

        maxWidth = this.measuredWidth 获取画布宽度

        canvas.drawArc()绘制扇形,里面有4个参数RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint,第一个参数是RectF,就是一个矩形,RectF(float left, float top, float right, float bottom),矩形的性质大家都知道了,所以用一个左上角和右下角这两个对角线的点就可以画出矩形了,可能有人要问,为什么不是另外两个点呢,嗯,我也不知道。这个矩形是一个正方形,我们要画的圆就画在里面,是矩形里面最大的圆。第二参数startAngle是起始度数。第三个sweepAngle叫角度差值,可不是结束的读书大小,这里呢就是扇形的度数了。useCenter就问你圆是不是要画在矩形中间。paint就是画笔,决定画出来的是什么线(包括颜色、粗细、虚实等等)。

        另外有一点要说的,如果要在布局文件上用我们的自定义的属性,记得先在res/values/attr.xml文件下或者styles.xml(如果没有请自己新建,文件名理论上并没有严格规定)声明一个自定义属性。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="SectorView">
        <attr name="radius" format="integer"/>
        <attr name="centreX" format="dimension"/>
        <attr name="centreY" format="dimension"/>
        <attr name="roundColor" format="color"/>
        <attr name="sect_roundWidth" format="dimension"/>
        <attr name="sect_roundColor" format="color"/>
        <attr name="sectorColor" format="color"/>
        <attr name="startAngle" format="float"/>
        <attr name="sweepAngle" format="float"/>
        <attr name="roundShowable" format="boolean"/>
        <attr name="sect_roundShowable" format="boolean"/>
        <attr name="roundWidth" format="dimension"/>
    </declare-styleable>
</resources>

    name建议跟自定义View名取一样的,其他自定义view的自定义属性声明也可以添加进去,只要保证格式一样就行了。在构造函数里获取到属性集合的标签。R.styleable.SectorView就是我们上面声明的。typedArray.getColor里面有两个参数,前面一个表示我们要获取的是哪个属性,后面一个在获取不到指定属性时会赋予mRoundColor,是一个默认值。用完后记得typedArray.recycle回收,要养成勤俭节约的好习惯。

val typedArray = context.obtainStyledAttributes(attrs, R.styleable.SectorView)
mRoundColor = typedArray.getColor(R.styleable.SectorView_roundColor, Color.GRAY)
typedArray.recycle()

另外布局文件中的最外层的layout一定要加上xmlns:app="http://schemas.android.com/apk/res-auto",冒号后面的app可以改成别的名字,随你喜好,然后引用的时候是以app:属性名称的形式。这样就可以取到布局文件里指定的值了。

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    tools:context="com.intelligenceSecuritySystem.ui.adjustPlan.AddAjustPlanActivity">
    <com.intelligenceSecuritySystem.view.SectorView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:sweepAngle="30"
        />
</android.support.constraint.ConstraintLayout>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值