ButterKnife 使用教程

ButterKnife 使用教程 (最新7.x版)
  安卓  开源工具      2015-11-07 12:30:20      318      0      0

注意:本文中大部分示例都是官网直接拿来的,本文就是官方翻译版加了很少量的说明。可以直接去官网看教程(因为他绝对是最新的,我这里可能更新到8.0或者有一些改动也没法及时更新),这个插件很简单,所以教程很详细了:看了一下和以前的版本(我以前用的是6.0.0的版本)没有特别多的新功能,不过方法名和注解名都有了改变,比以前清爽易懂,底层的实现有没有改不清楚,不过表面上看应该差不多。

官网及文档:http://jakewharton.github.io/butterknife/ 
GitHub地址:https://github.com/JakeWharton/butterknife 
我以前6.0版本的翻译稿:http://www.cnblogs.com/leo-lsw/p/butterknife.html


本文地址:http://blog.leoray.me/post/butterknife


加入到项目的方法

把这块放到最前是有原因的,下面是方法: 
MAVEN项目:(7.0.1是本文当前版本号)(在pom.xml文件中)

     
     
  1. <dependency>
  2. <groupId>com.jakewharton</groupId>
  3. <artifactId>butterknife</artifactId>
  4. <version>7.0.1</version>
  5. </dependency>

Gradle项目(现在Android应该都是Gradle了吧,在build.gradle文件中)

     
     
  1. compile 'com.jakewharton:butterknife:7.0.1'

另外,还需要下面两个配置:

     
     
  1. //支持lint warning 检查机制
  2. lintOptions {
  3. disable 'InvalidPackage'
  4. }
  5. //为什么加入这个呢?防止冲突,比如我同时用了dagger-compiler就会报错,说下面这个`Processor`重复了
  6. packagingOptions {
  7. exclude 'META-INF/services/javax.annotation.processing.Processor'
  8. }

这样加入了还没有完,我们还要在Proguard中加入下面这些代码(为什么呢?Proguard的原理大家如果懂的话就知道了,Butterknife的使用和生成的一些类都是动态的,而ProGuard这样的工具可能判定这些类没有被使用而移除他们,所以要在他的配置文件下面做下面的配置):

     
     
  1. -keep class butterknife.** { *; }
  2. -dontwarn butterknife.internal.**
  3. -keep class **$$ViewBinder { *; }
  4. -keepclasseswithmembernames class * {
  5. @butterknife.* <fields>;
  6. }
  7. -keepclasseswithmembernames class * {
  8. @butterknife.* <methods>;
  9. }

最简单的用法

最简单的肯定是自动关联View了,以前我们都是样板式的代码:

     
     
  1. private Button btn;
  2. @Override
  3. protected void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. btn=(Button)findViewById(R.id.btn);
  7. btn.setOnClickListener(new View.OnClickListener() {
  8. @Override
  9. public void onClick(View v) {
  10. Toast.makeText(MainActivity.this, "Btn Clicked", Toast.LENGTH_SHORT).show();
  11. }
  12. });
  13. }

后来有了ButterKnife就简单了,如下:

     
     
  1. class ExampleActivity extends Activity {
  2. @Bind(R.id.title) Button btn;
  3. @Override public void onCreate(Bundle savedInstanceState) {
  4. super.onCreate(savedInstanceState);
  5. setContentView(R.layout.activity_main);
  6. ButterKnife.bind(this);
  7. // TODO Use fields...
  8. }
  9. }

没有一堆的findViewById和强制转换是不是清爽多了呢? 
而且,即使这些代码,都可以通过Android Studio的插件Android ButterKnife Zelezny帮你完成了。

  1. ButterKnife可不是通过反射实现了,而是在编译的时候生成的代码,和我们自己写的其实原理一样,比如上面的Button的绑定的实现就类似下面的方法:

            
            
    1. public void bind(ExampleActivity activity) {
    2. activity.btn = (android.widget.Button) activity.findViewById(2130968578);
    3. }
  2. ButterKnife以前绑定的代码是@InjectView注解和inject方法,现在改了名字感觉更容易理解了,基本原理没变。

  3. 另外,大家关心@Bind注解对于绑定的成员变量有没有要求呢?其实是有的,如果你试着将它的限定符改为private,在编译的时候就会报错如下: 
            
            
    1. Error:(21, 20) 错误: @Bind fields must not be private or static. ...

