Android APT的学习和使用(二)

实现类似ButterKnife 为控制绑定id

前言

  之前写的第一篇文章 Android APT的学习和使用(一)初步了解kotlin下怎么编写注解并自动生成代码,这次继续实践一下,实现类似BufferKnife 在字段前添加注解自动为我们完成绑定id的功能,在此记录一下。

一、概览

1. 想要实现的效果

在这里插入图片描述
  我们使用注解,在里面传入控件的id,然后就会自动为我们绑定控件,我们在代码里就不用再写findViewById(R.id.xx)这样的代码了.

2. 项目结构

因为是在我这个打算代代相传的demo项目里编写的,所以上一篇文章的项目结构一致
在这里插入图片描述

3.使用kotlinpoet生成的最终代码

  这个代码需要实现的功能、名字、传入的参数等等都要提前想好,保证能够正确执行,最好先自己建个类,然后手动调用该类,确保能够准确执行了,然后再将该代码作为模板,在BindViewProcessor中用kotlinpoet生成代码。

public class AptActivityBinding {
  public fun bind(activity: AptActivity): Unit {
    activity.textView1 = activity.findViewById(2131230798)
    activity.button1 = activity.findViewById(2131230797)
  }
}

二、实现

1. 定义注解

因为是在字段上使用,所以Target用AnnotationTarget.FIELD

@Retention(AnnotationRetention.SOURCE)
@Target(AnnotationTarget.FIELD)
annotation class BindView(val value: Int)
2. 继承AbstractProcessor处理注解并生成我们想要的代码
@AutoService(Processor::class)
@SupportedOptions("MODULE_NAME")
@SupportedAnnotationTypes("com.wzy.apt_annotation.BindView")
@SupportedSourceVersion(SourceVersion.RELEASE_8)
class BindViewProcessor : AbstractProcessor() {
    private var mFiler: Filer? = null

    override fun init(processingEnvironment: ProcessingEnvironment?) {
        super.init(processingEnvironment)
        processingEnvironment?.apply {
            mFiler = filer
        }
    }

    override fun process(p0: MutableSet<out TypeElement>?, p1: RoundEnvironment?): Boolean {
        if (p0.isNullOrEmpty()) return false

        // 构建一个kt文件存放生成的类
        val ktFile = FileSpec.builder("com.wzy.demo", "BindViewAutoCreateFile")

        //获取全部的类
        p1?.rootElements?.forEach {
            //获取类的包名
            val packageName = it.enclosingElement.toString()
            //获取类的名字
            val className = it.simpleName.toString()
            //构建新的类: 原类名+Binding
            val newClassName = ClassName(packageName,"${className}Binding")

            //构建bind(activity: XXXActivity)函数
            val bindFuncitonBuilder = FunSpec.builder("bind")
                .addModifiers(KModifier.PUBLIC)
                .returns(Unit::class)
                .addParameter("activity", ClassName(packageName, className))

            //判断该类下是否使用了BindView注解从而判断是否需要生成代码
            var skip = true
            it.enclosedElements.forEach { enclosedElemnet ->
                if (enclosedElemnet.kind == ElementKind.FIELD) {
                    enclosedElemnet.getAnnotation(BindView::class.java)?.let { bindViewAnnotation ->
                        skip = false
                        bindFuncitonBuilder.addStatement(
                            "activity.%N = activity.findViewById(%L)",
                            enclosedElemnet.simpleName,bindViewAnnotation.value
                        )
                    }
                }
            }

            //是否跳过生成代码
            if (!skip) {
                //构建新的类
                val classBuilder = TypeSpec.classBuilder(newClassName)
                    .addFunction(bindFuncitonBuilder.build())

                //往BindViewAutoCreateFile.kt文件中写入该类
                ktFile.addType(classBuilder.build())
            }

        }

        try {
            mFiler?.let {
                ktFile.build().writeTo(it)
            }
        } catch (e: Exception) {
            e.printStackTrace()
        }

        return true
    }

}
3. 编译项目

查看是否生成我们想要的代码
在这里插入图片描述
在这里插入图片描述
   代码已经生成好了,只要传入activity对象,我们就可以拿到View,然后手动调用findViewById()方法为他们绑定id。

4. 封装Api

  上面说到我们需要调用生成的XXXActivityBinding的bind()方法,并且传入activity就算完成。这种操作肯定不需要我们自己去写,不然每个Activity会生成自己的Binding类,每次调用都要new一个XXXActivityBinding类,然后还要调用该方法。虽然说不是不可以,但是ButterKnife只需要在执行一行代码ButterKnife.bind(this)就可以完成自动绑定,根本不需要管是哪个类,这种api调用明显方便的多。
  所以我们可以写一个api,利用反射帮我们去new XXXActivityBinding,然后执行bind()方法。

object ButterKnifeApi {
    fun bind(activity: Activity) {
        try {
            //获取注解为我们生成的Binding类
            val activityBinding = Class.forName("${activity.packageName}.${activity.javaClass.simpleName}Binding")
            //拿到bind()方法
            val bindFunction = activityBinding.getMethod("bind", activity.javaClass)
            //调用该方法
            bindFunction.invoke(activityBinding.newInstance(), activity)
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}

使用和ButterKnife一样,调用ButterKnifeApi.bind(this)就可以了。

5.示例
class AptActivity : AppCompatActivity() {

    @BindView(R.id.aptTextView1)
    lateinit var textView1: TextView

    @BindView(R.id.aptButton1)
    lateinit var button1: Button

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_apt)
        initViewByBinding()
    }

    private fun initViewByBinding() {
        ButterKnifeApi.bind(this)
        button1.setOnClickListener {
            textView1.text = "绑定成功"
        }
    }
}

在这里插入图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值