1. 程式人生 > >Butterknife系列一:史上最全的android中Butterknife的使用

Butterknife系列一:史上最全的android中Butterknife的使用

史上最全的android中Butterknife的使用

Butterknife是什麼?

  是由國外一個大牛開源出來的一個專案,是為了用過註解的形式來在android中繫結view以及事件資訊。 目前在github上面的開源地址為
https://github.com/JakeWharton/butterknife
有興趣的可以看看

Butterknife的好處

  1. 強大的View繫結事件和資原始檔的繫結
  2. 使用的便捷性上,剔除了原始繫結時候的複雜性
  3. 由於大多數的註解都是在編譯期,所以不會影響程式執行的效率
  4. 程式碼清晰,可讀性相對來說要強很多

怎麼使用Butterknife?

外掛的新增和庫的依賴

外掛的新增

    File -> Settings -> Plugins -> 搜尋ButterKnife,找到Android ButterKnife Zeleany進行安裝重啟AndroidStudio 

這裡寫圖片描述

庫的依賴

如果想要去使用Butterknife,肯定是要去進行庫的依賴的吧
    compile 'com.jakewharton:butterknife:8.8.1'
    annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'

認識Butterknife的註解型別

Butterknife最新的版本中,為我們提供了總共有25個註解。其中分成兩類,一個是資源的繫結形式,另外一種就是事件監聽的型別

繫結註解,檢視,資源,等等,一共13個
名稱 解析
@BindViews 繫結多個view id 為一個view的list變數 @BindViews({ R.id.first_name, R.id.middle_name, R.id.last_name }) List<EditText> nameViews;
@BindView 繫結一個view id為一個view變數@BindView(R.id.title) TextView title;
@BindArray 繫結String中的array陣列 @BindArray(R.array.city)String[] citys;
@BindBitmap 繫結圖片資原始檔, @BindBitmap(R.mipmap.wifi) Bitmap bitmap;
@BindBool 繫結真假boolean @BindBool(R.bool.boor)
@BindColor 繫結顏色 @BindColor(R.color.red)
@BindDimen 繫結尺寸 @BindDimen(R.dimen.spacer) Float spacer;
@BindDrawable 繫結Drawable @BindDrawable(R.drawable.graphic) Drawable graphic
@BindFloat 繫結Float
@BindInt 繫結Int
@BindString 繫結一個String id為String變數, @BindString(R.string.app_name) String msg
@BindAnim 繫結動畫
@BindFont 繫結字型文字
繫結事件,一共有12個事件監聽
名稱 解析
@OnClick 點選事件
@OnCheckedChanged 選中,選中取消
@OnEditorAction 軟鍵盤的功能按鍵
@OnFocusChange 焦點改變
@OnItemClick Item被點選事件(注意這裡有坑,如果item裡面有Button等這些有點選的控制元件事件的,需要設定這些控制元件屬性focusable為false)
@OnItemLongClick tem長按,返回真則可以攔截onItemClick
@OnItemSelected Item被選擇事件
@OnLongClick 長按事件
@OnPageChange 頁面改變事件
@OnTextChanged EditText裡面的文字變化事件
@OnTouch 觸控事件
@Optional 選擇性注入,如果當前物件不存在,就會丟擲一個異常,為了壓制這個異常,可以在變數或者方法上加入一下註解,讓注入變成選擇性的,如果目標View存在,則注入, 不存在,則什麼事情都不做=如下程式碼
ButterKnife在Activity中的使用方式
資料繫結標籤13個,Bind系列

@BindAnim
使用方式如下所示:

@BindAnim(R.anim.fade_in) Animation fadeIn;

@BindArray
使用方式如下所示:
由於陣列資原始檔中的陣列可以定義成多種資料型別,因此使用方式如下所示

1:String array
    //陣列:
    <string-array name="countries">
        <item>中國</item>
        <item>日本</item>
</string-array>
    @BindArray(R.array.countries) String[] countries;
2:Int array
    //陣列:
    <integer-array name="phones">
        <item>12345</item>
        <item>43534</item>
    </integer-array>
    @BindArray(R.array.phones) int [] phones
3:Text array
    //陣列:與stringArray類似
    <string-array name="countries">
        <item>中國</item>
        <item>日本</item>
    </string-array>
    @BindArray(R.array.options) CharSequence[] options;

