1. 程式人生 > >依賴注入框架-ButterKnife使用方法總結

依賴注入框架-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. 開始使用

  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外掛

  1. 安裝:zelezny是編譯器層面的外掛,用於輔助ButterKnife,提供快捷方法來繫結控制元件。在AS的Setting中找到Plugins,然後搜尋外掛:zelezny,安裝後重啟AS。
  2. 使用:
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

    1. 在Activity的setContentView後使用ButterKnife.bind(this)
    2. 在方法外部使用註解@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"
    }
}
  1. module中宣告控制元件
apply plugin: 'kotlin-android-extensions'
  1. 匯入佈局
import kotlinx.android.synthetic.main.app_bar_chips.*
  1. 直接使用控制元件的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);
    }
});
  1. 通過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);
  1. 通過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 - 完()