從這次實戰(zhàn)我能學(xué)會什么
實戰(zhàn)要實現(xiàn)的功能
完全仿寫B(tài)utterKnife的bindView注解原理鹤竭,實現(xiàn)在Activity中控件id賦值功能
實戰(zhàn)看點
這次實戰(zhàn)是從源碼出發(fā)猖闪,仿照ButterKnife的源碼實現(xiàn)其中的BindView的功能宏侍,實際上就是把ButterKnife的BindView的邏輯剝離出來單獨實現(xiàn)徽千,代碼量少了許多但與源碼差別不大爱榔,達到了練習(xí)注解的目的挟伙。所以我們學(xué)會了這個,你會發(fā)現(xiàn)荐开,看ButterKnife的源碼突然變得簡單了
實戰(zhàn)基礎(chǔ)
了解并能運用AutoService,JavaPoet,AbstractProcessor
不熟悉的付翁,請先參考我之前的文章:安卓使用注解處理器自動生成代碼操作詳解(AutoService,JavaPoet,AbstractProcessor)
下面我們就來通過實戰(zhàn)探究ButterKnife的注解奧秘
ButterKnife原理的簡單介紹
既然要仿寫,必然要先對仿寫對象的實現(xiàn)原理要有一些了解
這是它的簡單使用:
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
public TextView textView1;
private Unbinder unbinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
unbinder = ButterKnife.bind(this);
//試著運行一下吧
textView1.setText("這是一個賦值測試");
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
}
運行后我們會發(fā)現(xiàn)生成了一個MainActivity_ViewBinding
文件晃听,結(jié)合我上篇文章我們知道這是使用了注解處理器:
// Generated code from Butter Knife. Do not modify!
package com.jay.bindview;
import android.view.View;
import android.widget.TextView;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import butterknife.Unbinder;
import butterknife.internal.Utils;
import java.lang.IllegalStateException;
import java.lang.Override;
public class MainActivity_ViewBinding implements Unbinder {
private MainActivity target;
@UiThread
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
@UiThread
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
target.textView1 = Utils.findRequiredViewAsType(source, R.id.tv1, "field 'textView1'", TextView.class);
}
@Override
@CallSuper
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.textView1 = null;
}
}
欸百侧,是不是執(zhí)行了MainActivity_ViewBinding
的構(gòu)造方法就完成了控件賦值了,那么這個方法在哪執(zhí)行的呢杂伟,看ButterKnife.bind(this)
方法移层,最終會到這:
@NonNull @UiThread
public static Unbinder bind(@NonNull Object target, @NonNull View source) {
Class<?> targetClass = target.getClass();
if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());
Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass);
if (constructor == null) {
return Unbinder.EMPTY;
}
//noinspection TryWithIdenticalCatches Resolves to API 19+ only type.
try {
return constructor.newInstance(target, source);
} catch (IllegalAccessException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InstantiationException e) {
throw new RuntimeException("Unable to invoke " + constructor, e);
} catch (InvocationTargetException e) {
Throwable cause = e.getCause();
if (cause instanceof RuntimeException) {
throw (RuntimeException) cause;
}
if (cause instanceof Error) {
throw (Error) cause;
}
throw new RuntimeException("Unable to create binding instance.", cause);
}
}
注意constructor.newInstance(target, source);
這行代碼仍翰,這是通過反射獲取到類然后執(zhí)行這個類的構(gòu)造方法赫粥,這樣一分析,實現(xiàn)流程是不是一目了然了予借,先通過BindView注解獲取到控件的id,控件的類型和控件父類的名稱(用于生成特定的類文件名和控件賦值)越平,然后生成代碼,最后通過反射執(zhí)行生成的類的構(gòu)造方法灵迫,是不是就實現(xiàn)了我們想要的功能秦叛。
下面開始擼碼
BindView代碼實現(xiàn)
既然是仿,我們仿的像一點瀑粥,先新建一個項目挣跋,創(chuàng)建3個library:
- bindview-annotation 注意是java library,用于存放注解
- bindview-compiler 注意還是java library狞换,用于處理注解避咆,生成代碼
- bindviewlib 安卓library,用于反射調(diào)用生成的代碼
bindviewlib 和bindview-compiler都需要依賴bindview-annotation
項目結(jié)構(gòu)如圖所示:
我們先在bindview-annotation中創(chuàng)建一個BindView注解:
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface BindView {
@IdRes int value();
}
隨后,我們就按照源碼在bindview-compiler
中新建4個類:
- BindingSet 代碼統(tǒng)籌管理類修噪,處理代碼生成邏輯
- FieldViewBinding 用來保存字段的信息
- ID 用來保存id信息
- ViewBinding 用來保存FieldViewBinding 和 ID的實例查库,方便管理和緩存
先看FieldViewBinding
,就簡單的保存了兩個字段信息,TypeName
是字段的類型,相當于我的生成的代碼里的TextView
,而name
相當于我們的代碼里的textview1
final class FieldViewBinding {
//字段名稱
private final String name;
//字段類型
private final TypeName type;
FieldViewBinding(String name,TypeName type){
this.name = name;
this.type = type;
}
public String getName() {
return name;
}
public TypeName getType() {
return type;
}
public ClassName getRawType() {
if (type instanceof ParameterizedTypeName) {
return ((ParameterizedTypeName) type).rawType;
}
return (ClassName) type;
}
}
再看ID
類,value
就是我們從注解中獲取到的id,傳入到CodeBlock
中方便生成代碼
final class ID {
/**
* value及注解中的value id
*/
final CodeBlock code;
ID(int value){
this.code = CodeBlock.of("$L", value);
}
}
再來看一下ViewBinding
類,仿照源碼用了構(gòu)建者模式黄琼,保存了ID
和FieldViewBinding
的值:
final class ViewBinding {
private final ID id;
@Nullable
private final FieldViewBinding fieldBinding;
private ViewBinding(ID id, @Nullable FieldViewBinding fieldBinding) {
this.id = id;
this.fieldBinding = fieldBinding;
}
public ID getId() {
return id;
}
@Nullable
public FieldViewBinding getFieldBinding() {
return fieldBinding;
}
static final class Builder {
private final ID id;
@Nullable
private FieldViewBinding fieldBinding;
Builder(ID id) {
this.id = id;
}
public void setFieldBinding(FieldViewBinding fieldBinding) {
if (this.fieldBinding != null) {
throw new AssertionError();
}
this.fieldBinding = fieldBinding;
}
public ViewBinding build() {
return new ViewBinding(id, fieldBinding);
}
}
}
最后就是我們的核心類BindingSet
了樊销,看看是怎么來創(chuàng)建代碼的吧:
final class BindingSet{
private final TypeName targetTypeName; //示例值 MainActivity
private final ClassName bindingClassName; //示例值 MainActivity_ViewBinding
private final TypeElement enclosingElement; //這是注解元素的父類Element,用于獲取父類元素
private final ImmutableList<ViewBinding> viewBindings; //保存了每一個字段的元素
private BindingSet(
TypeName targetTypeName, ClassName bindingClassName, TypeElement enclosingElement,
ImmutableList<ViewBinding> viewBindings) {
this.targetTypeName = targetTypeName;
this.bindingClassName = bindingClassName;
this.enclosingElement = enclosingElement;
this.viewBindings = viewBindings;
}
/**
* 從這個方法開始構(gòu)建代碼,這里只實現(xiàn)BindView的代碼邏輯
*
* @return JavaFile
*/
JavaFile brewJava() {
TypeSpec bindingConfiguration = createType();
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
private TypeSpec createType() {
//第一步 先創(chuàng)建類
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(Modifier.PUBLIC)
.addOriginatingElement(enclosingElement); //設(shè)置注解處理器的源元素
//添加解綁接口
result.addSuperinterface(ClassName.get("com.jay.bindviewlib", "Unbinder"));
//添加activity字段target
result.addField(targetTypeName, "target");
//添加構(gòu)造方法
result.addMethod(createBindingConstructorForActivity());
//添加找id的方法
result.addMethod(createBindingConstructor());
//添加解綁的方法
result.addMethod(createBindingUnbindMethod());
return result.build();
}
/**
* 示例:MainActivity_BindView(MainActivity target){
* this(target, target.getWindow().getDecorView())
* }
*
* @return MethodSpec
*/
private MethodSpec createBindingConstructorForActivity() {
MethodSpec.Builder builder = MethodSpec.constructorBuilder()
.addModifiers(PUBLIC)
.addParameter(targetTypeName, "target");
builder.addStatement("this(target, target.getWindow().getDecorView())");
return builder.build();
}
private static final ClassName VIEW = ClassName.get("android.view", "View");
/**
* 創(chuàng)建構(gòu)造方法,這個方法里包含找id的代碼
*
* @return MethodSpec
*/
private MethodSpec createBindingConstructor() {
MethodSpec.Builder constructor = MethodSpec.constructorBuilder()
.addModifiers(PUBLIC);
constructor.addParameter(targetTypeName, "target");
constructor.addParameter(VIEW, "source");
constructor.addStatement("this.target = target");
constructor.addCode("\n");
//這里循環(huán)創(chuàng)建控件賦值代碼
for (ViewBinding binding : viewBindings) {
addViewBinding(constructor, binding);
}
return constructor.build();
}
//創(chuàng)建一條賦值代碼
//示例:target.textview1 = (TextView)source.findViewById(id)
//這里的source = target.getWindow().getDecorView() target是Activity
private void addViewBinding(MethodSpec.Builder result, ViewBinding binding) {
FieldViewBinding fieldBinding = requireNonNull(binding.getFieldBinding());
CodeBlock.Builder builder = CodeBlock.builder()
.add("target.$L = ", fieldBinding.getName()); //添加代碼 target.textview1 =
builder.add("($T) ", fieldBinding.getType()); //添加強轉(zhuǎn)代碼
builder.add("source.findViewById($L)", binding.getId().code); //找id
result.addStatement("$L", builder.build()); //將代碼添加到方法中
}
/**
* 創(chuàng)建解綁的方法
*
* @return MethodSpec
*/
private MethodSpec createBindingUnbindMethod() {
MethodSpec.Builder result = MethodSpec.methodBuilder("unbind")
.addAnnotation(Override.class)
.addModifiers(PUBLIC);
result.addStatement("$T target = this.target", targetTypeName);
result.addStatement("if (target == null) throw new $T($S)", IllegalStateException.class,
"Bindings already cleared.");
result.addStatement("$N = null","this.target");
result.addCode("\n");
for (ViewBinding binding : viewBindings) {
if (binding.getFieldBinding() != null) {
result.addStatement("target.$L = null", binding.getFieldBinding().getName());
}
}
return result.build();
}
/**
* 生成代碼生成的類的類名
* @return Name 規(guī)則 ActivityName__ViewBinding
*/
static ClassName getBindingClassName(TypeElement typeElement) {
String packageName = getPackage(typeElement).getQualifiedName().toString();
String className = typeElement.getQualifiedName().toString().substring(
packageName.length() + 1).replace('.', '$');
return ClassName.get(packageName, className + "_ViewBinding");
}
/**
* 創(chuàng)建一個Builder
* @param enclosingElement 父類元素围苫,也就是那個Activity
* @return 這里生成了類名稱與類target
*/
static Builder newBuilder(TypeElement enclosingElement) {
TypeMirror typeMirror = enclosingElement.asType();
TypeName targetType = TypeName.get(typeMirror);
if (targetType instanceof ParameterizedTypeName) {
targetType = ((ParameterizedTypeName) targetType).rawType;
}
ClassName bindingClassName = getBindingClassName(enclosingElement);
return new Builder(targetType, bindingClassName, enclosingElement);
}
static final class Builder {
private final TypeName targetTypeName;
private final ClassName bindingClassName;
private final TypeElement enclosingElement;
//緩存ViewBinding實例裤园,提升性能
private final Map<ID, ViewBinding.Builder> viewIdMap = new LinkedHashMap<>();
private Builder(
TypeName targetTypeName, ClassName bindingClassName, TypeElement enclosingElement) {
this.targetTypeName = targetTypeName;
this.bindingClassName = bindingClassName;
this.enclosingElement = enclosingElement;
}
void addField(ID id, FieldViewBinding binding) {
getOrCreateViewBindings(id).setFieldBinding(binding);
}
private ViewBinding.Builder getOrCreateViewBindings(ID id) {
ViewBinding.Builder viewId = viewIdMap.get(id);
if (viewId == null) {
viewId = new ViewBinding.Builder(id);
viewIdMap.put(id, viewId);
}
return viewId;
}
BindingSet build() {
ImmutableList.Builder<ViewBinding> viewBindings = ImmutableList.builder();
for (ViewBinding.Builder builder : viewIdMap.values()) {
viewBindings.add(builder.build());
}
return new BindingSet(targetTypeName, bindingClassName, enclosingElement, viewBindings.build());
}
}
}
這個類完全仿照源碼編寫,只保留了Activity
的賦值邏輯剂府,先來看用到的四個參數(shù)的作用:
-這個是控件的父類的類型名稱比然,用于生成target的值
private final TypeName targetTypeName;
-這個是生成的文件名稱
private final ClassName bindingClassName;
-這個是注解的父注解元素
private final TypeElement enclosingElement;
-這個就是我們的字段信息的緩存集合了
private final ImmutableList<ViewBinding> viewBindings;
我們得先獲取到這4個參數(shù)的值,這是一個構(gòu)建者模式周循,構(gòu)建者的賦值邏輯在newBuilder
方法中:
/**
* 創(chuàng)建一個Builder
* @param enclosingElement 父類元素强法,也就是那個Activity
* @return 這里生成了類名稱與類target
*/
static Builder newBuilder(TypeElement enclosingElement) {
TypeMirror typeMirror = enclosingElement.asType();
TypeName targetType = TypeName.get(typeMirror);
if (targetType instanceof ParameterizedTypeName) {
targetType = ((ParameterizedTypeName) targetType).rawType;
}
ClassName bindingClassName = getBindingClassName(enclosingElement);
return new Builder(targetType, bindingClassName, enclosingElement);
}
看一下getBindingClassName
方法是如何獲取到名稱的:
/**
* 生成代碼生成的類的類名
* @return Name 規(guī)則 ActivityName__ViewBinding
*/
static ClassName getBindingClassName(TypeElement typeElement) {
String packageName = getPackage(typeElement).getQualifiedName().toString();
String className = typeElement.getQualifiedName().toString().substring(
packageName.length() + 1).replace('.', '$');
return ClassName.get(packageName, className + "_ViewBinding");
}
可以看到是通過父元素的getQualifiedName
方法獲取到標準格式的類名后截取的,隨后手動添加了_ViewBinding后綴湾笛。
我們還有一個viewBindings
沒有賦值饮怯,這個值需要從注解器里去拿到,這個隨后再說嚎研,下面我們看代碼生成邏輯蓖墅,入口是brewJava
方法:
JavaFile brewJava() {
TypeSpec bindingConfiguration = createType();
return JavaFile.builder(bindingClassName.packageName(), bindingConfiguration)
.addFileComment("Generated code from Butter Knife. Do not modify!")
.build();
}
可以看到通過createType
獲取到TypeSpec
就直接生成JavaFile
了,看一下createType
方法做了什么:
private TypeSpec createType() {
//第一步 先創(chuàng)建類
TypeSpec.Builder result = TypeSpec.classBuilder(bindingClassName.simpleName())
.addModifiers(Modifier.PUBLIC)
.addOriginatingElement(enclosingElement); //設(shè)置注解處理器的源元素
//添加解綁接口
result.addSuperinterface(ClassName.get("com.jay.bindviewlib", "Unbinder"));
//添加activity字段target
result.addField(targetTypeName, "target");
//添加構(gòu)造方法
result.addMethod(createBindingConstructorForActivity());
//添加找id的方法
result.addMethod(createBindingConstructor());
//添加解綁的方法
result.addMethod(createBindingUnbindMethod());
return result.build();
}
通過TypeSpec.Builder
依次添加各個部分的代碼临扮,邏輯還是比較清晰的论矾,需要注意的是,添加Unbinder
類傳入包名的時候要填寫正確的路徑哦杆勇,不要直接把我的包名復(fù)制進去了贪壳。看不太懂的結(jié)合生成的代碼比較著看蚜退,相信很容易就能看懂闰靴,這里與源碼是有差別的,源碼中是使用的Utils
來尋找id,我這里為了方便直接生成了findviewbyid
的代碼钻注,注意區(qū)別!!
代碼生成的邏輯寫好了蚂且,接下來就到了我們的注解處理器上了,我們的BindingSet
需要從處理器中獲取到字段信息和控件的父元素信息才能創(chuàng)建代碼對吧幅恋,接下來請看:
我們新建一個類名叫BindViewProcessor
:
@AutoService(Processor.class)
public class BindViewProcessor extends AbstractProcessor {
private Filer mFiler;
@Override
public synchronized void init(ProcessingEnvironment processingEnvironment) {
super.init(processingEnvironment);
//我們可以從這里獲取一些工具類
mFiler = processingEnvironment.getFiler();
}
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//緩存BindingSet并給BindingSet賦值
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(roundEnvironment);
//第二步杏死,循環(huán)獲取BindingSet并執(zhí)行brewJava開始繪制代碼
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
/**
* 給BindingSet賦值并生成一個map
* @param env 當前元素環(huán)境
* @return 元素集合
*/
private Map<TypeElement, BindingSet> findAndParseTargets(RoundEnvironment env) {
Map<TypeElement, BindingSet.Builder> builderMap = new LinkedHashMap<>();
Set<TypeElement> erasedTargetNames = new LinkedHashSet<>();
//這里循環(huán)生成了BindingSet.Builder并將值放入了builderMap中
Set<? extends Element> envs = env.getElementsAnnotatedWith(BindView.class);
for (Element element : envs) {
try {
parseBindView(element, builderMap);
} catch (Exception e) {
e.printStackTrace();
}
}
//從builderMap中取出值并生成BindingSet放入bindingMap中,源碼是用的while捆交,并有處理父類的super邏輯淑翼,這里直接用for
Map<TypeElement, BindingSet> bindingMap = new LinkedHashMap<>();
for (Map.Entry<TypeElement, BindingSet.Builder> entry:builderMap.entrySet()) {
TypeElement type = entry.getKey();
BindingSet.Builder builder = entry.getValue();
bindingMap.put(type, builder.build());
}
return bindingMap;
}
/**
* 為BindingSet賦值,從Element元素中獲取Activity與控件信息零渐,并保存到BindingSet中
*/
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap) {
//獲取父類的Element
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
TypeMirror elementType = element.asType();
if (elementType.getKind() == TypeKind.TYPEVAR) {
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
Name qualifiedName = enclosingElement.getQualifiedName();
Name simpleName = element.getSimpleName();
int id = element.getAnnotation(BindView.class).value();
BindingSet.Builder builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
builder.addField(new ID(id), new FieldViewBinding(name, type));
}
/**
* 創(chuàng)建BindingSet 并且將BindingSet緩存到builderMap中
*/
private BindingSet.Builder getOrCreateBindingBuilder(
Map<TypeElement, BindingSet.Builder> builderMap, TypeElement enclosingElement) {
BindingSet.Builder builder = builderMap.get(enclosingElement);
if (builder == null) {
builder = BindingSet.newBuilder(enclosingElement);
builderMap.put(enclosingElement, builder);
}
return builder;
}
@Override
public Set<String> getSupportedAnnotationTypes() {
Set<String> types = new LinkedHashSet<>();
types.add(BindView.class.getCanonicalName()); //將我們自定義的注解添加進去
return types;
}
@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}
}
其他3個方法基本都是樣板代碼窒舟,著重看process
方法,首先是通過findAndParseTargets
方法獲取到BindingSet
的緩存诵盼,BindingSe
t的賦值邏輯在parseBindView
方法中:
/**
* 為BindingSet賦值惠豺,從Element元素中獲取Activity與控件信息银还,并保存到BindingSet中
*/
private void parseBindView(Element element, Map<TypeElement, BindingSet.Builder> builderMap) {
//獲取父類的Element
TypeElement enclosingElement = (TypeElement) element.getEnclosingElement();
TypeMirror elementType = element.asType();
if (elementType.getKind() == TypeKind.TYPEVAR) {
TypeVariable typeVariable = (TypeVariable) elementType;
elementType = typeVariable.getUpperBound();
}
Name qualifiedName = enclosingElement.getQualifiedName();
Name simpleName = element.getSimpleName();
int id = element.getAnnotation(BindView.class).value();
BindingSet.Builder builder = getOrCreateBindingBuilder(builderMap, enclosingElement);
String name = simpleName.toString();
TypeName type = TypeName.get(elementType);
builder.addField(new ID(id), new FieldViewBinding(name, type));
}
我們通過element.getEnclosingElement();
方法就能獲取到控件的父元素,這是BindingSet
需要的值得其中之一洁墙,隨后通過element.getAnnotation(BindView.class).value();
獲取到id
并將它保存到ID
類中蛹疯,隨后通過getSimpleName
獲取到控件的名稱,也就是我們生成的代碼的那個textview1
名稱热监,控件類型捺弦,也就是我們的那個TextView
,可以通過element.asType()
先獲取到控件的信息類TypeMirror
孝扛,隨后通過TypeName.get
方法獲取到TypeName
的實例列吼,知道了TypeName
,我們就相當于在代碼中持有了這個類的實例苦始,我們就能直接把它作為參數(shù)傳入到JavaPoet方法構(gòu)建中去了寞钥,最后我們通過builder.addField(new ID(id), new FieldViewBinding(name, type));
方法傳入ID
和FieldViewBinding
類,保存到了viewBindings
中,需要的值也就都賦值完畢了陌选。
既然值都獲取到了理郑,我們回到process
方法,我們已經(jīng)獲取到了所有用BindView
標記的控件的一個集合咨油,接下來當然是循環(huán)調(diào)用brewJava
方法構(gòu)建代碼啦:
@Override
public boolean process(Set<? extends TypeElement> set, RoundEnvironment roundEnvironment) {
//緩存BindingSet并給BindingSet賦值
Map<TypeElement, BindingSet> bindingMap = findAndParseTargets(roundEnvironment);
//第二步您炉,循環(huán)獲取BindingSet并執(zhí)行brewJava開始繪制代碼
for (Map.Entry<TypeElement, BindingSet> entry : bindingMap.entrySet()) {
TypeElement typeElement = entry.getKey();
BindingSet binding = entry.getValue();
JavaFile javaFile = binding.brewJava();
try {
javaFile.writeTo(mFiler);
} catch (IOException e) {
e.printStackTrace();
}
}
return false;
}
核心代碼已經(jīng)寫好了,接下來就剩下調(diào)用方法啦役电,我們在bindviewlib
中新建一個類BindViewHelper
,我們可以直接將ButterKnife
類的代碼搬過來赚爵,偷一波懶,最后宴霸,我們在申明一個解綁的接口:
public interface Unbinder {
@UiThread
void unbind();
Unbinder EMPTY = () -> { };
}
整個代碼就寫完了囱晴,接下來就在Activity中實驗一下吧膏蚓,我們在app的build.gradle中引入這兩個包:
implementation project(':bindviewlib')
annotationProcessor project(':bindview-compiler')
在代碼中使用:
/**
* 自動生成的代碼位于build>generated>source>apt目錄下
*/
public class MainActivity extends AppCompatActivity {
@BindView(R.id.tv1)
public TextView textView1;
@BindView(R.id.tv2)
public TextView textView2;
private Unbinder unbinder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
unbinder = BindViewHelper.bind(this);
//試著運行一下吧
textView1.setText("這是一個賦值測試");
}
@Override
protected void onDestroy() {
super.onDestroy();
unbinder.unbind();
}
}
運行一下瓢谢,我們在build>generated>source>apt
目錄下發(fā)現(xiàn)成功生成了文件MainActivity_ViewBinding
// Generated code from Butter Knife. Do not modify!
package com.jay.bindview;
import android.view.View;
import android.widget.TextView;
import com.jay.bindviewlib.Unbinder;
import java.lang.IllegalStateException;
import java.lang.Override;
public class MainActivity_ViewBinding implements Unbinder {
MainActivity target;
public MainActivity_ViewBinding(MainActivity target) {
this(target, target.getWindow().getDecorView());
}
public MainActivity_ViewBinding(MainActivity target, View source) {
this.target = target;
target.textView1 = (TextView) source.findViewById(2131165312);
target.textView2 = (TextView) source.findViewById(2131165313);
}
@Override
public void unbind() {
MainActivity target = this.target;
if (target == null) throw new IllegalStateException("Bindings already cleared.");
this.target = null;
target.textView1 = null;
target.textView2 = null;
}
}
大功告成!!
結(jié)尾
先給個demo地址,方便讀者查閱代碼:https://github.com/Jay-huangjie/BindView
希望讀者能參照我的代碼重新寫一遍驮瞧,代碼并不復(fù)雜氓扛,相信對注解會有新的理解,然后你再去看ButterKnife的源碼论笔,會發(fā)現(xiàn)出奇的熟悉采郎,一下就看懂了,有任何的問題歡迎留言狂魔,關(guān)注我蒜埋,不迷路