美文网首页
Android开发(20)——自定义属性和文本绘制

Android开发(20)——自定义属性和文本绘制

作者: 让时间走12138 | 来源:发表于2021-04-10 16:47 被阅读0次

    本节内容

    1.代码改变view中属性的值

    2.自定义属性设置值

    3.进度圆弧绘制

    4.实现进度变化的动画

    5.绘制文本

    demo展示
    1.这次我们做的是一个显示进度变化的加载动画,点击开始下载之后,进度条会变化,同时百分比也会变化,并且是和进度条同步变化。
    动画进度
    下载过程
    一、代码改变view中属性的值
    1.改变自定义view属性值的两种方式
    • 在代码中直接设置(提供一个set和get方法即可)
    • 自定义属性,在xml中设置
    2.自定义属性的步骤:
    • 创建attr.xml
    • 添加declare-styleable节点
    • 添加attr,包含name和类型
    • xml中使用
    • 代码中解析xml中配置的值
    二、先用代码中设置的方式来实现这个demo
    1.先创建一个类,继承自view,并实现相应的构造方法
    class PercentLoading : View {
          constructor(context: Context):super(context){}
          constructor(context: Context,attrs:AttributeSet):super(context,attrs){}
          constructor(context: Context,attrs: AttributeSet,style:Int):super(context,attrs,style){}
    }
    
    2.首先我们先绘制背景圆圈,那么我们需要确定圆心坐标以及圆的半径,所以先定义几个变量来记录这几个值
        private var cx = 0f
        private var cy = 0f
        private var radius = 0f
    
    3.然后在onSizeChanged方法里面确定cx,cy和radius
    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            cx = width/2f
            cy = height/2f
            //半径等于最小边的一半
            radius = (Math.min(width,height))/2f
        }
    
    4.提供一个绘制背景圆弧的画笔,画笔的颜色和宽度我们就在外部设置
    var progressBgColor = Color.CYAN
    var mstrokeWidth = 50f
    private val progressBgPaint =Paint().apply {
            color = progressBgColor
            style = Paint.Style.STROKE
            strokeWidth = mstrokeWidth
        }
    
    5.在onDraw方法里面绘制圆
    override fun onDraw(canvas: Canvas?) {
            //绘制背景圆圈
            canvas?.drawCircle(cx,cy,radius,progressBgPaint)
    }
    
    6.用代码来改变对象属性的值
    • 在外部,也就是MainActivity里面改变画笔的宽度以及背景颜色
            mLoading.progressBgColor = resources.getColor(R.color.colorAccent,null)
    
    • 然后进行这个操作之后,运行模拟器,没有发生一点变化。因为这只是几个变量发生的变化,但是画笔并没有改变。
    • 所以要重写这个变量的构造方法,这样在外部也能改变画笔的属性
    var progressBgColor = Color.CYAN
            //一开始并不会直接调用此set方法
           set(value) {
               field = value
               progressBgPaint.color = value
           }
    
    7.这次再运行,就和预期效果一样了。但是发现,绘制的圆优点残缺,因为我们绘制圆的时候忽略了画笔的宽度,所以我们要修改一下圆的半径,让它变小一点,也就是减去画笔的宽度。
    radius = (Math.min(width,height))/2f -mstrokeWidth
    
    三、自定义属性设置值
    1.在values里面新建一个resourcefile,取名为attr。在里面添加一个declare-styleable ,声明一下这个是给谁用的(通常是自己定义的类名)。
    <resources>
        //自定义属性    name = "自己的类名"
        <declare-styleable name="PercentLoading">
            //添加一个属性  属性名为backgroundColor 属性的值为color颜色
            //背景圆环的颜色
            <attr name="backgroundColor" format="color|reference"/>
            //进度圆环的颜色
            <attr name="foregroundColor" format="color|reference"/>
            //文字的颜色   如果需要使用系统的 省略 format
            <attr name="android:textColor"/>
        </declare-styleable>
    </resources>
    
    2.然后可以在xml代码中直接配置
            app:backgroundColor="@color/colorAccent"
            app:foregroundColor="@color/colorPrimary"
    
    3.但如果就这样运行程序,结果是没有任何变化,因为我们还没有解析自定义属性对应的值。我们可以直接在重写的构造方法里面解析。
     constructor(context: Context,attrs:AttributeSet):super(context,attrs){
               //解析xml中配置的属性
            //将xml中传递过来的attrs解析出来
            //attrs  xml中为自定义的属性设置的值和属性名的集合
               val typedArray :TypedArray=
              context.obtainStyledAttributes(attrs,R.styleable.PercentLoading)
             typedArray.recycle()
    }
    
    • R.styleable.PercentLoading 依据哪个文件进行解析,因为在attrs中可以有多个declare-styleable,每一个对应的name都不一样,所以根据这个来解析。
    • attrs:从哪里解析,解析的数据就在哪里
    • 返回值:TypedArray
    • 必须使用 typedArray.recycle()回收
    4.解析完了之后就可以取值了,比如获取背景颜色。解析完了之后把结果赋给相应的变量
    progressBgColor=
      typedArray.getColor(R.styleable.PercentLoading_backgroundColor,Color.BLACK)
    
    • 第一个参数是对应的属性,第二个参数是改属性的默认值
    四、进度圆弧绘制
    1.圆弧的颜色和背景色不一样,所以我们把它单独拎出来,并配置对应的set方法
     var progressFgColor=Color.MAGENTA
        set(value) {
            field=value
            progressFgPaint.color=value
        }
    
    2.然后提供绘制圆弧的画笔
    private var progressFgPaint=Paint().apply {
            color = progressFgColor
            style = Paint.Style.STROKE
            strokeWidth = mStrokeWidth
        }
    
    3.一切都准备好了之后,就可以在onDraw方法里面开始绘制圆弧了。false表示圆弧不需要和中心连接在一起。
     canvas?.drawArc(mStrokeWidth,mStrokeWidth, 
          width.toFloat()-mStrokeWidth,height.toFloat()-mStrokeWidth,
                -90f,270f,false,progressFgPaint)
    
    4.为了美观一点,可以把圆弧的起点和终点改的圆润一点
     strokeCap = Paint.Cap.ROUND
    
    圆角
    5.想在代码中改变属性,那么直接在MainActivity里面直接改变即可。如果想在xml中设置,那么就要在attrs里面添加改属性,前面已经添加过了,然后在xml中配置即可(前面设置过了)
    6.之后在构造方法里面解析该属性
     progressFgColor=
        typedArray.getColor(R.styleable.PercentLoading_foregroundColor,Color.MAGENTA)
    
    五、实现进度变化的动画
    1.首先找到变化因子,就是画圆弧时绘制的角度,所以我们定义一个变量来管理我们的进度。
    var progress=0f
         set(value) {
             field=value
             invalidate()
         }
    
    2.所以绘制圆弧时,不是到270f,而是360f*progress
    canvas?.drawArc(mStrokeWidth,mStrokeWidth, 
    width.toFloat()-mStrokeWidth,height.toFloat()-mStrokeWidth,
                -90f,360f*progress,false,progressFgPaint)
    
    3.progress由外部来驱动,所以我们提供两个按钮(开始下载和暂停下载),然后在MainActivity里面监听这两个事件,当它们被点击的时候,就驱动这个动画。但由于我们还没学习网络下载,所以用动画模拟一下下载
    private val downloadAnimator: ValueAnimator by lazy {
            ValueAnimator.ofFloat(0f, 1f).apply {
                duration = 2000
                addUpdateListener {
                    mLoading.progress = it.animatedValue as Float
                }
            }
        }
    
    4.实现两个按钮的点击事件
    mStart.setOnClickListener {
                if (downloadAnimator.isPaused) {
                    downloadAnimator.resume()
                } else {
                    downloadAnimator.start()
                }
            }
    
            mStop.setOnClickListener {
                downloadAnimator.pause()
            }
    
    • start():启动动画
    • end():让动画结束
    • pause():让动画暂停
    • resume():重新启动暂停的动画
    六、绘制文本
    1.进度用百分比来显示。那么我们就要在onDraw方法里面绘制文本。先保存一下文本内容
     val text = "${(progress*100).toInt()}%"
    
    2.给该文本提供一只画笔,textAlign = Paint.Align.CENTER表示文字居中
     private var textPaint=Paint().apply {
            color = textColor
            style = Paint.Style.FILL
            textSize=100f
            textAlign = Paint.Align.CENTER
        }
    
    4.然后绘制文本,其中cx,cy表示文本绘制的位置
     canvas?.drawText(text,cx,cy,textPaint)
    
    5.如果按上面这样运行,会发现文本的位置有一点偏,可以看出来文本并不在最中间,有点偏上了
    image.png
    6.关于文本,它有一条基准线。基准线以下有一条线叫descent,表示文字不会超过这条线。最上方还有一条线叫accent,文字也不会超过这条线。这两条线相当于文字的边界线。我们要计算的就是这两条边界线与基准线之间的距离。
    距离
    • 要计算的就是绿色和红色线之间的距离。
            val metrics= textPaint.fontMetrics
            val space = (metrics.descent-metrics.ascent)/2-metrics.descent
            canvas?.drawText(text,cx,cy+space,textPaint)
    
    7.再次运行,得到了预期的结果
    最终

    相关文章

      网友评论

          本文标题:Android开发(20)——自定义属性和文本绘制

          本文链接:https://www.haomeiwen.com/subject/dremkltx.html