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

2021-04-10  本文已影响0人  让时间走12138

本节内容

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.再次运行,得到了预期的结果
最终
上一篇下一篇

猜你喜欢

热点阅读