4:Type array ----->android.content.res.TypeArray
    //陣列,TypeArray中是一種混合型別形式,所以相對比較複雜一點:
    <string-array name="feed_icons">
        <item>@color/colorAccent</item>
        <item>@dimen/dp_72</item>
        <item>@drawable/leak_canary_icon</item>
        <item>@drawable/flashligth_drawable_btn</item>
        <item>@drawable/ic_launcher_background</item>
        <item>@layout/encode</item>
    </string-array>
    @BindArray(R.array.icons) TypedArray icons;

@BindBitmap
繫結圖片操作

@BindBitmap(R.drawable.logo) Bitmap logo;
@BindBitmap(R.mipmap.ic_launcher) Bitmap mBitmap;

@BindBool
繫結boolean型別的值

//首先要在資原始檔中定義bool,
<bool name="isRun" >true</bool>

@BindBool(R.bool.isRun) Boolean isRun;
//不過一般情況下是不這樣進行使用的

@BindColor
繫結顏色的操作,同樣顏色可以繫結一個單一的顏色,同樣也可以繫結顏色的組合形式,

//繫結單一顏色:
//定義顏色型別:
<color name="colorAccent">#FF4081</color>
@BindColor(R.color. colorAccent) int colorAccent;

//繫結顏色組合:使用ColorStateList類進行操作,定義一個button_view的selector
<?xml version="1.0" encoding="utf-8"?>  
<selector xmlns:android="http://schemas.android.com/apk/res/android">  
            <item android:state_pressed="true"  
          android:color="#ffff0000"/> <!-- pressed -->  
        <item android:state_focused="true"  
          android:color="#ff0000ff"/> <!-- focused -->  
            <item android:color="#ff000000"/> <!-- default -->  
</selector>

//這樣就可以繫結ColorStateList,然後通過空間的setTextColor進行設定
@BindColor(R.color. button_view) ColorStateList greenSelector

@BindDimen

繫結尺寸,尺寸有int型別和float型別

@BindDimen(R.dimen.sp_20) int gapxp;
@BindDimen(R.dimen.sp_20) float gapsp;

@BindDrawable
Drawable的繫結

@BindDrawable(R.drawable.placeholder) Drawable placeholder
//還有另外一種形式,即drawable與attr共同使用,attr為特性,tint為著色的意思
@BindDrawable(value = R.drawable.placeholder, tint = R.attr.colorAccent) 
Drawable tintedPlaceholder;

@BindFloat 繫結float型別

<item name="whatever" format="float" type="dimen">1.1</item>
<item name="twelve_point_two" format="float" type="dimen">12.2</item>
    BindFloat(R.dimen.image_ratio) float imageRatio
BindFloat(R.dimen. twelve_point_two) float twelve_point_two

@BindFont
繫結的是字型樣式

BindFont(R.font.comic_sans) Typeface comicSans;
通過setTypeFace的形式進行設定樣式

@BindInt
繫結int型別

BindInt(R.int.columns) int columns;

@BindString
繫結String型別的資源

@BindString(R.string.app_name) String appName;

@BindView
將控制元件的id與檢視進行繫結操作

@BindView(R.id.showArrayText)  TextView mView;

@BindViews

BindViews其實是BindView的一種擴充套件形式,主要適用於有多個相同的view的時候,這個時候可以使用BindViews將其裝裝載在一個集合/陣列中,這樣在使用的時候,直接通過下標的形式進行使用

BindViews({ R.id.title, R.id.subtitle }) List<TextView> titlesList;
BindViews({ R.id.title, R.id.subtitle }) TextView [] titlesArray;
//然後通過titlesList.get(1)或者titlesArray [1]來進行獲取
事件觸發註解標籤的使用形式,12個On系列

@ OnCheckedChanged
這個屬性主要是針對類似RadioButton,CheckedBox,其父類為CompoudButton型別的控制元件
主要可以有以下三種形式:注意:函式名字其實是可以隨機取值的,只是形式引數有兩種形式

//第一種:形式引數中只有一個boolean
@OnCheckedChanged(R.id.checkBox)
    void onChecked(boolean isChecked){
        Toast.makeText(this, "checkBox:"+isChecked, Toast.LENGTH_SHORT).show();
}

