android butterknife 自定义view,教你实现自己的 butterknife

项目主要是为了让大家了解 写一个动态注入的框架 是怎么一个流程,都需要什么,怎么做。

大体思路

思路

编译期间,通过自定义处理器,动态生成代码

程序运行时,通过反射调用 动态生成的代码中的方法

最后达到绑定控件的目的

整体结构

整个项目需要三个model

app

java lib

android lib

java lib 中主要写处理器和注解,androdi lib 中写反射。处理器必须继承 AbstractProcessor ,而 AbstractProcessor 属于 javax , 所以需要建立两种项目

步骤

java lib(注解和控制器)

结构

4e7fa7c60909b6bc64f5a26e0ff1f6c4.png

MyProcessor 核心处理类

ClassEntey 类的抽象类 (包括 路径、类名、属性集合)

AttrEntey 属性抽象类 (包括 属性名、属性值、属性类型)

处理器

注册

处理器的作用就是在编译期间执行我们想生成的代码,javac会自动寻找继承了 AbstractProcessor 的类

注册处理器(两种方式)

方式一

在main文件下创建(不推荐)

resources/META-INF/javax.annotation.processing.Processor在文件中写

格式:包名+“.”+类名

com.example.MyProcessor

复制代码

方式二

依赖

dependencies {

compile 'com.google.auto.service:auto-service:1.0-rc2'

}

复制代码

在继承了 AbstractProcessor 的类上面写

@AutoService(Processor.class)

public class MyProcessor extends AbstractProcessor

复制代码

实现

创建注解

@Target(ElementType.FIELD)

@Retention(RetentionPolicy.CLASS)

public @interface BindView {

int value();

}

复制代码

在继承 AbstractProcessor 的类中,实现如下三个方法

方法名

作用getSupportedAnnotationTypes()

需要处理的那些注解

getSupportedSourceVersion()

限制版本

process()

核心函数,在这个里面做处理

主要介绍process()方法

当javac 查询到所有在getSupportedAnnotationTypes()里面注册过的注解后,才会调用process()方法

process()方法有两个参数

public boolean process(Set annotations, RoundEnvironment roundEnv)

复制代码

着重介绍第二个,第二个参数代表所有和这个注解有关信息的集合,如下

例子

所在路径

cxy.com.myviewbind.Main2Activity

复制代码

@BindView(R.id.button)

Button button ;

复制代码

方法名

含义roundEnv.getSimpleName()

button

roundEnv.getEnclosingElement()

cxy.com.myviewbind.Main2Activity

roundEnv.getAnnotation()

R.id.button 所对应的整数值

roundEnv.asType()

android.widget.Button

有这些就可以生成我们需要的类了

我们看一下process()中都做了什么

@Override

public boolean process(Set annotations, RoundEnvironment roundEnv) {

// 以activity名为key,类的抽象类为value

Map map = new LinkedHashMap<>();

//判断是否为BindView 注解

for (Element element : roundEnv.getElementsAnnotatedWith(BindView.class)) {

//判断是否为变量

if (element.getKind() == ElementKind.FIELD) {

String className = element.getEnclosingElement().toString();

//判断当前界面在map结合中是否存在,不存在则添加

ClassEntey classEntey = map.get(className);

if (classEntey == null) {

String parkageNmae = className.substring(0, className.toString().lastIndexOf("."));

String classSimpleName = className.substring(

className.lastIndexOf(".") + 1, className.length()

) + "_BindView";

classEntey = ClassEntey.create(classSimpleName, parkageNmae);

map.put(element.getEnclosingElement().toString(), classEntey);

}

//activity可能存在多个控件属性,创建属性抽象类并赋值,最后添加进当前界面为key的抽象类中

AttrEntey attrEntey = AttrEntey.create(

element.getSimpleName().toString(),

element.asType().toString().substring(

element.asType().toString().lastIndexOf(".") + 1, element.asType().toString().length()

),

element.getAnnotation(BindView.class).value()

);

classEntey.addAttr(attrEntey, processingEnv);

}

}

//以上,所有的属性已经获取获取

//以下,把所有的数据写成文件

//

for (Map.Entry entry : map.entrySet()) {

try {

List arrts = entry.getValue().getArrts();

Set setImp = new HashSet<>();

//循环获取出所有需要导入的包

for (AttrEntey entey : arrts) {

setImp.add(entey.getType());

}

//写文件

createJava(entry.getValue(), setImp);

} catch (Exception e) {

e.printStackTrace();

}

}

return false;

}

复制代码

createJava为写文件的方法,为了更容易读懂,没有用javapoet,使用拼接字符串的方式完成写文件操作

createJava

private void createJava(ClassEntey entry, Set setImp) throws Exception {

JavaFileObject filerClassFile = filer.createSourceFile(entry.getParkageName() + "." + entry.getClassSimpleName(), new Element[]{});

Writer writer = filerClassFile.openWriter();

PrintWriter pw = new PrintWriter(writer);

···

pw.flush();

writer.close();

}

复制代码

简单说一下 filer :是写文件用的,在init方法中获取

android lib(反射生成的文件)

依赖刚才的java lib

没什么难度,不说了

android app(项目文件)

使用方式和butterknife 一样

public class MainActivity extends AppCompatActivity {

@BindView(R.id.edittext)

EditText edittext;

@BindView(R.id.button)

Button button;

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

MyBindView.bind(this);

edittext.setText("界面1");

button.setText("跳转至第二个界面");

button.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View view) {

startActivity(new Intent(MainActivity.this, Main2Activity.class));

}

});

}

}

复制代码

-END-

哪里写的不清晰可以给我留言,我会第一时间修改

参考文档

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值