Kotlin编写自定义控件复习笔记

最近一年在公司要么写React Native,要么写前端WEB,好久没有写安卓原生代码和Kotlin了,刚刚来了个需求,RN项目安卓端更新不用跳转应用市场,直接通过后台的url下载apk,下载apk的时候进度条是定制的,如图:
《Kotlin编写自定义控件复习笔记》 更新UI图

ok,这就是一个很简单的弹框,其中进度条、进度文字、进度标识图片这些因素是和进度搭边的,所以把他们单独抽取出来做成一个控件CarProgressView,先上代码:

package com.carfinanceapp.update

import android.content.Context
import android.graphics.*
import android.util.AttributeSet
import android.view.View
import com.carfinanceapp.R
import com.carfinanceapp.utils.dp2px

class CarProgressView @JvmOverloads constructor(
        context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    var progress = 0
        set(value) {
            field = value
            invalidate()
        }
    private var numPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var bgPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var linePaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var carPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private var bgRect = RectF()
    private var lineRect = RectF()
    private val carBitmap = BitmapFactory.decodeResource(resources, R.drawable.ic_progress_car)

    init {
        numPaint.textAlign = Paint.Align.CENTER
        numPaint.textSize = 24f.dp2px()
        numPaint.color = Color.parseColor("#52D46E")
        bgPaint.color = Color.parseColor("#F0F0F0")
        linePaint.color = Color.parseColor("#52D46E")
        carPaint.isFilterBitmap = true
    }


    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
        canvas ?: return
        canvas.drawText("$progress%", measuredWidth / 2f, 30f.dp2px(), numPaint)
        drawBg(canvas)
        drawProgress(canvas)
        drawCar(canvas)
    }

    private fun drawBg(canvas: Canvas) {
        bgRect.top = 50f.dp2px()
        bgRect.left = measuredWidth * 0.06f
        bgRect.right = measuredWidth * 0.94f
        bgRect.bottom = 55f.dp2px()
        canvas.drawRoundRect(bgRect, 5f.dp2px(), 5f.dp2px(), bgPaint)
    }

    private fun drawProgress(canvas: Canvas) {
        lineRect.top = 49.5f.dp2px()
        lineRect.left = measuredWidth * 0.06f
        lineRect.right = measuredWidth * 0.94f * progress / 100f
        lineRect.bottom = 55.5f.dp2px()
        canvas.drawRoundRect(lineRect, 5f.dp2px(), 5f.dp2px(), linePaint)
    }

    private fun drawCar(canvas: Canvas) {
        //start -5 end -35
        canvas.drawBitmap(carBitmap, measuredWidth * 0.94f * progress / 100f - 5.dp2px() - (0.3f * progress).dp2px(), 45f.dp2px(), carPaint)
    }
}
  1. 重载方法注解 @JvmOverloads
    首先看类文件的开头,正常来说Kotlin继承父类或者是实现接口都是通过:来的,例如下面这些
//无参继承
open class Father
class Test() : Father()
//有参继承
open class Father2(age: Int)
class Test2(age: Int) : Father2(age)
class Test3() : Father2(99)
class Test4(age: Int) : Father2(99)

上面这些都是单构造方法,没有重载的,像安卓自定义View,继承于View就有四个重载方法,从最少传入Context,到最多传入Context,AttributeSet,defStyleAttr,defStyleRes这四个,Kotlin重载构造方法只需要使用@JvmOverloads constructor就可以了,而方法fun前面只用加 @JvmOverloads就行了

  1. field成员变量自带get、set方法
    看本类第一个成员变量:
var progress = 0
        set(value) {
            field = value
            invalidate()
        }

kotlin默认所有成员变量和方法都是public的,如果不想要public,得加上private,声明变量使用var或者val,一个可变一个类似于final不可变
所以第一行var progress = 0等价于java代码:int progress = 0且有两个public的方法,一个getProgress,一个getProgress
下面的 set(value) {…}就是重写了这个默认提供的set方法,加了一个刷新界面显示的invalidate()

  1. init初始化代码块
    kotlin把构造方法的参数声明在类名后面了,也没有构造方法的fun,所以类创建的时候需要初始化的代码就要写在init{…}里面
  2. 扩展方法
    这个就好玩了,比如我init代码中的第二行 :
    numPaint.textSize = 24f.dp2px()
    这里24f.dp2px()就是我给Float添加的扩展方法dp2px,扩展方法声明很简单,在任意kt文件里面都可以,去年一个项目中创建了一个NumberExtend.kt就直接拿来用了,里面就是各种dp和px互相转换的扩展方法,代码如下:
package com.carfinanceapp.utils

import android.content.res.Resources
import android.view.ViewGroup

/**
 * Created by liutanglei on 2017/11/4
 */
fun Int.dp2px(): Int {
    if (toInt() in intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) {
        return this
    }
    return (this * (Resources.getSystem().displayMetrics.densityDpi / 160f)).toInt()
}

fun Int.px2dp(): Int {
    if (toInt() in intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) {
        return this
    }
    val metrics = Resources.getSystem().displayMetrics
    return this / (metrics.densityDpi / 160)
}

fun Float.dp2px(): Float {
    if (toInt() in intArrayOf(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)) {
        return this
    }
    return this * (Resources.getSystem().displayMetrics.densityDpi / 160f)
}

如上面所示,格式就是fun 类名.扩展方法名():返回值{扩展代码}
同理还有扩展属性,这里我没用使用,效果的话就是扩展一个没有属性,和这个类似

至于进度条控件的话,用画布画灰色的背景,绿色的进度,绿色的文字,还有汽车的图片,除了第一个,其他三个都要随着当前的progress改变,这些没什么好说的

对了,kotlin拼接字符串也很方便,”${变量}字符串” , 和es6里面的`${变量}字符串`有异曲同工之妙

写到这里马上就要下班了,总结一下就是kotlin能减少大量冗余的java代码,更优雅更快速的开发工程,有机会用就尽量用吧,不说了,准备下班溜了

    原文作者:逆水行舟丶
    原文地址: https://www.jianshu.com/p/627c42bbdd61
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