我最喜歡的框架,沒有之一:
編譯期生成代碼的方式,對運行時沒有任何反作用。
加上AndroidStudio快捷鍵,簡直好用之至。java
implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
## butterknife start -keep class butterknife.** { *; } -dontwarn butterknife.internal.** -keep class **$$ViewBinder { *; } -keepclasseswithmembernames class * { @butterknife.* <fields>; } -keepclasseswithmembernames class * { @butterknife.* <methods>; } ## butterknife end
public class MainActivity extends AppCompatActivity { @BindView(R.id.id_tv) TextView mIdTv;//綁定視圖 @BindView(R.id.id_btn) Button mIdBtn; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //1.綁定Activity ButterKnife.bind(this); } @OnClick(R.id.id_btn)//單機事件 public void onViewClicked() { Log.e(TAG, "onViewClicked: "); } @OnLongClick(R.id.id_btn)//長按事件 public boolean onViewLongClicked() { Log.e(TAG, "onViewLongClicked: "); //和原生同樣,返回true,擡起時不觸發單機 return true; } }
@OnClick({R.id.id_btn, R.id.imageView}) public void onViewClicked(View view) { switch (view.getId()) { case R.id.id_btn: break; case R.id.imageView: break; } }
2、Fragment中使用:android
public class MyFragment extends Fragment { @BindView(R.id.imageView) ImageView mImageView; Unbinder unbinder; @Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { View view = inflater.inflate(R.layout.fg_test, container, false); //綁定View unbinder = ButterKnife.bind(this, view); return view; } @Override public void onDestroyView() { super.onDestroyView(); unbinder.unbind(); } //銷燬時解綁View @OnClick(R.id.imageView) public void onViewClicked() { Toast.makeText(getContext(), "hello", Toast.LENGTH_LONG).show(); } }
附:使用Fragmentgit
FragmentTransaction ft = getSupportFragmentManager().beginTransaction(); ft.add(R.id.id_fl,new MyFragment()); ft.commit();
還有其餘不少註解,感受都沒用太大用,下面看一下源碼是怎麼工做的github
bind有6個重載的方法:這裏使用的是一參Activity的bind方法編程
@NonNull @UiThread public static Unbinder bind(@NonNull Activity target) { //獲取Activity對應窗口上的最頂端佈局 View sourceView = target.getWindow().getDecorView(); //調用createBinding方法,見--B1 return createBinding(target, sourceView); }
這算一個很是核心的方法,6個bind()方法都是調用它微信
private static Unbinder createBinding(@NonNull Object target, @NonNull View source) { //獲取target的class Class<?> targetClass = target.getClass(); if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName()); //獲取綁定Class的構造函數,見--B2 Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); //若是構造函數是空的,返回EMPTY的Unbinder枚舉 if (constructor == null) { return Unbinder.EMPTY; } //noinspection TryWithIdenticalCatches Resolves to API 19+ only type. try { //返回使用構造函數建立MainActivity_ViewBinding實例: return constructor.newInstance(target, source); } catch (IllegalAccessException e) { throw new RuntimeException("Unable to invoke " + constructor, e); } catch (InstantiationException e) { throw new RuntimeException("Unable to invoke " + constructor, e); } catch (InvocationTargetException e) { Throwable cause = e.getCause(); if (cause instanceof RuntimeException) { throw (RuntimeException) cause; } if (cause instanceof Error) { throw (Error) cause; } throw new RuntimeException("Unable to create binding instance.", cause); } }
經過字節碼文件獲取類的構造函數app
@Nullable @CheckResult @UiThread private static Constructor<? extends Unbinder> findBindingConstructorForClass(Class<?> cls) { //BINDINGS的聲明:可見是一個LinkedHashMap,以class爲鍵,構造函數爲值。 //static final Map<Class<?>, Constructor<? extends Unbinder>> BINDINGS = new LinkedHashMap<>(); //從map中拿傳入的cls的構造函數 Constructor<? extends Unbinder> bindingCtor = BINDINGS.get(cls); //若是不爲空 if (bindingCtor != null) { if (debug) Log.d(TAG, "HIT: Cached in binding map."); //就返回拿到的構造函數 return bindingCtor; } //不然,獲取字節碼文件的名字:如:com.toly1994.butterknifetest.MainActivity String clsName = cls.getName(); //若是名字的字符串,是以android.或java.開頭的 if (clsName.startsWith("android.") || clsName.startsWith("java.")) { if (debug) Log.d(TAG, "MISS: Reached framework class. Abandoning search."); //返回空 return null; } try { //加載com.toly1994.butterknifetest.MainActivity_ViewBinding類生成Clazz對象bindingClass:見:--B3 Class<?> bindingClass = cls.getClassLoader().loadClass(clsName + "_ViewBinding"); //noinspection unchecked //獲取自動生成的MainActivity_ViewBinding中的兩參構造函數 bindingCtor = (Constructor<? extends Unbinder>) bindingClass.getConstructor(cls, View.class); if (debug) Log.d(TAG, "HIT: Loaded binding class and constructor."); } catch (ClassNotFoundException e) { if (debug) Log.d(TAG, "Not found. Trying superclass " + cls.getSuperclass().getName()); bindingCtor = findBindingConstructorForClass(cls.getSuperclass()); } catch (NoSuchMethodException e) { throw new RuntimeException("Unable to find binding constructor for " + clsName, e); } //將cls和獲取到的構造函數放入map BINDINGS.put(cls, bindingCtor); return bindingCtor; }
Butter Knife會自動建立這個類,咱們來看看它的廬山真面目框架
可見bind方法,主要是把XxxActivity建立一個XxxActivity_ViewBinding,並建立一個XxxActivity_ViewBinding對象ide
// Generated code from Butter Knife. Do not modify! public class MainActivity_ViewBinding implements Unbinder { //持有一個MainActivity的引用 private MainActivity target; //持有一個View的引用 private View view2131165244; //一參構造:調用兩參構造 @UiThread public MainActivity_ViewBinding(MainActivity target) { this(target, target.getWindow().getDecorView()); } //兩參構造: @UiThread public MainActivity_ViewBinding(final MainActivity target, View source) { //target賦值 this.target = target; View view; //將target對象中的mIdTv賦值爲:findRequiredViewAsType(視圖,id,字段介紹,類名)方法:見--B4 target.mIdTv = Utils.findRequiredViewAsType(source, R.id.id_tv, "field 'mIdTv'", TextView.class); //findRequiredView找到按鈕,見:--B4-1 view = Utils.findRequiredView(source, R.id.id_btn, "field 'mIdBtn' and method 'onViewClicked'"); //view強轉後爲target對象中的mIdBtn賦值 target.mIdBtn = Utils.castView(view, R.id.id_btn, "field 'mIdBtn'", Button.class); view2131165244 = view; //爲按鈕設置監聽:見--B5 view.setOnClickListener(new DebouncingOnClickListener() { @Override public void doClick(View p0) { //這裏是調用的target也就是Activity中的onViewClicked方法 //應該知道爲何能夠簡便的寫點擊事件了吧 target.onViewClicked(); } }); } @Override @CallSuper //解綁:置空操做 public void unbind() { MainActivity target = this.target; if (target == null) throw new IllegalStateException("Bindings already cleared."); this.target = null; target.mIdTv = null; target.mIdBtn = null; view2131165244.setOnClickListener(null); view2131165244 = null; } }
根據類型查詢須要的View
這個who只是在拋異常的時候告訴你,是誰異常函數
public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who, Class<T> cls) { //findRequiredView(視圖,id,字段介紹):見--B4-1 View view = findRequiredView(source, id, who); //castView(視圖,id,字段, Class):見--B4-2 return castView(view, id, who, cls); }
看到findViewById有沒有小激動
public static View findRequiredView(View source, @IdRes int id, String who) { //真正的findViewById操做 View view = source.findViewById(id); if (view != null) { //若是視圖不爲空就返回找到的視圖 return view; } //視圖爲空,就拋出一個IllegalStateException異常: String name = getResourceEntryName(source, id); throw new IllegalStateException("Required view '" + name + "' with ID " + id + " for " + who + " was not found. If this view is optional add '@Nullable' (fields) or '@Optional'"
public static <T> T castView(View view, @IdRes int id, String who, Class<T> cls) { try { //將View強轉爲T類型,T類型是Class<T>中的泛型,即findRequiredViewAsType中傳入的類型 return cls.cast(view); } catch (ClassCastException e) { String name = getResourceEntryName(view, id); throw new IllegalStateException("View '" + name + "' with ID " + id + " for " + who + " was of the wrong type. See cause for more info.", e); } }
cast()方法是Clazz的一個公共方法:由下可見它反會一個由傳入值強轉成的T類型對象
@SuppressWarnings("unchecked") public T cast(Object obj) { if (obj != null && !isInstance(obj)) throw new ClassCastException(cannotCastMsg(obj)); return (T) obj; }
繼承自:View.OnClickListener
public abstract class DebouncingOnClickListener implements View.OnClickListener { //是否可用 static boolean enabled = true; //能夠再次使用 private static final Runnable ENABLE_AGAIN = new Runnable() { @Override public void run() { enabled = true; } }; @Override public final void onClick(View v) { //若是可用 if (enabled) { //設置爲不可用 enabled = false; // v.post(ENABLE_AGAIN); doClick(v);//模板方法 } } public abstract void doClick(View v); }
[1]本文由張風捷特烈原創,轉載請註明
[2]歡迎廣大編程愛好者共同交流
[3]我的能力有限,若有不正之處歡迎你們批評指證,一定虛心改正
[4]你的喜歡與支持將是我最大的動力
更多安卓技術歡迎訪問:安卓技術棧
個人github地址:歡迎star
簡書首發,騰訊雲+社區同步更新
張風捷特烈我的網站,編程筆記請訪問:http://www.toly1994.com
QQ:1981462002
郵箱:1981462002@qq.com
微信:zdl1994328