//第二種:形式引數中有控制元件CompoundButton和boolean
@OnCheckedChanged(R.id.checkBox2)
void onCheckedChanged(CompoundButton compoundButton,boolean isChecked){
        Toast.makeText(this, "compoundButton111:"+compoundButton+"\tcheckBox:"+isChecked, Toast.LENGTH_SHORT).show();
}

//第三種形式為ID具有兩個,這個時候如果沒有檢視繫結,預設是會進行繫結操作的
@OnCheckedChanged({R.id.checkBox3,R.id.checkBox4})
void onCheckedChanged2(CompoundButton compoundButton,boolean isChecked){
        Toast.makeText(this, "compoundButton2222:"+compoundButton+"\tcheckBox:"+isChecked, Toast.LENGTH_SHORT).show();
}

@ OnClick
OnClick很簡單,其實也就是點選事件,只是在ButterKnife中點選事件中的引數可以沒有,也可以有一個View型別,,沒有繫結View,也可以生效

@OnClick(R.id.example) 
    void onClick() {
        Toast.makeText(this, "Clicked!", Toast.LENGTH_SHORT).show();
    }

@OnClick({R.id.topText,R.id.bottom_text})
    void onTextViewClick(View mView){
        Toast.makeText(BufferKnifeDemoActicity.this,((TextView)mView).getText().toString(),Toast.LENGTH_LONG).show();
}

//如果是在自定義view中,類似,這個時候,就不用指定具體的ID了。
Class customBtn extends Button{
        @Onclick
        public void onClick(View view){
    }
}

@ OnEditorAction
OnEditorAction主要是針對EditText來說的,需要注意的是 setOnEditorActionListener這個方法,並不是在我們點選EditText的時候觸發,也不是在我們對EditText進行編輯時觸發,而是在我們編輯完之後點選軟鍵盤上的回車鍵才會觸發
因為EditText是繼承自TextView,因此在實際過程中,也是可以有TextView返回的

@OnEditorAction(R.id.example) boolean onEditorAction(KeyEvent key) {
    Toast.makeText(this, "Pressed: " + key, Toast.LENGTH_SHORT).show();
    return true;
 }

@OnEditorAction(R.id.example) boolean onEditorAction(TextView mTextView,int actionId,KeyEvent key) {
    Toast.makeText(this, "Pressed: " + key, Toast.LENGTH_SHORT).show();
    return true;
 }

//其具體使用的方式可以參考OnEditorActionListener的使用

@ OnFocusChange

此註解主要是來源於setOnFocusChangeListener,即其主要的作用同樣也是針對EditText來進行使用的

@OnFocusChange(R.id.example) void onFocusChanged(boolean focused) {
        Toast.makeText(this, focused?"Gainedfocus": "Lost focus", Toast.LENGTH_SHORT).show();
  }

//同時還有一種兩個引數是的函式形式
@OnFocusChange(R.id.example) void onFocusChanged(View mView,boolean focused) {
        Toast.makeText(this, focused?"Gainedfocus": "Lost focus", Toast.LENGTH_SHORT).show();
 }

@ OnItemClick
這個註解主要是針對AdapterView,即類似ListView這樣的控制元件使用的事件型別,貌似在實際RecycleView不是繼承與AdapterView,因此不能使用此註解

//第一種形式:引數只有一個position
    @OnItemClick(R.id.example_list) void onItemClick(int position) {
        Toast.makeText(this, "Clicked position " + position + "!", Toast.LENGTH_SHORT).show();
  }

//第二種形式:引數有四個
    @OnItemClick(R.id.example_list) void onItemClick(AdapterView mView,View itemView,int position,long num) {
        Toast.makeText(this, "Clicked position " + position + "!", Toast.LENGTH_SHORT).show();
  }

@ OnItemLongClick
這個註解主要是針對AdapterView,即類似ListView這樣的控制元件使用的事件型別,貌似在實際RecycleView不是繼承與AdapterView,因此不能使用此註解

//第一種形式:引數只有一個position
    @ OnItemLongClick (R.id.example_list) void onItemClick(int position) {
        Toast.makeText(this, "Clicked position " + position + "!", Toast.LENGTH_SHORT).show();
  }

//第二種形式:引數有四個
    @OnItemLongClick(R.id.example_list) 
void onItemClick(AdapterView mView,View itemView,int position,long num) {
        Toast.makeText(this, "Clicked position " + position + "!", Toast.LENGTH_SHORT).show();
  }

