ButterKnife简介

ButterKnife是JakeWharton大神开发的一个开源库,官方对这个库的介绍为:

Butter Knife
Field and method binding for Android views

ButterKnife是一个使用注解方式来为Android中的View视图绑定字段和方法,能通过自动解析注解来搜索资源文件并赋值给Activity中的字段,如使用@BindView,@BindColor替代原生的findViewById,getColor等方法,或者给View视图的监听器绑定方法,如使用@OnClick 替代 setOnClickListener等方法。ButterKnife通过注解方式为我们封装了很多原生操作,我们只需要编写少量的代码就能实现跟原生代码一样的操作,减少了开发者的很多工作,提高了工作效率。

ButterKnife是通过使用注解方式来自动生成模板代码,从而来将Activity中的字段和方法与View绑定在一起。目前ButterKnife支持如下的特性:

  1. 使用@BindView 来代替findViewById 完成View的引用。
  2. 将多个View组合成list或者array,使用actions,setters或者属性来同时操作它们。
  3. 使用@OnClick等注解字段来注解方法,从而来代替监听器中的匿名内部类。
  4. 使用@BindString等注解字段来注解字段,从而来代替Context.getString等获取资源的方式。

这样说很抽象,下面就使用代码来实践说明一下。

Android Studio配置

从外部引入ButterKnife库很简单,只需要在项目的App包中的build.gradle文件中的dependencies中加入依赖即可:

dependencies {.....//省略    compile 'com.jakewharton:butterknife:8.5.1'    annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1'}

最新版本的8.5.1,如需了解到最新的版本号可以参考Github:
https://github.com/JakeWharton/butterknife

View绑定

我们一般在Activity中的onCreate中使用findViewById方式来引用布局文件中的View对象,而且还需要进行强制类型转换成所声明的变量的类型。当有多个View时,就需要调用多次findViewById方法,代码工作量大,而且也不简洁。ButterKnife使用@BindView(View的ID)注解字段来注解变量,这样ButterKnife就能自动引用布局中的View资源并能转换为所声明的相应的View类型。

    @BindView(R.id.MainActivity_Btn)Button btn;    @BindView(R.id.MainActivity_Text)TextView text;    @BindView(R.id.MainActivity_RecyclerView)RecyclerView recyclerView;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        ButterKnife.bind(this);        }

注意还需要在onCreate方法调用ButterKnife.bind(this)来绑定Activity和ButterKnife。

以前一直以为ButterKnife是使用反射机制的,所以一直对ButterKnife的性能有点担忧。现在才了解到ButterKnife并不是使用反射机制的,而是解析注解字段来自动生成代码来执行查询和绑定的,说到底就是为我们封装了一些代码,减少我们的工作量。具体生成的代码文件可以查看app包中的build-generated-source-apt-debug-包名中的类文件,里面的类文件命名规则为调用ButterKnife的bind方法的class类加上”_ViewBinding”,具体如下:
Android ButterKnife 注解框架的使用详解和原理分析_第1张图片

点击MainActivity_ViewBinding文件进去查看源码,可以看到变量对应我们在MainActivity中声明的View变量一致:
Android ButterKnife 注解框架的使用详解和原理分析_第2张图片

在MainActivity_ViewBinding方法进行变量的初始化和监听绑定:
Android ButterKnife 注解框架的使用详解和原理分析_第3张图片

在其中的代码中可以看到 Utils.findOptionalViewAsType来引用布局中的View并进行类型转换的:

    target.text = Utils.findOptionalViewAsType(source, R.id.MainActivity_Text, "field 'text'", TextView.class);    target.recyclerView = Utils.findRequiredViewAsType(source, R.id.MainActivity_RecyclerView, "field 'recyclerView'", RecyclerView.class);

对于监听器的绑定,则直接调用了对应的setOnXXXX方法,然后在监听器方法里面调用target即Activity里面的被ButterKnife注解的事件处理方法:

Android ButterKnife 注解框架的使用详解和原理分析_第4张图片

其中的部分源代码:

    view.setOnClickListener(new DebouncingOnClickListener() {      @Override      public void doClick(View p0) {        target.multiHandleClick(Utils.castParam(p0, "doClick", 0, "multiHandleClick", 0));      }    });    view = Utils.findRequiredView(source, R.id.MainActivity_EditText, "method 'onTextChanged', method 'beforeTextChanged', and method 'afterTextChanged'");    view2131427424 = view;    view2131427424TextWatcher = new TextWatcher() {      @Override      public void onTextChanged(CharSequence p0, int p1, int p2, int p3) {        target.onTextChanged(p0, p1, p2, p3);      }      @Override      public void beforeTextChanged(CharSequence p0, int p1, int p2, int p3) {        target.beforeTextChanged(p0, p1, p2, p3);      }      @Override      public void afterTextChanged(Editable p0) {        target.afterTextChanged(p0);      }    };    ((TextView) view).addTextChangedListener(view2131427424TextWatcher);