也就是你的成员变量不能是private 或者static修饰了。

是不是和我们自己写的代码差不多呢?

更多的绑定

绑定资源

     
     
  1. class ExampleActivity extends Activity {
  2. @BindString(R.string.title) String title;
  3. @BindDrawable(R.drawable.graphic) Drawable graphic;
  4. @BindColor(R.color.red) int red; // int or ColorStateList field
  5. @BindDimen(R.dimen.spacer) Float spacer; // int (for pixel size) or float (for exact value) field
  6. // ...
  7. }
  1. 绑定其他的资源类似,应该不需要一一列举了吧
  2. 不过有时候资源不需要搞成成员变量吧?自己选择吧

非Activity的绑定

比如说Fragment中(得到View,然后bind方法传入这个View的实例):

     
     
  1. public class FancyFragment extends Fragment {
  2. @Bind(R.id.button1) Button button1;
  3. @Bind(R.id.button2) Button button2;
  4. @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  5. View view = inflater.inflate(R.layout.fancy_fragment, container, false);
  6. ButterKnife.bind(this, view);
  7. // TODO Use fields...
  8. return view;
  9. }
  10. }

在Adapter中的用法

经常会在Adapter中使用ViewHolder,其实你也可以在ViewHolder中使用ButterKnife: 
在ViewHolder的构造函数中调用bind,然后成员变量同样的使用@Bind注解。其实这几种绑定原理都一样,就是我们前面编译后的代码那样的方式。

     
     
  1. public class MyAdapter extends BaseAdapter {
  2. @Override public View getView(int position, View view, ViewGroup parent) {
  3. ViewHolder holder;
  4. if (view != null) {
  5. holder = (ViewHolder) view.getTag();
  6. } else {
  7. view = inflater.inflate(R.layout.whatever, parent, false);
  8. holder = new ViewHolder(view);
  9. view.setTag(holder);
  10. }
  11. holder.name.setText("John Doe");
  12. // etc...
  13. return view;
  14. }
  15. static class ViewHolder {
  16. @Bind(R.id.title) TextView name;
  17. @Bind(R.id.job_title) TextView jobTitle;
  18. public ViewHolder(View view) {
  19. ButterKnife.bind(this, view);
  20. }
  21. }
  22. }

ButterKnife.bind()还有其他的一些API,可以自己关注一下,直接看源码的注释很容易理解了。

View List批量操作

如果我们有一系列的View放到了一个List里面,就可以进行批量操作了:批量绑定,批量设置属性等。

     
     
  1. //批量绑定
  2. @Bind({ R.id.first_name, R.id.middle_name, R.id.last_name })
  3. List<EditText> nameViews;
     
     
  1. //批量设置
  2. ButterKnife.apply(nameViews, DISABLE);
  3. ButterKnife.apply(nameViews, ENABLED, false);

其中,DIABLE,ENABLED是我们定义的两个对象,一个是Action,负责执行操作,一个是Setter,负责将值设置为第三个参数:

     
     
  1. static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
  2. @Override public void apply(View view, int index) {
  3. view.setEnabled(false);
  4. }
  5. };
  6. static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() {
  7. @Override public void set(View view, Boolean value, int index) {
  8. view.setEnabled(value);
  9. }
  10. };

也就是第一个apply将所有的nameViews中的View对象设置为disabled,第二个apply将所有的对象的enable属性设置为false(第三个参数)

另外,我们可以直接对属性进行设置,而不需要编写ActionSetter,例如:

     
     
  1. ButterKnife.apply(nameViews, View.ALPHA, 0.0f);

绑定监听器

ButterKnife还有一个比较常用的功能就是类似@OnClick等的绑定监听器的方法,Android中需要大量的监听器监听用户的操作。示例如下:

     
     
  1. @OnClick(R.id.submit)
  2. public void submit(View view) {
  3. // TODO submit data to server...
  4. }

如果不需要绑定的对象,不写也可以:

     
     
  1. @OnClick(R.id.submit)
  2. public void submit() {
  3. // TODO submit data to server...
  4. }

