android ButterKnife的简单使用
今天,簡(jiǎn)單講講android里如何使用ButterKnife。
Android框架系列:
一.android EventBus的簡(jiǎn)單使用
二.android Glide簡(jiǎn)單使用
三.android OKHttp的基本使用詳解
四.android RxJava(RxAndroid)的簡(jiǎn)單使用
五.android ButterKnife的簡(jiǎn)單使用
?
前言:
ButterKnife是一個(gè)專注于Android系統(tǒng)的View注入框架,以前總是要寫很多findViewById來找到View對(duì)象,有了ButterKnife可以很輕松的省去這些步驟。是大神JakeWharton的力作,目前使用很廣。最重要的一點(diǎn),使用ButterKnife對(duì)性能基本沒有損失,因?yàn)锽utterKnife用到的注解并不是在運(yùn)行時(shí)反射的,而是在編譯的時(shí)候生成新的class。項(xiàng)目集成起來也是特別方便,使用起來也是特別簡(jiǎn)單。
?
ButterKnife 閃亮登場(chǎng)
ButterKnife 初識(shí)
ButterKnife,又被戲稱為黃油刀,至于為什么被戲稱為這個(gè),大家可以看下面附上的從官方截取的icon~
一塊桌布,一個(gè)盤子,一個(gè)Android小機(jī)器人形狀的黃油,一把刀。這些合起來被大家戲稱為黃油刀。(我說呢,糾結(jié)我半天,都搞不懂黃油刀是個(gè)什么鬼,這次曉得了)
icon下面簡(jiǎn)單解釋就是為Android 視圖(View)提供綁定字段和方法。 也就是說,我們今后可以通過這把刀去替換之前瑣碎的初始化~
?
一、 ButterKnife是啥
注解中相對(duì)簡(jiǎn)單易懂的很不錯(cuò)的開源框架
1. 強(qiáng)大的View綁定和Click事件處理功能,簡(jiǎn)化代碼,提升開發(fā)效率
2. 方便的處理Adaper里的ViewHolder綁定問題
3. 運(yùn)行時(shí)不會(huì)影響app效率,使用配置方便
4. 代碼清晰,可讀性強(qiáng)
github開源地址:
https://github.com/JakeWharton/butterknife
?
?
二、使用步驟
2.1 添加插件
File -> Settings -> Plugins -> 搜索ButterKnife,找到Android ButterKnife Zeleany進(jìn)行安裝重啟AndroidStudio
?
2.2 添加依賴
在對(duì)應(yīng)module的build.gradle里面添加下面的依賴,重新編譯就會(huì)自動(dòng)下載
dependencies {compile 'com.jakewharton:butterknife:8.5.1'annotationProcessor 'com.jakewharton:butterknife-compiler:8.5.1' }注意和Dagger2配合使用需要注意把a(bǔ)nnotationProcessor改為apt。
2.3 在Activity中使用
2.3.1 使用插件
在R.layout.activity_main上面右擊 選擇Generate -> Generate ButterKnife injections,即可選擇 生成view對(duì)應(yīng)的注解,還支持ViewHolder和OnClick
2.3.2 手動(dòng)
下面的代碼把R.id.tv_test這個(gè)TextView定義了為tvTest,注意類型不能為static或者private
@BindView(R.id.tv_test) TextView tvTest;//當(dāng)然寫成一行也是可以 @BindView(R.id.tv_test) TextView tvTest;添加注解@OnClick(R.id.xx)即為點(diǎn)擊方法,對(duì)應(yīng)的View參數(shù)可有可無(wú),如果有多個(gè)參數(shù)可以只加一個(gè)參數(shù)(例如onItemClick有四個(gè)參數(shù),你可以只加一個(gè)int position的參數(shù))。如果參數(shù)錯(cuò)誤,編譯的時(shí)候會(huì)報(bào)錯(cuò)。
@OnClick(R.id.bt_test) public void onClick(View v) {tvTest.setText("被點(diǎn)擊了..."); }如果定義為指定的類型,將會(huì)自動(dòng)轉(zhuǎn)換
@OnClick(R.id.bt_test) public void onClick(Button button) {tvTest.setText("被點(diǎn)擊了..."); }更多事件看下面的 三
2.4 在Fragment中使用
在Fragment中使用和在Activity中使用,只是有一點(diǎn)不一樣:在獲取View的時(shí)候,加多一個(gè)view參數(shù)把View綁定到ButterKnife就行了。
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.my_fragment, container, false);//這里需要加多一個(gè)參數(shù)viewButterKnife.bind(this, view);return view; }Fragment生命周期和activity有點(diǎn)不同,銷毀的時(shí)候可以進(jìn)行解綁
public class MyFragment extends Fragment {@BindView(R.id.bt_test)Button btTest;private Unbinder unbinder;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.my_fragment, container, false);//綁定的時(shí)候返回了一個(gè)Unbinder對(duì)象unbinder = ButterKnife.bind(this, view);return view;}@Overridepublic void onDestroyView() {super.onDestroyView();unbinder.unbind();} }2.5 在Adapter ViewHolder中使用
把ViewHolder加一個(gè)構(gòu)造方法,在new ViewHolder的時(shí)候把view傳遞進(jìn)去。
static class ViewHolder {@BindView(R.id.tv_item_test)TextView tvItemTest;@BindView(R.id.bt_item_test)Button btItemTest;public ViewHolder(View view) {ButterKnife.bind(this, view);} }item里面Button控件的點(diǎn)擊事件也可以使用注解@OnClick來實(shí)現(xiàn),在適配器里面添加就行,如:
//item的button的點(diǎn)擊事件@OnClick(R.id.bt_item_test)void onClick(){Toast.makeText(context,"點(diǎn)擊了按鈕",Toast.LENGTH_SHORT).show();}static class ViewHolder {@BindView(R.id.tv_item_test)TextView tvItemTest;@BindView(R.id.bt_item_test)Button btItemTest;public ViewHolder(View view) {ButterKnife.bind(this, view);}}注意: 如果item里面有Button等這些有點(diǎn)擊的控件事件的,需要設(shè)置這些控件屬性focusable為false
三、 事件注解
一共有12個(gè)
| @OnClick | 點(diǎn)擊事件 |
| @OnCheckedChanged | 選中,取消選中 |
| @OnEditorAction | 軟鍵盤的功能鍵 |
| @OnFocusChange | 焦點(diǎn)改變 |
| @OnItemClick | item被點(diǎn)擊(注意這里有坑,如果item里面有Button等這些有點(diǎn)擊的控件事件的,需要設(shè)置這些控件屬性focusable為false) |
| @OnItemLongClick | item長(zhǎng)按(返回真可以攔截onItemClick) |
| @OnItemSelected | item被選擇事件 |
| @OnLongClick | 長(zhǎng)按事件 |
| @OnPageChange | 頁(yè)面改變事件 |
| @OnTextChanged | EditText里面的文本變化事件 |
| @OnTouch | 觸摸事件 |
| @Optional | 選擇性注入,如果當(dāng)前對(duì)象不存在,就會(huì)拋出一個(gè)異常,為了壓制這個(gè)異常,可以在變量或者方法上加入一下注解,讓注入變成選擇性的,如果目標(biāo)View存在,則注入, 不存在,則什么事情都不做=如下代碼 |
?
三、綁定注解
有11種
| @BindViews | 綁定多個(gè)view id為一個(gè)view的list變量 |
| @BindView | 綁定一個(gè)view id為一個(gè)view 變量 |
| @BindArray | 綁定string里面array數(shù)組,@BindArray(R.array.city ) String[] citys ; |
| @BindBitmap | 綁定圖片資源為Bitmap,@BindBitmap( R.mipmap.wifi ) Bitmap bitmap; |
| @BindBool | 綁定真假boolean |
| @BindColor | 綁定color,@BindColor(R.color.colorAccent) int black; |
| @BindDimen | 綁定Dimen,@BindDimen(R.dimen.borth_width) int mBorderWidth; |
| @BindDrawable | 綁定Drawable,@BindDrawable(R.drawable.test_pic) Drawable mTestPic; |
| @BindFloat | 綁定float |
| @BindInt | 綁定int |
| @BindString | 綁定一個(gè)String id為一個(gè)String變量,@BindString( R.string.app_name ) String meg; |
?
五、 ButterKnife更多使用
4.1 指定多個(gè)id綁定事件
例如綁定多個(gè)Button的onclick事件,在小括號(hào)里面加大括號(hào),然后用逗號(hào)分割即可。
@OnClick({R.id.bt_test,R.id.bt_test_two})public void onClick(View v) {switch (v.getId()){case R.id.bt_test:tvTest.setText("按鈕1被點(diǎn)擊了...");break;case R.id.bt_test_two:tvTest.setText("按鈕2被點(diǎn)擊了...");break;}}4.2 多個(gè)view
使用BindViews綁定多個(gè)
@BindViews({R.id.tv_test,R.id.tv_test_two,R.id.tv_test_three}) List<TextView> tvList;使用Action接口或者Setter接口或者View的Property,可以同時(shí)設(shè)置多個(gè)view的屬性
@BindViews({R.id.tv_test,R.id.tv_test_two,R.id.tv_test_three}) List<TextView> tvList;@Override protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);ButterKnife.bind(this);//設(shè)置多個(gè)view的屬性//方式1 傳遞值ButterKnife.apply(tvList,ENABLED,false);//方式2 指定值ButterKnife.apply(tvList,DISABLE);//方式3 設(shè)置View的PropertyButterKnife.apply(tvList, View.ALPHA, 0.0f);}//Action接口設(shè)置屬性 static final ButterKnife.Action<View> DISABLE = new ButterKnife.Action<View>() {@Override public void apply(View view, int index) {view.setEnabled(false);} };//Setter接口設(shè)置屬性 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);} };4.3 自定義View使用注解事件
不用指定id,直接注解OnClick
public class MyButton extends Button {@OnClickpublic void onClick() {} }4.4 綁定api
使用Activity為根視圖綁定任意對(duì)象時(shí),如果你使用類似MVC的設(shè)計(jì)模式你可以在Activity 調(diào)用ButterKnife.bind(this, activity),來綁定Controller。
使用ButterKnife.bind(this)綁定一個(gè)view的子節(jié)點(diǎn)字段.如果你在子View的布局里或者自定義view的構(gòu)造方法里 使用了inflate,你可以立刻調(diào)用此方法。或者,從XML inflate來的自定義view類型可以在onFinishInflate回調(diào)方法中使用它。
4.5 多方法的listener
例如Spinner的setOnItemSelectedListener,他會(huì)回調(diào)兩個(gè)方法:
mSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {@Overridepublic void onItemSelected(AdapterView<?> parent, View view, int position, long id) {}@Overridepublic void onNothingSelected(AdapterView<?> parent) {} });所以這時(shí)候如果我們需要注解onNothingSelected,需要在注解參數(shù)添加一個(gè)callback
@OnItemSelected(R.id.my_spiner)//默認(rèn)callback為ITEM_SELECTED void onItemSelected(int position) {Toast.makeText(this, "position: " + position, Toast.LENGTH_SHORT).show(); }@OnItemSelected(value = R.id.my_spiner, callback = OnItemSelected.Callback.NOTHING_SELECTED) void onNothingSelected() {Toast.makeText(this, "Nothing", Toast.LENGTH_SHORT).show(); }注意的是Spinner中只要有數(shù)據(jù),默認(rèn)都會(huì)選中第0個(gè)數(shù)據(jù),所以想進(jìn)入到onNothingSelected()方法,就需要把Adapter中的數(shù)據(jù)都清空
?
4.6 使用findById
Butter knife仍然包含了findById方法,用于仍需從一個(gè)view ,Activity,或者Dialog上 find view的時(shí)候。并且它可以自動(dòng)轉(zhuǎn)換類型。
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);4.7 代碼混淆(這個(gè)不用理解,知道有這個(gè)功能就行,因?yàn)槲乙矝]有明白)
-keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep class **$$ViewBinder { *; }-keepclasseswithmembernames class * {@butterknife.* <fields>; }-keepclasseswithmembernames class * {@butterknife.* <methods>; }?
五、 原理
利用了IOC的(Inverse of Controll)控制反轉(zhuǎn)結(jié)構(gòu),04年后改名為DI(dependency injection)依賴注入。目的是為了使類與類之間解耦合,提高系統(tǒng)的可擴(kuò)展性和可維護(hù)性。
?
六、 和Dagger使用注意
搭配Dagger使用的時(shí)候,ButterKnife可能會(huì)不起作用,沒效果,綁定控件為null。這時(shí)候把依賴包的annotationProcessor改為apt 就可以了:
compile 'com.jakewharton:butterknife:8.5.1'//aptapt 'com.jakewharton:butterknife-compiler:8.5.1'?
好了,ButterKnife的基本使用就到這里的。如果只是想知道怎么使用,看到這里就可以了。下面會(huì)講講具體的原理代碼,對(duì)能力有一定的要求,大家自己決定看不看。
?
ButterKnife 語(yǔ)法
1. activity fragment 綁定與 fragment解綁
想要使用ButterKnife,簡(jiǎn)單配置之后,我們還需要在Activity中onCreate()綁定,如下:
@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);// 必須在setContentView()之后綁定ButterKnife.bind(this); }而如果使用fragment,官方給出的綁定以及解綁如下:
public class FancyFragment extends Fragment {@BindView(R.id.button1) Button button1;@BindView(R.id.button2) Button button2;private Unbinder unbinder;@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fancy_fragment, container, false);// 綁定unbinder = ButterKnife.bind(this, view);// TODO Use fields...return view;}@Override public void onDestroyView() {super.onDestroyView();// 解綁unbinder.unbind();} }綁定之后,我們一起來看看,常用的幾種監(jiān)聽通過使用ButterKnife之后,我們又該如何編寫相關(guān)事件呢?別急,往下看~
2.單擊事件
首先我們先看看人家表層提供我們代碼中,我們可以得到哪兒些對(duì)我們有用的信息
首先明確,targetType(目標(biāo)類型)為View,setter為setOnClickListener(單擊事件監(jiān)聽),type為ButterKnife封裝的單擊事件(butterknife.internal.DebouncingOnClickListener),而method中則是name為doClick以及parameters為View類型的倆個(gè)參數(shù);而下面的interface接口中需要我們傳遞一個(gè)id。
簡(jiǎn)單了解后,我們衍生出三種寫法,如下:
// 寫法1@OnClick(控件ID)void 方法名() {//業(yè)務(wù)邏輯操作}// 寫法2@OnClick(控件ID)void 方法名(控件類型) {//業(yè)務(wù)邏輯操作}// 寫法3@OnClick(控件ID)void 方法名(View view) {//業(yè)務(wù)邏輯操作}你可以按照上面指定一個(gè)個(gè)的寫,也可以綁定多個(gè),如官網(wǎng)提供下面寫法
3.長(zhǎng)按事件
同樣依舊看人家怎么寫的,看看我們能了解到什么
和單擊事件對(duì)比,長(zhǎng)按時(shí)間則多出了一個(gè)returnType(返回值),且默認(rèn)為false。So,寫法如下~
// 方法1@OnLongClick(控件ID)boolean 方法名(){// 業(yè)務(wù)邏輯操作return false;}// 方法2@OnLongClick(控件ID)boolean 方法名(控件類型){// 業(yè)務(wù)邏輯操作return false;}// 方法3@OnLongClick(控件ID)boolean 方法名(View view){// 業(yè)務(wù)邏輯操作return false;}4.Checked改變事件
老規(guī)矩:
改變,一般來說,會(huì)提供我們一個(gè)標(biāo)識(shí),去方便我們根據(jù)不同的狀態(tài)去處理不同的邏輯,so…
// 寫法1@OnCheckedChanged(控件ID)void radioButtonCheckChange(boolean isl) {// 業(yè)務(wù)邏輯}// 寫法2@OnCheckedChanged(控件ID)void radioButtonCheckChange(控件類型,boolean isl) {// 業(yè)務(wù)邏輯}5.監(jiān)聽軟鍵盤右下角按鈕事件
老規(guī)矩:
so…經(jīng)過上面幾個(gè)大家可以知道,我們只需要對(duì)parameters以及是否是returnType重點(diǎn)關(guān)注即可。
// 寫法1@OnEditorAction(控件ID)boolean 方法名() {// 業(yè)務(wù)邏輯操作return false;}// 寫法2// code:狀態(tài)碼@OnEditorAction(控件ID)boolean EditTextAction(int code) {// 業(yè)務(wù)邏輯操作return false;}// 寫法3// KeyEvent@OnEditorAction(控件ID)boolean EditTextAction(KeyEvent keyEvent) {// 業(yè)務(wù)邏輯操作return false;}// 寫法4@OnEditorAction(控件ID)boolean EditTextAction(int code, KeyEvent keyEvent) {// 業(yè)務(wù)邏輯操作return false;}// 寫法5@OnEditorAction(控件ID)boolean EditTextAction(TextView textView,int code, KeyEvent keyEvent) {// 業(yè)務(wù)邏輯操作return false;}6. EditText內(nèi)容改變監(jiān)聽事件
由于源碼中內(nèi)容較長(zhǎng),不方便截圖,故截取部分代碼做解析,如下:
@Target(METHOD) @Retention(CLASS) @ListenerClass(targetType = "android.widget.TextView",setter = "addTextChangedListener",remover = "removeTextChangedListener",type = "android.text.TextWatcher", ---> 這里同樣對(duì)之前的TextWatcher做了相關(guān)處理 gggcallbacks = OnTextChanged.Callback.class ---> 自定義枚舉,通過枚舉類型標(biāo)識(shí)當(dāng)前操作 666 ) public @interface OnTextChanged {/** View IDs to which the method will be bound. */@IdRes int[] value() default { View.NO_ID }; ---> 需要傳入ID/** Listener callback to which the method will be bound. */Callback callback() default Callback.TEXT_CHANGED; ---> 未改變狀態(tài)/** {@link TextWatcher} callback methods. */enum Callback { ---> 枚舉中分為三種類似 未改變 改變前 改變后/** {@link TextWatcher#onTextChanged(CharSequence, int, int, int)} */@ListenerMethod(name = "onTextChanged", ---> 當(dāng)前標(biāo)識(shí)為 未改變parameters = {"java.lang.CharSequence", ---> 用戶輸入字符"int", ---> 改變前個(gè)數(shù)"int", ---> 測(cè)試時(shí),返回0,沒整明白代表什么意思"int" ---> 根據(jù)打印結(jié)果,猜測(cè)這個(gè)應(yīng)該是每次增加內(nèi)容個(gè)數(shù)})TEXT_CHANGED,/** {@link TextWatcher#beforeTextChanged(CharSequence, int, int, int)} */@ListenerMethod(name = "beforeTextChanged", ---> 當(dāng)前標(biāo)識(shí)為 改變前parameters = {"java.lang.CharSequence", ---> 用戶輸入字符"int", ---> 改變前個(gè)數(shù)"int","int"})BEFORE_TEXT_CHANGED,/** {@link TextWatcher#afterTextChanged(android.text.Editable)} */@ListenerMethod(name = "afterTextChanged", ---> 當(dāng)前標(biāo)識(shí)為 改變后parameters = "android.text.Editable" ---> 用戶輸入字符)AFTER_TEXT_CHANGED, ---> 我們關(guān)注的重點(diǎn)在此,每次只需要監(jiān)聽這個(gè),去做相關(guān)處理即可}從上得知,關(guān)于EditText內(nèi)容改變事件,我們關(guān)注點(diǎn)只在乎改變后的內(nèi)容格式(個(gè)數(shù))是否符合項(xiàng)目需求,而其他可以暫時(shí)忽略,從而衍生下面寫法:
// 內(nèi)容改變后監(jiān)聽// Editable editable:用戶輸入字符@OnTextChanged(value = 控件ID, callback = 監(jiān)聽類型,改變后取值為:OnTextChanged.Callback.AFTER_TEXT_CHANGED)void editTextChangeAfter(Editable editable) {// 業(yè)務(wù)邏輯}// 內(nèi)容改變前監(jiān)聽@OnTextChanged(value = 控件ID, callback = 監(jiān)聽類型,改變前取值為:OnTextChanged.Callback.BEFORE_TEXT_CHANGED)void editTextChangeBefore(CharSequence s, int start) {// 業(yè)務(wù)邏輯}// 內(nèi)容未發(fā)生改變監(jiān)聽@OnTextChanged(value = 控件ID, callback = 監(jiān)聽類型,取值為:OnTextChanged.Callback.TEXT_CHANGED)void editTextChange(CharSequence s, int start) {// 業(yè)務(wù)邏輯}7. 焦點(diǎn)監(jiān)聽事件
老規(guī)矩:
由此可見,如下:
@OnFocusChange(控件ID)void editTextFocus(boolean isl){// 業(yè)務(wù)邏輯}8. 觸摸監(jiān)聽事件
老規(guī)矩:
寫法如下:
@OnTouch(控件ID)boolean imageView(MotionEvent event){// 業(yè)務(wù)邏輯return false;}?
9. item項(xiàng)單擊監(jiān)聽事件
老規(guī)矩:
so…
@OnItemClick(控件ID)void listItemClick(int position){// 業(yè)務(wù)邏輯}10. item項(xiàng)長(zhǎng)按監(jiān)聽事件
老規(guī)矩:
so…
@OnItemLongClick(R.id.listView)boolean listItemLongClick(int position) {Toast.makeText(this, "OnItemLongClick---點(diǎn)擊了第" + position + "個(gè)", Toast.LENGTH_SHORT).show();return true;}?
簡(jiǎn)單總結(jié)一下,ButterKnife就是一個(gè)框架,用注解來取代了findviewbyid的功能,而且它是編譯器完成的,對(duì)于運(yùn)行沒有任何的效率的影響。使用ButterKnife最好是下載對(duì)應(yīng)的插件Android ButterKnife Zelezny,具體怎么下載,我下一步博客再講。
?
android ButterKnife的簡(jiǎn)單使用就講完了。
?
就這么簡(jiǎn)單。
總結(jié)
以上是生活随笔為你收集整理的android ButterKnife的简单使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: android RxJava(RxAnd
- 下一篇: android 解决错误:Intel H