画个爱心——使用Kotlin自定义View

3,776 阅读5分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 13 天,点击查看活动详情

楔子

昨天我们刚刚看了一下Kotlin高阶函数(安卓开发之Kotlin学习——初遇高阶函数 - 掘金 (juejin.cn)),今天理应去继续研究与深入,但由于作者工作一天没去补充高阶函数知识,无法填坑,所以决定将后续改到周日。

本篇我们来看看Kotlin语言版的自定义View,之前一直看到Kotlin的自定义View就不想做。但在这段时间看了一些关于Kotlin自定义View的文章后,我感觉可以稍微写一下了。

之前评论区的小伙伴好像还想看画动画,如果用Java我似乎没有问题的,但在家里我主要坚持用Kotlin来写安卓,所以Kotlin面对=向对象方面的语法不熟让我一直对Kotlin的类的构造函数有点不适应,而继承View后的构造我之前没看别人写的我自己有点无从下手,更别说去draw了,而这次用自定义View后我很快就会去看看动画如何做,然后再出文章来分享。

自定义View

言归正传,我们还是继续看如何写自定义View。

View类的构造函数:

image.png

在Java中,我很熟练了,之前工作开发中已经写了不少自定义控件,从自定义进度条Progress到自定义倍数选择器然后到最近的摇杆控件,只要你多看看别人怎么去实现自定义的,然后把自己想要的控件给画好就成功大半,下面我们采用path路径draw方式去画一个爱心

Java实现对比

下面我们先看看Java的自定义View:

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.View;

public class TestViewJava extends View {

    public TestViewJava(Context context) {
        super(context, null);
        init();
    }

    public TestViewJava(Context context, AttributeSet attrs) {
        super(context, attrs, 0);
        init();
    }

    public TestViewJava(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private Paint paint;

    private Path path;

    void init() {
        paint = new Paint();

        path = new Path();

        //抗锯齿
        paint.setAntiAlias(true);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

//        paint.setColor(getResources().getColor(R.color.black));
//        paint.setTextSize(36);
//        canvas.drawText("Text Java", 200, 200, paint);

        paint.setColor(getResources().getColor(R.color.red));
//        canvas.drawCircle(50,50, 20, paint);

        //使用路径画爱心

        //弧形
        // startAngle 是弧形的起始角度(x 轴的正向,即正右的方向,是 0 度的位置;顺时针为正角度,逆时针为负角度)
        // sweepAngle 是弧形划过的角度
        path.addArc(200, 200, 400, 400, -225, 225);

        //画弧形
        //forceMoveTo :是否留下移动的痕迹 true-无痕迹,false-留下痕迹
        path.arcTo(400, 200, 600, 400, -180, 225, false);
        // 从当前位置向目标位置画一条直线(当前位置,即最后一次调用画 Path 的方法的终点位置,初始值为原点 (0, 0))
        // x 和 y 是目标位置的坐标
        // 参数是绝对坐标
        path.lineTo(400, 542);

        canvas.drawPath(path, paint);

    }
}

实现Kotlin自定义View

再看Kotlin的实现:

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View

class TestView : View {

    private var mPaint: Paint? = null

    private var path: Path? = null

    constructor(context : Context) : super(context, null) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, null, 0) {
        init()
    }

    constructor(context: Context, attrs: AttributeSet?, def : Int) : super(context, attrs, def) {
        init()
    }

    private fun init() {
        mPaint = Paint()
        path = Path()

        //抗锯齿
        mPaint!!.isAntiAlias = true

    }

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        //使用路径画爱心
        path!!.addArc(200f, 200f, 400f, 400f, -225f, 225f)

        path!!.arcTo(400f, 200f, 600f, 400f, -180f, 225f, false)

        path!!.lineTo(400f, 542f)

        canvas!!.drawPath(path!!, mPaint!!)

    }
}

对比代码后,我们可以发现其实最主要就是构造函数的实现方式不一样,在Kotlin中我们使用关键字constructor构造,而Java中我们是使用与类名相同的方法(构造函数),而Koltin还多了空安全,且不用去new对象。 2/18勘正 上面的代码,昨晚作者在Activity中试图获取布局中添加的上述代码的id,然后出现空指针闪退的情况,所以代码做下面的调整:

import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Path
import android.util.AttributeSet
import android.view.View

class BallView : View {

    private var mPaint : Paint? = null

    private var path : Path? = null

    constructor(context : Context) : this(context, null)

    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)

    constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initPaint()
    }

    private fun initPaint() {
        mPaint = Paint()
        path = Path()
        //抗锯齿
        mPaint!!.isAntiAlias = true
        //防抖动
        mPaint!!.isDither = true
    }

    override fun draw(canvas: Canvas?) {
        super.draw(canvas)
        mPaint!!.color = Color.RED
        //使用路径画爱心
        path!!.addArc(200f, 200f, 400f, 400f, -225f, 225f)

        path!!.arcTo(400f, 200f, 600f, 400f, -180f, 225f, false)

        path!!.lineTo(400f, 542f)

        canvas!!.drawPath(path!!, mPaint!!)

    }

}

我们增加了Paint的防抖动方法调用,然后主要是把次构造函数前两个的super改为this,最终指向最后一个构造,只需要在最后一个构造写init()初始化即可,这样放入布局加id后在Activity中可正常调用。

总体而言,写起来两者还是挺相似的,这也告诉作者只有动手才知道难度。

实现效果预览

我们将两个自定义View控件都放入布局,如果没有错误,我们在预览或者运行完成程序后就能看到效果图,如下:

image.png

尾声

实现了Kotlin的自定义View后,我也可以尝试用Kotlin去实现一些动画(希望没有什么语法障碍,明明说会Java开发安卓换Kotlin很容易,可能还是作者实践少了),比如之前我用CSS画的爱心效果(情人节专场——爱心动画 - 掘金 (juejin.cn)),我们理论上在安卓上也可以实现相同效果,先让作者看看应该用哪种动画模式,后面实现再分享出来。