而且,这里可以直接将绑定的实例转换成实际的对象:

     
     
  1. @OnClick(R.id.submit)
  2. public void sayHi(Button button) {
  3. button.setText("Hello!");
  4. }

另外,多个View也可以绑定到一个处理方法上:

     
     
  1. @OnClick({ R.id.door1, R.id.door2, R.id.door3 })
  2. public void pickDoor(DoorView door) {
  3. if (door.hasPrizeBehind()) {
  4. Toast.makeText(this, "You win!", LENGTH_SHORT).show();
  5. } else {
  6. Toast.makeText(this, "Try again", LENGTH_SHORT).show();
  7. }
  8. }

如果是自定义的View,可以直接绑定到他自己的处理方法上而不需要指定ID

     
     
  1. public class FancyButton extends Button {
  2. @OnClick
  3. public void onClick() {
  4. // TODO do something!
  5. }
  6. }

这样,用户点击FancyButton时就会触发该方法。

绑定重置

我们可能需要在Fragment销毁的时候将绑定的View全部设置为null,ButterKnife提供了一个unbind方法自动执行这个操作。

     
     
  1. public class FancyFragment extends Fragment {
  2. @Bind(R.id.button1) Button button1;
  3. @Bind(R.id.button2) Button button2;
  4. @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  5. View view = inflater.inflate(R.layout.fancy_fragment, container, false);
  6. ButterKnife.bind(this, view);
  7. // TODO Use fields...
  8. return view;
  9. }
  10. @Override public void onDestroyView() {
  11. super.onDestroyView();
  12. ButterKnife.unbind(this);
  13. }
  14. }

可选的绑定

如果你给一个绑定添加了一个@Nullable注解,即使对应的资源id不存在也不会报错。有时候我们不能保证这个id一定存在,或者有特殊的需求的时候可以使用

     
     
  1. @Nullable @Bind(R.id.might_not_be_there) TextView mightNotBeThere;
  2. @Nullable @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {
  3. // TODO ...
  4. }

多方法监听

例如ListView的设置onItemOnItemSelectedSelectedListener接口有两个回调方法,一个是onItemSelected,一个是onNothingSelected,这时候,我们可以设置两个注解来分别处理这两个回调方法:

     
     
  1. @OnItemSelected(R.id.list_view)
  2. void onItemSelected(int position) {
  3. // TODO ...
  4. }
  5. @OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)
  6. void onNothingSelected() {
  7. // TODO ...
  8. }

小工具

如果你不想用注解的方法绑定View但是又很讨厌强制转换,就可以用ButterKnife.findById(id)这样的方法:

     
     
  1. View view = LayoutInflater.from(context).inflate(R.layout.thing, null);
  2. TextView firstName = ButterKnife.findById(view, R.id.first_name);
  3. TextView lastName = ButterKnife.findById(view, R.id.last_name);
  4. ImageView photo = ButterKnife.findById(view, R.id.photo);

其实这个方法你自己实现也只需要非常简单的代码,就是使用泛型,例如我自己定义一个静态的工具类Views.java,他的代码如下:

     
     
  1. public class Views {
  2. public static <T> T findById(Activity context, int id) {
  3. return (T) context.findViewById(id);
  4. }
  5. }
  6. //其他的方法也类似

后记:现在的MVVM框架,比如官方的DataBinding 都有其他的方法进行绑定,可能会更好用一些。最近正准备研究这些框架,后面会写一些博文。


严禁转载!谢谢

上一篇: Dagger 2 使用,详细解读和从Dagger1迁移的方法介绍

下一篇: ImageView.scaleType设置图解

   318 人读过
   
立即登录, 发表评论. 
没有帐号?  立即注册
0 条评论
      文档导航
    • 0
      点赞
    • 0
      收藏
      觉得还不错? 一键收藏
    • 0
      评论

    “相关推荐”对你有帮助么?

    • 非常没帮助
    • 没帮助
    • 一般
    • 有帮助
    • 非常有帮助
    提交
    评论
    添加红包

    请填写红包祝福语或标题

    红包个数最小为10个

    红包金额最低5元

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

    抵扣说明:

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

    余额充值