依賴注入框架-ButterKnife使用方法總結
ButterKnife
2018-9-6 10:45 - QG2017移動組 - 張藝雋
ButterKnife是JakeWharton大神出品的用於View的注入框架。提供註解來簡單快捷地完成View的繫結、點選事件的分離等。
- 來自官方的說明:
Field and method binding for Android views which uses annotation processing to generate boilerplate code for you. - Eliminate findViewById calls by using @BindView on fields. - Group multiple views in a list or array. Operate on all of them at once with actions, setters, or properties. - Eliminate anonymous inner-classes for listeners by annotating methods with @OnClick and others. - Eliminate resource lookups by using resource annotations on fields. Remember: A butter knife is like a dagger only infinitely less sharp.
非常有意思的一句話,這裡的匕首指的是Android的另一個主流的依賴注入框架Dagger。
0. 參考資料
1. 開始使用
- 匯入依賴:
dependencies {
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
}
如果使用Kotlin,則將**annotationProcessor**替換為**kapt**
2. 如果不需要在library中使用ButterKnife,則**直接跳過以下步驟。**
- 修改buildscript:
buildscript {
repositories {
mavenCentral()
}
dependencies {
classpath 'com.jakewharton:butterknife-gradle-plugin:8.8.1'
}
}
- 需要gradle降級
classpath 'com.android.tools.build:gradle:3.0.1'
- 在module上方加入:
apply plugin: 'com.jakewharton.butterknife'
- 確保R全部替換為R2
class ExampleActivity extends Activity {
@BindView(R2.id.user) EditText username;
@BindView (R2.id.pass) EditText password;
}
2. zelezny外掛
- 安裝:zelezny是編譯器層面的外掛,用於輔助ButterKnife,提供快捷方法來繫結控制元件。在AS的Setting中找到Plugins,然後搜尋外掛:zelezny,安裝後重啟AS。
- 使用:
setContentView(R.layout.acty_login)
將滑鼠移到acty_login上,右鍵選擇Generate(或Alt+Insert),點選選項Generate ButterKnife Injecttions即可。
3. @BindView
@BindView註解是ButterKnife中最基本的用法,先觀看原始碼:
@Retention(CLASS) @Target(FIELD)
public @interface BindView {
/** View ID to which the field will be bound. */
@IdRes int value();
}
@IdRes註解指定@BindView的引數為一個控制元件資源。
Java
- 在Activity的setContentView後使用ButterKnife.bind(this)
- 在方法外部使用註解@BindView()繫結控制元件。繫結的控制元件不能被private或static修飾。
@BindView(R.id.toolbar)
Toolbar toolbar;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chips);
ButterKnife.bind(this);
setSupportActionBar(toolbar);
}
可以看到這種方式使程式碼變得簡潔明瞭。不需要在onCreate中寫一堆findViewById,而是專注於其他業務的實現。
- Kotlin
@BindView(R.id.toolbar)
@JvmField
var toolbar: Toolbar? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_empty)
ButterKnife.bind(this)
setSupportActionBar(toolbar)
}
或者使用KotterKnife
implementation 'com.jakewharton:kotterknife:0.1.0-SNAPSHOT'
val toolbar : Toolbar by bindView(R.id.toolbar)
其實使用Kotlin時可以放棄ButterKnife,轉而使用更加方便的方法來繫結view: 1. buildscript中加入kotlin的android擴充套件
buildscript {
dependencies {
classpath "org.jetbrains.kotlin:kotlin-android-extensions:$kotlin_version"
}
}
- module中宣告控制元件
apply plugin: 'kotlin-android-extensions'
- 匯入佈局
import kotlinx.android.synthetic.main.app_bar_chips.*
- 直接使用控制元件的id即可
setSupportActionBar(toolbar)
- 下文主要針對Java,不涉及Kotlin程式碼。
4. @BindViews
還記得官方的介紹中有這麼一句話嗎:
Group multiple views in a list or array. Operate on all of them at once with actions, setters, or properties.
ButterKnife提供 @BindViews註解用於繫結多個控制元件。
public class MainActivity extends AppCompatActivity {
@BindViews({ R2.id.button1, R2.id.button2, R2.id.button3})
public List<Button> buttonList ;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ButterKnife.bind(this);
buttonList.get( 0 ).setText( "hello 1 ");
buttonList.get( 1 ).setText( "hello 2 ");
buttonList.get( 2 ).setText( "hello 3 ");
}
}
同時ButetrKnife提供快捷的apply() 方法對View陣列進行操作。 1. 通過Action:
ButterKnife.apply(views, new ButterKnife.Action<View>() {
@Override
public void apply(@NonNull View view, int index) {
view.setVisibility(View.GONE);
}
});
- 通過Setter:
ButterKnife.apply(views, new ButterKnife.Setter<View, Integer>() {
@Override
public void set(@NonNull View view, Integer value, int index) {
view.setVisibility(value);
}
}, View.INVISIBLE);
- 通過Property:
ButterKnife.apply(views, View.ALPHA, 0.0f);
View中還提供rotation、scale、translation等屬性用作Property引數,具體用處不詳細介紹。
5.在Fragment中使用
在Fragment中使用ButterKnife時 - 在onCreateView中使用ButterKnife.bind(this,view) 進行繫結。這裡的 this是Fragment,不能使用getActivity()或者getContext()將Activity、Context傳進去。 - 在onDestroyView中解除ButterKnife與Fragment的繫結。
public class ButterknifeFragment extends Fragment{
private Unbinder unbinder;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment, container, false);
//返回一個Unbinder值(進行解綁),注意這裡的this不能使用getActivity()
unbinder = ButterKnife.bind(this, view);
return view;
}
/**
* onDestroyView中進行解綁操作
*/
@Override
public void onDestroyView() {
super.onDestroyView();
unbinder.unbind();
}
}
6. 在ViewHolder中使用
在ViewHolder中使用ButterKnife時,在建構函式中使用ButterKnife.bind(this, view) 進行繫結
static class ViewHolder {
@BindView(R.id.title) TextView name;
@BindView(R.id.job) TextView job;
public ViewHolder(View view) {
ButterKnife.bind(this, view);
}
}
7. @OnClick註解
@Onclick註解在方法上(@Target(METHOD)),將點選事件從臃腫的程式碼中抽離出來成為一個獨立的方法。
@OnClick(R.id.button1) //給 button1 設定一個點選事件
public void showToast(){
Toast.makeText(this, "is a click", Toast.LENGTH_SHORT).show();
}
@OnLongClick(R2.id.button1) //給 button1 設定一個長按事件
public boolean showToast2(){
Toast.makeText(this, "is a long click", Toast.LENGTH_SHORT).show();
return true;
}
- Java
//給一組控制元件設定點選事件
@OnClick({R.id.ll_product_name, R.id.ll_product_lilv,R.id.ll_product_repayment_methods})
public void onViewClicked(View view) {
switch (view.getId()) {
case R.id.ll_product_name:
System.out.print("我是點選事件1");
break;
case R.id.ll_product_lilv:
System.out.print("我是點選事件2");
break;
case R.id.ll_product_repayment_methods:
System.out.print("我是點選事件3");
break;
}
}
- Kotlin
private val user by lazy { User() }
//給一組控制元件設定點選事件
@OnClick(R.id.iv_user_img, R.id.tv_user_age, R.id.tv_user_name)
fun onViewsClick(v: View) : Unit {
val msg = with(user) {
when {
v is ImageView -> "Declare this intent to show a img of $name"
v.id == R.id.tv_user_name -> "User's name: $name"
v.id == R.id.tv_user_age -> "User's age : $age"
else -> ""
}
}
println(msg)
}
8. 更多註解
- 繫結註解
@BindView ->繫結一個view
@BindViews -> 繫結多個view
@BindArray -> 繫結string裡面array陣列:@BindArray(R.array.city ) String[] citys;
@BindBitmap ->繫結圖片資源為Bitmap:@BindBitmap( R.mipmap.wifi ) Bitmap bitmap;
@BindBool ->繫結boolean值
@BindColor ->繫結color
@BindDimen ->繫結Dimen:@BindDimen(R.dimen.borth_width) int mBorderWidth;
@BindDrawable -> 繫結Drawable:@BindDrawable(R.drawable.test_pic) Drawable mTestPic;
@BindFloat ->繫結float
@BindInt ->繫結int
@BindString ->繫結一個String
- 事件註解
@OnClick ->點選事件
@OnCheckedChanged ->選中,取消選中
@OnEditorAction ->軟鍵盤的功能鍵
@OnFocusChange ->焦點改變
@OnItemClick ->item被點選(注意這裡有坑,如果item裡面有Button等這些有點選的控制元件事件的,需要設定這些控制元件屬性focusable為false)
@OnItemLongClick ->item長按(返回真可以攔截onItemClick)
@OnItemSelected ->item被選擇事件
@OnLongClick ->長按事件
@OnPageChange ->頁面改變事件
@OnTextChanged ->EditText裡面的文字變化事件
@OnTouch ->觸控事件
@Optional ->選擇性注入,如果當前物件不存在,就會丟擲一個異常,為了壓制這個異常,可以在變數或者方法上加入一下註解,讓注入變成選擇性的,如果目標View存在,則注入, 不存在,則什麼事情都不做
下面使用 @OnItemSelected註解為Spinner設定OnItemClickListener
@OnItemSelected(R.id.my_spiner)
void onItemSelected(int position) {
Toast.makeText(this, "position: " + position, Toast.LENGTH_SHORT).show();
}
等同於
spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
Toast.makeText(this, "position: " + position, Toast.LENGTH_SHORT).show();
}
@Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
若想要指定回撥的方法為onNothingSelected,則可以這樣設定:
@OnItemSelected(value = R.id.my_spiner, callback = OnItemSelected.Callback.NOTHING_SELECTED)
void onNothingSelected() {
Toast.makeText(this, "Nothing", Toast.LENGTH_SHORT).show();
}
結語:沒想到花了整個晚上來看ButterKnife。這款著名的框架在github上有22,000+個star,實在是非常驚人。它的實現使用了神奇的Javapoet來動態生成程式碼,因此觀看ButterKnife的原始碼也列入了我的程序中。在依賴注入方面,我下一階段的目標將是學習Dragger2。
#ButterKnife - 完()