Android(安卓)Butterknife 框架源码解析(1)——ButterKnife的使用
ButterKnife是目前常用的一种依托Java注解机制实现辅助代码生成的框架,有了它,妈妈再也不用担心我写大量枯燥的findViewById以及OnXXXListener响应事件了,一行代码就搞定,自从接触它以后我就再也离不开它了。既然如此,我也就抽个时间,好好研究了一下它,总结一下它的使用方法和原理。
配置编译环境
因为ButterKnife用到了注解处理器,所以比起一般的框架多了一点配置,如下所示:
在Project的build.gradle里添加如下配置(以8.1.0版本为例子,目前最新的可以访问https://github.com/JakeWharton/butterknife获取):
buildscript { ... dependencies { classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8' }}
在Module的build.gradle里添加如下配置:
apply plugin: 'com.neenbedankt.android-apt'android { ...}dependencies { compile 'com.jakewharton:butterknife:8.1.0' apt 'com.jakewharton:butterknife-compiler:8.1.0'}
Sync一下,我们就可以看到它了
基本使用
传统的写法,一个控件相关可以拆分为这样几部分:
1.Button btn;
2.btn = (Button) findViewById(R.id.btn);
3.btn.setOnClickListener(this);
4.
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn:
break;
}
}
比如一个简单的例子就是:
public class MainActivity extends AppCompatActivity { private Button button; private TextView textView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); button=(Button)findViewById(R.id.button); textView=(TextView)findViewById(R.id.textView); button.setOnClickListener(new View.OnClickListener() { @Override public voidonClick(View v) { textView.setText("我被点击了一下"); } }); }}
但实际上我们对这个控件关心的只有两个部分:从哪来来,干什么。于是我们的ButterKnife就将传统的写法中1和2进行合并,3和4进行合并,用这样的方式来解决这两个问题。于是同样的代码用ButterKnife来写就是这样的:
public class MainActivity extends AppCompatActivity { @BindView(R.id.button) Button button; @BindView(R.id.textView) TextView textView; @OnClick(R.id.button) public void click(){ textView.setText("我被点击了一下"); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_knife ); ButterKnife.bind(this); }}
也就是用了@BindView执行了控件的绑定,解决了从哪来的问题,用@OnClick执行了控件监听器的绑定,也就是做什么的问题。先解决这两个问题,最后再通过一句ButteKnife.bind就将控件和Activity关联在一起了。
这里需要注意的是被ButterKnife修饰的方法和控件都不能是私有的和静态的,否则会报错,比如:
@BindView(R.id.button)private Button button;@BindView(R.id.textView)private TextView textView;@OnClick(R.id.button)private void click(){ textView.setText("我被点击了一下");}
就会提示:
有人也许就会问,这样写法并没简单多少。其实ButterKnife主要是将控件相关的逻辑上简化了,代码清晰可读性强,这只个很简单的例子,当控件很多、结构比较复杂的时候(比如ViewHolder里),ButterKnife就能更发挥它的优势。
常见使用:
我们可以看到ButterKnife是通过注解来控件和监听器的绑定的,所以我们先从ButterKnife里的annotations包里看看ButterKnife里包含了多少种注解:
看上半部分可以知道ButterKnife里除了view控件的绑定,也有一些resource资源的绑定,也就是可以通过@BindXXX的注解实现res文件夹里的资源绑定;下半部分看出ButterKnife将常用的监听事件也封装成了注解。
总结一下将常见使用分为三个部分:
1.控件的绑定
View的绑定,使用方法为:
@BindView(R.id.button)private Button button;@BindView(R.id.textView)private TextView textView;
Resource的绑定,使用方法为:
@BindView(R.id.button)private Button button;@BindView(R.id.textView)private TextView textView;
2.响应事件的绑定
单事件的绑定:
可以一个控件制定一个事件回调
/** * 带参数 */@OnClick(R.id.button)public void onButterKnifeBtnClick() {}/** * 带参数 */@OnClick(R.id.button)public void onButterKnifeBtnClick(View view) {}/** * 带参数 * @parambutton */@OnClick(R.id.button)public void onButterKnifeBtnClick(Button button) {}
也可以多个控件指定一个事件回调:
/** * 两个不同的button都相应onButterKnifeBtnClick事件回调 * * @parambutton */@OnClick({R.id.button,R.id.button1})public void onButterKnifeBtnClick(Button button) {}
多事件的回调:
有一些View的listener是有多个回调方法的,比如EditText添加addTextChangedListener:
editText.addTextChangedListener(newTextWatcher() { @Override public voidbeforeTextChanged(CharSequence s, intstart, intcount, intafter) { } @Override public voidonTextChanged(CharSequence s, intstart, intbefore, intcount) { } @Override public voidafterTextChanged(Editable s) { }});
可以用注解写为:
@OnTextChanged(value = R.id.nameEditText,callback= OnTextChanged.Callback.BEFORE_TEXT_CHANGED)void beforeTextChanged(CharSequence s, intstart, intcount, intafter) {}@OnTextChanged(value = R.id.nameEditText,callback= OnTextChanged.Callback.TEXT_CHANGED)void onTextChanged(CharSequence s, intstart, intbefore, intcount) {}@OnTextChanged(value = R.id.nameEditText,callback= OnTextChanged.Callback.AFTER_TEXT_CHANGED)void afterTextChanged(Editable s) {}
选择性绑定:
默认情况下,@Bind 和listener的绑定都是必须的,如果target view没有被发现,则会报错.为了抑制这种行为,可以用@Optional注解来标记field和方法,让绑定变成选择性的,如果targetView存在,则绑定,不存在,则什么事情都不做.或者使用 Android's "support-annotations" library.中的@Nullable来修饰。
@Nullable@BindView(R.id.might_not_be_there)TextView mightNotBeThere;@Optional@OnClick(R.id.maybe_missing)void onMaybeMissingClicked() { //TODO ...}
3.View的绑定和解绑
写好了控件和响应事件绑定,我们需要在布局初始化以后将它和我们的ButterKnife用ButterKnife.bind()方法绑定起来。这里我们可以看看ButterKnife.bind()方法的源码为:
/** * BindView annotated fields and methods in the specified {@link Activity}. The current content * view is used as the view root. * * @param target Target activity for view binding. */public static Unbinder bind(@NonNull Activity target) { return getViewBinder(target).bind(Finder.ACTIVITY, target, target);}/** * BindView annotated fields and methods in the specified {@link View}. The view and its children * are used as the view root. * * @param target Target view for view binding. */@NonNullpublic static Unbinder bind(@NonNull View target) { return getViewBinder(target).bind(Finder.VIEW, target, target);}/** * BindView annotated fields and methods in the specified {@link Dialog}. The current content * view is used as the view root. * * @param target Target dialog for view binding. */@SuppressWarnings("unused") // Public api.public static Unbinder bind(@NonNull Dialog target) { return getViewBinder(target).bind(Finder.DIALOG, target, target);}/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link Activity} as the view root. * * @param target Target class for view binding. * @param source Activity on which IDs will be looked up. */public static Unbinder bind(@NonNull Object target, @NonNull Activity source) { return getViewBinder(target).bind(Finder.ACTIVITY, target, source);}/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link View} as the view root. * * @param target Target class for view binding. * @param source View root on which IDs will be looked up. */@NonNullpublic static Unbinder bind(@NonNull Object target, @NonNull View source) { return getViewBinder(target).bind(Finder.VIEW, target, source);}/** * BindView annotated fields and methods in the specified {@code target} using the {@code source} * {@link Dialog} as the view root. * * @param target Target class for view binding. * @param source Dialog on which IDs will be looked up. */@SuppressWarnings("unused") // Public api.public static Unbinder bind(@NonNull Object target, @NonNull Dialog source) { return getViewBinder(target).bind(Finder.DIALOG, target, source);}
可以看出源码中能和我们布局绑定的来源一共有Activity,View和Dialog三种,值得注意的是,在这些来源生命周期结束时候需要执行ButterKnife.unbind()方法来将绑定的变量变为null,从而释放内存。
以上就是ButterKnife的基本用法。
参考文章:
http://www.cnblogs.com/whoislcj/p/5620128.html
http://www.jianshu.com/p/9ad21e548b69
https://www.zhihu.com/question/38742543?sort=created
更多相关文章
- 边做iOS边学Android(二):认识几个常用的控件
- 一起来学习Android自定义控件1
- Android中将控件放到线性布局的任意位置(一)
- android 绑定sqlite数据库与程序一起发布
- Gallery控件初体验——简单的相册
- Android(安卓)自定义标尺控件(选择身高、体重等)
- Android(安卓)自定义控件——自定义属性
- 荐 android 如何打包自定义控件(转)
- Android性能优化之路(二)