您的位置:首页 > Web前端

ButterKnife 使用教程

2016-04-16 01:29 405 查看


加入到项目的方法

把这块放到最前是有原因的,下面是方法: 

MAVEN项目:(7.0.1是本文当前版本号)(在pom.xml文件中)
<dependency>

<groupId>com.jakewharton</groupId>

<artifactId>butterknife</artifactId>

<version>7.0.1</version>

</dependency>


Gradle项目(现在Android应该都是Gradle了吧,在build.gradle文件中)
compile 'com.jakewharton:butterknife:7.0.1'


另外,还需要下面两个配置:
//支持lint warning 检查机制

lintOptions {

disable 'InvalidPackage'

}

//为什么加入这个呢?防止冲突,比如我同时用了dagger-compiler就会报错,说下面这个`Processor`重复了


packagingOptions {

exclude 'META-INF/services/javax.annotation.processing.Processor'

}


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

-dontwarn butterknife.internal.**

-keep class **$$ViewBinder { *; }


-keepclasseswithmembernames class * {

@butterknife.* <fields>;

}


-keepclasseswithmembernames class * {

@butterknife.* <methods>;

}



最简单的用法

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

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

btn=(Button)findViewById(R.id.btn);

btn.setOnClickListener(new View.OnClickListener() {

        @Override

public void onClick(View v) {

Toast.makeText(MainActivity.this, "Btn Clicked", Toast.LENGTH_SHORT).show();

}

});


}


后来有了ButterKnife就简单了,如下:
class ExampleActivity extends Activity {

@Bind(R.id.title) Button btn;


@Override public void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

ButterKnife.bind(this);

// TODO Use fields...

}

}


没有一堆的
findViewById
和强制转换是不是清爽多了呢? 

而且,即使这些代码,都可以通过
Android Studio
的插件
Android
ButterKnife Zelezny
帮你完成了。

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

activity.btn = (android.widget.Button) activity.findViewById(2130968578);

}


ButterKnife以前绑定的代码是
@InjectView
注解和
inject
方法,现在改了名字感觉更容易理解了,基本原理没变。

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


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

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


更多的绑定


绑定资源

class ExampleActivity extends Activity {

@BindString(R.string.title) String title;

@BindDrawable(R.drawable.graphic) Drawable graphic;

@BindColor(R.color.red) int red; // int or ColorStateList field

@BindDimen(R.dimen.spacer) Float spacer; // int (for pixel size) or float (for exact value) field

// ...

}


绑定其他的资源类似,应该不需要一一列举了吧
不过有时候资源不需要搞成成员变量吧?自己选择吧


非Activity的绑定

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

@Bind(R.id.button1) Button button1;

@Bind(R.id.button2) Button button2;


@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fancy_fragment, container, false);

ButterKnife.bind(this, view);

// TODO Use fields...

return view;

}

}



在Adapter中的用法

经常会在Adapter中使用ViewHolder,其实你也可以在ViewHolder中使用ButterKnife: 

在ViewHolder的构造函数中调用
bind
,然后成员变量同样的使用
@Bind
注解。其实这几种绑定原理都一样,就是我们前面编译后的代码那样的方式。
public class MyAdapter extends BaseAdapter {

@Override public View getView(int position, View view, ViewGroup parent) {

ViewHolder holder;

if (view != null) {

holder = (ViewHolder) view.getTag();

} else {

view = inflater.inflate(R.layout.whatever, parent, false);

holder = new ViewHolder(view);

view.setTag(holder);

}


holder.name.setText("John Doe");

// etc...


return view;

}


static class ViewHolder {

@Bind(R.id.title) TextView name;

@Bind(R.id.job_title) TextView jobTitle;


public ViewHolder(View view) {

  ButterKnife.bind(this, view);

}

}

}


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


View List批量操作

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

@Bind({ R.id.first_name, R.id.middle_name, R.id.last_name })

List<EditText> nameViews;


//批量设置

ButterKnife.apply(nameViews, DISABLE);

ButterKnife.apply(nameViews, ENABLED, false);


其中,
DIABLE
,
ENABLED
是我们定义的两个对象,一个是Action,负责执行操作,一个是Setter,负责将值设置为第三个参数:
static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {

@Override public void apply(View view, int index) {

view.setEnabled(false);

}

};

static final ButterKnife.Setter<View, Boolean> ENABLED = new ButterKnife.Setter<View, Boolean>() {

@Override public void set(View view, Boolean value, int index) {

view.setEnabled(value);

}

};


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

另外,我们可以直接对属性进行设置,而不需要编写
Action
Setter
,例如:
ButterKnife.apply(nameViews, View.ALPHA, 0.0f);



绑定监听器

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

public void submit(View view) {

// TODO submit data to server...

}


如果不需要绑定的对象,不写也可以:
@OnClick(R.id.submit)

public void submit() {

// TODO submit data to server...

}


而且,这里可以直接将绑定的实例转换成实际的对象:
@OnClick(R.id.submit)

public void sayHi(Button button) {

button.setText("Hello!");

}


另外,多个View也可以绑定到一个处理方法上:
@OnClick({ R.id.door1, R.id.door2, R.id.door3 })

public void pickDoor(DoorView door) {

if (door.hasPrizeBehind()) {

Toast.makeText(this, "You win!", LENGTH_SHORT).show();

} else {

Toast.makeText(this, "Try again", LENGTH_SHORT).show();

}

}


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

@OnClick

public void onClick() {

// TODO do something!

}

}


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


绑定重置

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

@Bind(R.id.button1) Button button1;

@Bind(R.id.button2) Button button2;


@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

View view = inflater.inflate(R.layout.fancy_fragment, container, false);

ButterKnife.bind(this, view);

// TODO Use fields...

return view;

}


@Override public void onDestroyView() {

super.onDestroyView();

ButterKnife.unbind(this);

}

}



可选的绑定

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


@Nullable @OnClick(R.id.maybe_missing) void onMaybeMissingClicked() {

// TODO ...

}



多方法监听

例如ListView的设置
onItemOnItemSelected
SelectedListener
接口有两个回调方法,一个是
onItemSelected
,一个是
onNothingSelected
,这时候,我们可以设置两个注解来分别处理这两个回调方法:
@OnItemSelected(R.id.list_view)

void onItemSelected(int position) {

// TODO ...

}


@OnItemSelected(value = R.id.maybe_missing, callback = NOTHING_SELECTED)

void onNothingSelected() {

// TODO ...

}



小工具

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


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

public static <T> T findById(Activity context, int id) {

return (T) context.findViewById(id);

}

}

//其他的方法也类似
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  ButterKnife