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)
image.png5.如果按上面这样运行,会发现文本的位置有一点偏,可以看出来文本并不在最中间,有点偏上了
距离6.关于文本,它有一条基准线。基准线以下有一条线叫descent,表示文字不会超过这条线。最上方还有一条线叫accent,文字也不会超过这条线。这两条线相当于文字的边界线。我们要计算的就是这两条边界线与基准线之间的距离。
要计算的就是绿色和红色线之间的距离。
val metrics= textPaint.fontMetrics
val space = (metrics.descent-metrics.ascent)/2-metrics.descent
canvas?.drawText(text,cx,cy+space,textPaint)
最终7.再次运行,得到了预期的结果