@ OnItemSelected
針對的控制元件也是AdapterView的子類,即ListView以及Spinner這一類,這個註釋的使用與OnItemSelectedListener的使用方式類似

@OnItemSelected(R.id.example_list) void onItemSelected(int position) {
            Toast.makeText(this, "Selected position " + position + "!", Toast.LENGTH_SHORT).show();
    }


@OnItemSelected(R.id.example_list) void onItemSelected(android.widget.AdapterView view, android.view.View mview, int position, long id) {
            Toast.makeText(this, "Selected position " + position + "!", Toast.LENGTH_SHORT).show();
    }


@OnItemSelected(value = R.id.example_list, callback = NOTHING_SELECTED)
    void onNothingSelected() {
        Toast.makeText(this, "Nothing selected!", Toast.LENGTH_SHORT).show();
    }

@ OnLongClick
OnLongClick很簡單,其實也就是點選事件,只是在ButterKnife中點選事件中的引數可以沒有,也可以有一個View型別,,沒有繫結View,也可以生效

@OnLongClick (R.id.example) 
    void onClick() {
        Toast.makeText(this, " OnLongClick!", Toast.LENGTH_SHORT).show();
    }

@OnLongClick ({R.id.topText,R.id.bottom_text})
    void onTextViewClick(View mView){
        Toast.makeText(BufferKnifeDemoActicity.this,((TextView)mView).getText().toString(),Toast.LENGTH_LONG).show();
}

@ OnPageChange
OnPageChange註解主要是針對ViewPager來進行實現的,ViewPager中有OnPageChangeListener啊


//使用方式一:
OnPageChange(R.id.example_pager) void onPageSelected(int position) {
  Toast.makeText(this, "Selected " + position + "!", Toast.LENGTH_SHORT).show();
}

//使用方式二:
@OnPageChange(value = R.id.example_pager, callback = PAGE_SCROLL_STATE_CHANGED)
void onPageStateChanged(int state) {
    Toast.makeText(this, "State changed: " + state + "!", Toast.LENGTH_SHORT).show();
}

//使用方式三:
@OnPageChange(value = R.id.example_pager, callback = PAGE_SCROLLED)
void onPageScrolled (int position, float positionOffset, int positionOffsetPixels) {

}
//主要是通過列舉型別,PAGE_SCROLLED,PAGE_SCROLL_STATE_CHANGED,PAGE_SELECTED來進行判斷使用的

@ OnTextChanged
主要是針對TextWatcher控制元件來進行實現的。

//使用方式一:
@OnTextChanged(R.id.example) void onTextChanged(CharSequence text) {
    Toast.makeText(this, "Text changed: " + text, Toast.LENGTH_SHORT).show();
}

//使用方式二:
@OnTextChanged(value = R.id.example, callback = BEFORE_TEXT_CHANGED)
void onBeforeTextChanged(CharSequence text) {
   Toast.makeText(this, "Before text changed: " + text, Toast.LENGTH_SHORT).show();
 }

//使用方式三:
@OnTextChanged(value = R.id.example, callback = AFTER_TEXT_CHANGED )
void onAfterTextChange(Editable editable) {
   Toast.makeText(this, "after text changed: " + text, Toast.LENGTH_SHORT).show();
 }
//與OnPagerChange使用類似,其也是用過列舉變數來進行控制的,BEFORE_TEXT_CHANGED,TEXT_CHANGED,AFTER_TEXT_CHANGED

@ OnTouch
針對所有的View都有的onTouch事件來進行設計的,只要是View的子類都可以使用此註解

//使用方式一:
    @OnTouch(R.id.example) boolean onTouch() {
            Toast.makeText(this, "Touched!", Toast.LENGTH_SHORT).show();
            return false;
    }
//使用方式二,帶引數型別
    @OnTouch(R.id.example) boolean onTouch(View mView,MotionEvent event) {
            Toast.makeText(this, "Touched!", Toast.LENGTH_SHORT).show();
            return false;
    }
//注意點:
    //OnTouch為了與系統的onTouch進行匹配上,即滿足事件傳遞的要求,所以其是有返回值的,也就要求沒和函式都是要有返回值。

@ Optional
預設情況下,@Bind 和listener的注入都是必須的,如果target view沒有被發現,則會報錯. 為了抑制這種行為,可以用@Optional註解來標記field和方法,讓注入變成選擇性的,如果targetView存在,則注入, 不存在,則什麼事情都不做.或者使用 Android’s “support-annotations” library.中的@Nullable來修飾