从上面的分析中就可以总结出ButterKnife的原理了,简单来说就是ButterKnife框架解析注解字段,然后自动生成代码来引用View和为View绑定事件监听器。

Resources资源绑定

除了使用@BindView来引用view,ButterKnife还对引用resource资源文件也提供了相应的支持。可以使用@BindBool,@BindColor,@BindDimen,@BindDrawable,@BindInt,@BindString等注解字段来引用预定义的resource资源。
如:

    @BindString(R.string.app_name)String sub;    @BindColor(R.color.TextColor)int textColor;

这个资源绑定的原理也很简单,可以参考上面的分析,在MainActivity_ViewBinding文件中的MainActivity_ViewBinding方法中可以看到:

    Context context = source.getContext();    Resources res = context.getResources();    target.textColor = ContextCompat.getColor(context, R.color.TextColor);    target.sub = res.getString(R.string.app_name);

NON-Activity绑定

除了可以在Activity中使用 ButterKnife来进行绑定,还可以在Fragment,RecyclerView的Adapter等任意对象上使用,前提是要提供这个对象上绑定的根视图View Root。
使用 ButterKnife.bind(Object,View);方法来绑定对象和ButterKnife,如下为Fragment中的使用:

public class MainFragment extends Fragment{    @BindView(R.id.MainFragment_Btn)Button btn;    @BindView(R.id.MainFragment_Text)TextView text;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){        View view=inflater.inflate(R.layout.fragement_main,container,false);        ButterKnife.bind(this,view);        return view;    }}

在Adapter中使用ButterKnife来简化ViewHolder模式:

public class ListAdapter extends RecyclerView.Adapter<ListAdapter.ListHolder> {    private ArrayList data;    public ListAdapter(ArrayList list){        this.data=list;    }    @Override    public ListHolder onCreateViewHolder(ViewGroup parent, int viewType) {        return new ListHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item,parent,false));    }    @Override    public void onBindViewHolder(ListHolder holder, int position) {        holder.text.setText("Text: no"+data.get(position));    }    @Override    public int getItemCount() {        return data.size();    }    static class ListHolder extends RecyclerView.ViewHolder{        @BindView(R.id.MainActivity_ListItem_Btn)Button btn;        @BindView(R.id.MainActivity_ListItem_Text)TextView text;        public ListHolder(View itemView) {            super(itemView);            ButterKnife.bind(this,itemView);        }    }}

View List

对于多个有相同作用的View时,我们可以将这些View组合成一个List或者Array,然后只需调用一个方法就可以对这些View实行同一个操作。
使用@BindViews( { View.ID,View.ID,View.ID…} )方法来初始化,注意有花括号 { }:

    @BindViews({R.id.MainActivity_FirstName,R.id.MainActivity_MiddleName,R.id.MainActivity_LastName})    List nameTexts;

对同一组中的View实行同一操作可以使用apply方法,首先我们来了解下两个重要的接口 Action和Setter,这两个接口可以指定对View的行为。在Activity中初始化这两个接口:

    static final ButterKnife.Action DISABLE=new ButterKnife.Action() {        @Override        public void apply(@NonNull View view, int index) {            view.setEnabled(false);        }    };    static final ButterKnife.Setter ENABLE=new ButterKnife.Setter() {        @Override        public void set(@NonNull View view, Boolean value, int index) {            view.setEnabled(value);        }    };    static final ButterKnife.Setter ChangeColor=new ButterKnife.Setter() {        @Override        public void set(@NonNull TextView view, Integer value, int index) {            view.setTextColor(value);        }    };

可以看到Action和Setter方法的区别,Action接口只能获取到View对象,但是不能传递参数;而Setter接口使用了泛型,可以在实现接口的时候指定需要传递的数据类型,然后在其中的set方法的第二个参数中获取传递参数。
使用apply就可以执行这些定义中的操作了:

        ButterKnife.apply(nameTexts,DISABLE);        ButterKnife.apply(nameTexts,ENABLE,false);        ButterKnife.apply(nameTexts,ChangeColor,textColor);

ButterKnife还允许在apply方法中直接使用Android中的属性,例如:

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

监听绑定

