前言
ButterKnife是控件注入框架,可以帮助安卓开发者省去初始化控件的重复性工作,简单快捷地初始化布局文件中的控件,极大地提升开发效率。
项目地址-传送门。
导入ButterKnife至项目中
在项目的build.gradle中添加依赖,然后同步项目,即可下载butterknife库至项目中
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation 'com.android.support:appcompat-v7:25.2.0'
//加入下面这两行代码
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
简单使用butterknife初始化控件
1.创建一个android工程,布局如下
2.在activity中的oncreate()方法里初始化butterknife框架
注意初始化要放在setView()之后
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_butter_knife_test);
ButterKnife.bind(this);
}
3.查找TextView与Button
public class ButterKnifeTestActivity extends AppCompatActivity {
//绑定控件,省去了写findviewbyid的重复性操作
@BindView(R.id.tv_test1)
private TextView tvTest;
@BindView(R.id.btn_test1)
private Button btnTest;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_butter_knife_test);
ButterKnife.bind(this);
}
}
4.测试控件是否被正确初始化
tvTest.setText("文本控件已被初始化");
btnTest.setText("按钮被初始化");
运行工程,结果如下,报了一个编译错误。
Error:(14, 22) 错误: @BindView fields must not be private or static. (com.example.hapzhu.andrioddemotest.ButterKnifeTestActivity.tvTest)
意思是控件不能被声明为私有的或者是静态的,看来butterknife框架是对控件声明有限制的,将private去掉即可。
@BindView(R.id.tv_test1)
TextView tvTest;
@BindView(R.id.btn_test1)
Button btnTest;
5.给按钮设置点击监听事件
@OnClick(R.id.btn_test1)
public void onclick(View view){
btnTest.setText("我被点击了");
tvTest.setText("天若有情天亦老");
}
随便写一个方法,参数可以放View也可以不放任何参数,
在方法上一行注解OnClick即可绑定点击事件,注意该方法必须不能为private或者是static的,与声明时规则一致。
下载注解插件进行一键初始化
打开android的plugins中心,搜索butterknife
点击browse repo,下载前两个插件都可以。
下载完成后,回到activity代码的setContentview处,右键布局文件名,注意一定要把光标停在布局文件名上
选择Generate,这时可以看到生成butterknife注解的选项,如果光标不放在布局文件名上,则看不到该选项。
点击生成butterknife注解
插件将布局文件中的所有id都加载出来了,还可以设置点击事件,点击confirm即可快速初始化控件,非常方便。
@BindView(R.id.tv_test1)
TextView tvTest1;
@BindView(R.id.btn_test1)
Button btnTest1;
@BindView(R.id.et_test1)
EditText etTest1;
@BindView(R.id.lv_test1)
ListView lvTest1;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_butter_knife_test);
ButterKnife.bind(this);
tvTest1.setText("文本控件已被初始化");
btnTest1.setText("按钮被初始化");
}
@OnClick(R.id.btn_test1)
public void onViewClicked() {
btnTest1.setText("我被点击了");
tvTest1.setText("天若有情天亦老");
}
效果与手动注解一样,对于安卓开发工作者来说,这个插件确实是个神器,节省了大量初始化控件的时间。
其它用途绑定
绑定res里资源对象
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
// ...
}
绑定Fragment
public class FancyFragment extends Fragment {
@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
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);
//利用构造器将item的view传入viewHolder中
holder = new ViewHolder(view);
view.setTag(holder);
}
holder.name.setText("John Doe");
// etc...
return view;
}
static class ViewHolder {
@BindView(R.id.title) TextView name;
@BindView(R.id.job_title) TextView jobTitle;
public ViewHolder(View view) {
//在构造器中绑定view
ButterKnife.bind(this, view);
}
}
}
绑定控件ID直接将控件添加至集合或数组中
@BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name })
//nameViews的集中中添加了三个edittext对象
List<EditText> nameViews;
一次性改变集合中所有对象的值-apply
//Action and Setter interfaces allow specifying simple behavior.
//初始化DISABLE
static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {
@Override public void apply(View view, int index) {
//将view设为不可输入
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);
}
};
//DISABLE与ENABLED是上述接口的实现类,表示统一设置view集合的对应属性
ButterKnife.apply(nameViews, DISABLE);
ButterKnife.apply(nameViews, ENABLED, false);
//如果是view的自带属性,可以不用实现接口,直接使用
ButterKnife.apply(nameViews, View.ALPHA, 0.0f);
绑定listview的item点击事件
@OnItemSelected(R.id.list_view)
void onItemSelected(int position) {
// TODO ...
}
findviewById的简化
适用于从dialog中找到控件
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);
2018/04/03更新注意事项
在Android Studio 3.0以上,依赖的控件是butter knife 8.5.1以上版本都会报这个错
需要将classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'
这个控件的依赖降到8.5.1
Unable to find method 'com.android.build.gradle.api.BaseVariant.getOutputs()Ljava/util/List;'.
Possible causes for this unexpected error include:<ul><li>Gradle's dependency cache may be corrupt (this sometimes occurs after a network connection timeout.)
Re-download dependencies and sync project (requires network)</li><li>The state of a Gradle build process (daemon) may be corrupt. Stopping all Gradle daemons may solve this problem.
Stop Gradle build processes (requires restart)</li><li>Your project may be using a third-party plugin which is not compatible with the other plugins in the project or the version of Gradle requested by the project.</li></ul>In the case of corrupt Gradle processes, you can also try closing the IDE and then killing all Java processes.
2018/07/28更新:as 3.1及以上,butterknife 8.8.1报错解决方案
在Android studio 3.1及以上版本中,butterknife又是最新版的8.8.1,构建时老是报错。针对此异常,查看了很多解决方法,没想到最后解决方案竟然是这么简单。
只需要将build中的引用的butteknife注释插件去掉,然后将app.build的butterknife依赖跟注解依赖改成最新的8.8.1即可解决构建异常问题,并且功能正常使用。
实际上是我之前所写的引入butterknife插件的那两步是只针对于组件中的library的,并不是主module,所以会出现构建异常,如果是主module只需要添加依赖即可了,这确实是我误导了别人,深感抱歉,现在我将前面的内容给移到这里来了。
下述步骤为组件library中使用butterknife插件的方法。
1.在library工程的build.gradle中导入butterknife插件
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
//加入下面这段代码
classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'
}
}
allprojects {
repositories {
jcenter()
}
}
2.在项目的build.gradle中添加butterknife的插件,即是app中的builde.gradle
//引用的library表示这是一个组件,而非主module(application)
apply plugin: 'com.android.library'
//加入下面这段代码
apply plugin: 'com.jakewharton.butterknife'
3.使用R2代替R来查找资源id
class ExampleActivity extends Activity {
@BindView(R2.id.user) EditText username;
@BindView(R2.id.pass) EditText password;
...
}
之前在主module中添加了butterknifte插件并构建异常的解决方案。
第一步:
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:2.3.0'
//删除前文中所说在工程的build.gradle中引入的插件
//classpath 'com.jakewharton:butterknife-gradle-plugin:8.5.1'
}
}
allprojects {
repositories {
jcenter()
}
}
第二步
apply plugin: 'com.android.application'
//在app.build删除下面这段代码
//apply plugin: 'com.jakewharton.butterknife'
第三步
将butterknife的依赖改为下述代码
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'