@Optional @OnClick(R.id.subtitle) void onSubtitleClick() {}
ButterKnife的findById

ButterKnife 也提供了findById函式,通過findById()可以獲取Activity、Dialog、View中的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);
不過以上的一個方法其實已經過時了,官方其實不再建議去使用這幾個方法了

ButterKnife.apply()函式
函式型別:

在ButterKnife這個類中關於apply方法的過載數量高達到12條,由此可以看出來,apply這個函式的重要性。但是這個函式使用來幹嘛的撒???
ButterKnife中的apply()函式對view集合元素或者單個view的Action, Setter和Property進行修改。
要想知道的方式,必須首先看一下它的函式型別引數型別吧:如以下所示:

@UiThread @SafeVarargs 
1:public static <T extends View> void apply(@NonNull List<T> list, @NonNull Action<? super T>... actions)

@UiThread @SafeVarargs 
2:public static <T extends View> void apply(@NonNull T[] array, @NonNull Action<? super T>... actions)

@UiThread
3:public static <T extends View> void apply(@NonNull List<T> list,@NonNull Action<? super T> action)

@UiThread
4:public static <T extends View> void apply(@NonNull T[] array, @NonNull Action<? super T> action)

@UiThread
5:public static <T extends View> void apply(@NonNull List<T> list,@NonNull Action<? super T> action)

6:
@UiThread @SafeVarargs 
public static <T extends View> void apply(@NonNull T view,@NonNull Action<? super T>... actions)

7:
@UiThread
public static <T extends View> void apply(@NonNull T view, @NonNull Action<? super T> action)

8: 
@UiThread
public static <T extends View, V> void apply(@NonNull List<T> list,@NonNull Setter<? super T, V> setter, V value)

9:
@UiThread
public static <T extends View, V> void apply(@NonNull T[] array,@NonNull Setter<? super T, V> setter, V value)

10: 
@UiThread
public static <T extends View, V> void apply(@NonNull T view,@NonNull Setter<? super T, V> setter, V value)

11: 
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) // http://b.android.com/213630
@RequiresApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@UiThread
public static <T extends View, V> void apply(@NonNull List<T> list,@NonNull Property<? super T, V> setter, V value)

12     
@TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) // http://b.android.com/213630
@RequiresApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
@UiThread
public static <T extends View, V> void apply(@NonNull T[] array,@NonNull Property<? super T, V> setter, V value)
引數解釋:

所有的函式其實都圍繞三個引數型別來的:

1:泛型型別為View子類的型別或者泛型陣列或者泛型集合,可以引數可以認為是繫結的View型別
2:Action型別的介面和Setter型別的介面

public interface Action<T extends View> {
    /** Apply the action on the {@code view} which is at {@code index} in the list.*/
        @UiThread
        void apply(@NonNull T view, int index);
  }

/** A setter that can apply a value to a list of views. */
public interface Setter<T extends View, V> {
    /** Set the {@code value} on the {@code view} which is at {@code index} in the list. */
    @UiThread
     void set(@NonNull T view, V value, int index);
  }

其實Action介面主要是為了對View或者Views進行管理操作,而Setter介面其實就是對view或者views的屬性或者值進行操作管理

使用案例:
使用方式一:
//1:繫結View
  @Bind({ R.id.first_name, R.id.middle_name, R.id.last_name })  
  List<EditText> nameViews; 

//2:定義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);  
  }  
};

//3:對views呼叫apply,修改views中每個view型別屬性
ButterKnife.apply(nameViews, DISABLE);  
ButterKnife.apply(nameViews, ENABLED, false);

使用方式二:
    對於系統的相關屬性可以直接系統預設的屬性常量進行設定
    ButterKnife.apply(nameViews, View.ALPHA, 0.0f);

程式碼混淆方式

-keep class butterknife.** { *; }  
-dontwarn butterknife.internal.**  
-keep class **$$ViewBinder { *; }  

-keepclasseswithmembernames class * {  
    @butterknife.* <fields>;  
}  

-keepclasseswithmembernames class * {  
    @butterknife.* <methods>;  
} 

以上就是關於
ButterKnife的使用,如果對你的學習有幫助的話,點個關注撒。後續從原始碼角度分析這種繫結的機制