一般我们为View设置监听需要调用View.addOnXXXXXListener或者setOnXXXXXListener方法来绑定事件监听器,这样每次都要新建匿名内部类,而且有时还必须实现监听器接口中我们并不需要使用的事件方法。ButterKnife也对事件监听器绑定提供了支持,只需要使用相应的注解字段,如@OnClick,@OnTextChanged字段,来注解一个已声明的方法,然后ButterKnife就会自动将这个方法与事件监听器绑定在一起。
对于单个View的事件监听方法,方法参数可有可无:

    @OnClick(R.id.MainActivity_Btn)    public void click(View view){        Log.e("ButterKnife","Click:"+view.getId());    }    @OnClick(R.id.MainActivity_Btn)    public void click(){        Log.e("ButterKnife","Click no arguments");    }    @OnClick(R.id.MainActivity_Btn)    public void click(Button button){//指定View类型,会被自动类型转换        Log.e("ButterKnife","button Click:"+button.getId());    }

我们也可以在OnClick方法参数中声明多个View.ID,然后就可以对这一组View绑定同一监听器来实现相同的事件处理:

    @OnClick({R.id.MainActivity_FirstName,R.id.MainActivity_MiddleName,R.id.MainActivity_LastName})    public void multiHandleClick(TextView textView){        Log.e("ButterKnife","multiHandleClick:"+textView.getId());    }

在自定义View中,可以不指定View.ID来绑定监听器:

public class FancyButton extends Button {  @OnClick  public void onClick() {    // TODO do something!  }}

监听器多事件处理

当一个View的监听器中有多个事件处理函数时,如TextWatch监听器,我们可以指定其中的一个事件处理函数来绑定方法。ButterKnife为每一个方法注解字段都默认绑定了一个事件处理函数,我们可以使用callback 参数来为方法注解字段指定相应的事件处理函数。
例如@OnTextChanged方法注解字段默认绑定的是TextWatch中的onTextChanged事件处理函数,但我们可以使用callback = OnTextChanged.Callback.BEFORE_TEXT_CHANGED来为@OnTextChanged注解绑定beforeTextChanged事件处理函数,使用callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED来为@OnTextChanged注解绑定afterTextChanged事件处理函数:

    @OnTextChanged(R.id.MainActivity_EditText)    public void onTextChanged(CharSequence s, int start, int before, int count){        Log.e("onTextChanged",String.valueOf(s));    }    @OnTextChanged(value = R.id.MainActivity_EditText,callback = OnTextChanged.Callback.BEFORE_TEXT_CHANGED)    public void beforeTextChanged(CharSequence s, int start, int count, int after){        Log.e("beforeTextChanged",String.valueOf(s));    }    @OnTextChanged(value = R.id.MainActivity_EditText,callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)    public void afterTextChanged(Editable s){        Log.e("afterTextChanged",String.valueOf(s.toString()));    }

解除绑定

Fragment跟Activity一样,有自己对应的生命周期。在Fragment的onCreateView方法中绑定View,在onDestroyView将view置为null。当我们在onCreateView中调用ButterKnife.bind(this,view);时,这个方法会返回一个Unbinder实例对象,可以在适当的生命周期回调函数中调用Unbinder的unbind方法来解除View和ButterKnife的绑定,释放资源。

public class MainFragment extends Fragment{    @BindView(R.id.MainFragment_Btn)Button btn;    @BindView(R.id.MainFragment_Text)TextView text;    private Unbinder unbinder;    @Override    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState){        View view=inflater.inflate(R.layout.fragement_main,container,false);        unbinder=ButterKnife.bind(this,view);        return view;    }    @Override    public void onDestroyView(){        super.onDestroyView();        unbinder.unbind();    }}

防止异常发生

当使用@Bind来引用View和资源或者绑定View事件处理函数时,如果目标view是null就会抛出异常。为了防止异常的发生和创建可选择性的绑定,在变量上使用@Nullable注解,在方法上使用@Optional注解。

    @Nullable @BindView(R.id.MainActivity_Btn)Button btn;    @Nullable @BindView(R.id.MainActivity_Text)TextView text;    @Optional    @OnClick(R.id.MainActivity_Btn)    public void click(View view){        Log.e("ButterKnife","Click:"+view.getId());    }

使用findById

除了使用@BindView来简化findViewById的使用,我们还可以使用ButterKnife.findById来简化在View,Activity或者Dialog中来引用View的代码。ButterKnife使用了泛型来推断返回的View类型并自动进行强制类型转换:

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

参考:
http://jakewharton.github.io/butterknife/

更多相关文章

  1. 32、adb INSTALL_FAILED_TEST_ONLY 处理方法
  2. js调Android与IOS方法
  3. android studio 添加按钮点击事件的三种方法
  4. 在android中运行java main方法
  5. React-Native 调用原生方法,弹出自定义对话框
  6. Android截屏截图的几种方法总结
  7. Ubuntu adb devices : no permissions 解决方法

随机推荐

  1. mysql 5.7.21解压版本安装 Navicat数据库
  2. windows 10下mysql 5.7.21 winx64安装配
  3. win10下mysql5.7.21解压版安装教程
  4. Windows下mysql5.7.21安装详细教程
  5. mysql中datetime类型设置默认值方法
  6. mysql 5.7.21 winx64免安装版配置方法图
  7. mysql 5.7.20\5.7.21 免安装版安装配置
  8. MySQL5.7.21安装与密码图文配置教程
  9. 云服务器Ubuntu_Server_16.04.1安装MySQL
  10. MySQL快速对比数据技巧