零、前言

我最喜欢的框架,没有之一:
编译期生成代码的方式,对运行时没有任何副作用。
加上AndroidStudio快捷键,简直好用之至。

添加依赖:
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

一、Activity中使用

1.基础使用:
public class MainActivity extends AppCompatActivity {@BindView(R.id.id_tv)TextView mIdTv;//绑定视图@BindView(R.id.id_btn)Button mIdBtn;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//1.绑定ActivityButterKnife.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;}
}

二、Fragment中使用:

public class MyFragment extends Fragment {@BindView(R.id.imageView)ImageView mImageView;Unbinder unbinder;@Nullable@Overridepublic View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,@Nullable Bundle savedInstanceState) {View view = inflater.inflate(R.layout.fg_test, container, false);//绑定Viewunbinder = ButterKnife.bind(this, view);return view;}@Overridepublic void onDestroyView() {super.onDestroyView();unbinder.unbind();}//销毁时解绑View@OnClick(R.id.imageView)public void onViewClicked() {Toast.makeText(getContext(), "hello", Toast.LENGTH_LONG).show();}
}

附:使用Fragment

FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.add(R.id.id_fl,new MyFragment());
ft.commit();

还有其他很多注解,感觉都没用太大用,下面看一下源码是怎么工作的


三、源码浅析:

1、首先来看这句话都进行了哪些事:ButterKnife.bind(this);

---B0:butterknife.ButterKnife#bind(android.app.Activity)

bind有6个重载的方法:这里使用的是一参Activity的bind方法

  @NonNull @UiThreadpublic static Unbinder bind(@NonNull Activity target) {//获取Activity对应窗口上的最顶端布局View sourceView = target.getWindow().getDecorView();//调用createBinding方法,见--B1return createBinding(target, sourceView);}
--B1:butterknife.ButterKnife#createBinding

这算一个非常核心的方法,6个bind()方法都是调用它

private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {//获取target的classClass<?> targetClass = target.getClass();if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName());//获取绑定Class的构造函数,见--B2Constructor<? 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);}}
--B2:butterknife.ButterKnife#findBindingConstructorForClass

通过字节码文件获取类的构造函数

 @Nullable @CheckResult @UiThreadprivate 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.MainActivityString 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:见:--B3Class<?> 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和获取到的构造函数放入mapBINDINGS.put(cls, bindingCtor);return bindingCtor;}
--B3:见鬼,哪来的什么MainActivity_ViewBinding让我加载?

Butter Knife会自动创建这个类,我们来看看它的庐山真面目

MainActivity_ViewBinding.png

可见bind方法,主要是把XxxActivity创建一个XxxActivity_ViewBinding,并创建一个XxxActivity_ViewBinding对象


2、现在焦点就在MainActivity_ViewBinding的身上

com.toly1994.butterknifetest.MainActivity_ViewBinding
// Generated code from Butter Knife. Do not modify!public class MainActivity_ViewBinding implements Unbinder {
//持有一个MainActivity的引用private MainActivity target;
//持有一个View的引用private View view2131165244;//一参构造:调用两参构造@UiThreadpublic MainActivity_ViewBinding(MainActivity target) {this(target, target.getWindow().getDecorView());}//两参构造:@UiThreadpublic MainActivity_ViewBinding(final MainActivity target, View source) {//target赋值this.target = target;View view;//将target对象中的mIdTv赋值为:findRequiredViewAsType(视图,id,字段介绍,类名)方法:见--B4target.mIdTv = Utils.findRequiredViewAsType(source, R.id.id_tv, "field 'mIdTv'", TextView.class);//findRequiredView找到按钮,见:--B4-1view = 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;//为按钮设置监听:见--B5view.setOnClickListener(new DebouncingOnClickListener() {@Overridepublic 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;}
}
--B4:butterknife.internal.Utils#findRequiredViewAsType

根据类型查询需要的View
这个who只是在抛异常的时候告诉你,是谁异常

  public static <T> T findRequiredViewAsType(View source, @IdRes int id, String who,Class<T> cls) {//findRequiredView(视图,id,字段介绍):见--B4-1View view = findRequiredView(source, id, who);//castView(视图,id,字段, Class):见--B4-2return castView(view, id, who, cls);}
--B4-1:findRequiredView(视图,id,字段介绍)

看到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'"
--B4-2:castView(视图,id,字段, Class)
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;}
--B5:这是butterknife源码中的一个类:

继承自: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.声明:

[1]本文由张风捷特烈原创,转载请注明
[2]欢迎广大编程爱好者共同交流
[3]个人能力有限,如有不正之处欢迎大家批评指证,必定虚心改正
[4]你的喜欢与支持将是我最大的动力

2.连接传送门:

更多安卓技术欢迎访问:安卓技术栈
我的github地址:欢迎star
简书首发,腾讯云+社区同步更新
张风捷特烈个人网站,编程笔记请访问:http://www.toly1994.com

3.联系我

QQ:1981462002
邮箱:1981462002@qq.com
微信:zdl1994328

4.欢迎关注我的微信公众号,最新精彩文章,及时送达:
公众号.jpg

转载于:https://www.cnblogs.com/toly-top/p/9781872.html

O3-开源框架使用之Butterknife 8.8.1及源码浅析相关推荐

  1. 【2021软件创新实验室暑假集训】SpringMVC框架(设计原理、简单使用、源码探究)

    系列文章目录 20级 Java篇 [2021软件创新实验室暑假集训]计算机的起源与大致原理 [2021软件创新实验室暑假集训]Java基础(一) [2021软件创新实验室暑假集训]Java基础(二) ...

  2. 计算机毕业设计ssm基于SSM框架的中医养生系统i9830系统+程序+源码+lw+远程部署

    计算机毕业设计ssm基于SSM框架的中医养生系统i9830系统+程序+源码+lw+远程部署 计算机毕业设计ssm基于SSM框架的中医养生系统i9830系统+程序+源码+lw+远程部署 本源码技术栈: ...

  3. 基于SpringBoot+Mybaits框架开发的OA自动化办公系统Java源码

    源码介绍 办公自动化(OA)是面向组织的日常运作和管理,员工及管理者使用频率最高的应用系统,极大提高公司的办公效率.基于SpringBoot+Mybaits框架开发的OA自动化办公系统Java源码,基 ...

  4. dz index.php 编写,Thinkphp5.0.24框架开发仿DZ应用平台资源站源码

    [温馨提示]源码包解压密码:www.youhutong.com 资源描述 Thinkphp5.0.24框架开发仿DZ应用平台资源站源码 源码无任何加密,可用来做资源下载站,素材资源站,交易站等等... ...

  5. 计算机毕业设计ssm基于SSM框架的宿舍管控平台6z76b系统+程序+源码+lw+远程部署

    计算机毕业设计ssm基于SSM框架的宿舍管控平台6z76b系统+程序+源码+lw+远程部署 计算机毕业设计ssm基于SSM框架的宿舍管控平台6z76b系统+程序+源码+lw+远程部署 本源码技术栈: ...

  6. java ssm框架的点歌系统的设计与实现源码

    项目名称 java ssm框架的点歌系统的设计与实现源码 下载地址 下载地址 系统说明 4.2 系统功能 4.2.1 登录与注册功能 系统的登录分为了前台登录和后台登录两个模块,都分别处在不同的界面上 ...

  7. 2022年最新视觉框架VM PRO 2.7版本,C#源码框架,机器视觉源码框架

    2022年最新视觉框架VM PRO 2.7版本,C#源码框架,机器视觉源码框架,编程语言C#,算法使用的是halcon,参考了cognex visionpro的输入输出,有C#基础和Halcon基础学 ...

  8. 计算机毕业设计JAVA基于Bootstrap框架的读书网站设计与实现mybatis+源码+调试部署+系统+数据库+lw

    计算机毕业设计JAVA基于Bootstrap框架的读书网站设计与实现mybatis+源码+调试部署+系统+数据库+lw 计算机毕业设计JAVA基于Bootstrap框架的读书网站设计与实现mybati ...

  9. java毕业设计基于Bootstrap框架的读书网站设计与实现mybatis+源码+调试部署+系统+数据库+lw

    java毕业设计基于Bootstrap框架的读书网站设计与实现mybatis+源码+调试部署+系统+数据库+lw java毕业设计基于Bootstrap框架的读书网站设计与实现mybatis+源码+调 ...

  10. QT界面免费版开源图片转文字工具程序完整版附源码

    QT界面免费版开源图片转文字工具程序完整版附源码 需求源码的朋友请留言 操作步骤如下:

最新文章

  1. 带动画效果的卷积神经网络的讲解.pptx
  2. iOS实现自定义的弹出视图(popView)
  3. 单端 平衡 音质区别_听上去「高大上」的平衡接口,到底有什么门道?
  4. HDU-1874 畅通工程续 (最短路径启蒙题)
  5. android 小游戏源码_Python入门太难?不如从玩塔防小游戏开始,玩通关就能学会编程...
  6. JSP-BUG-The type java.xx.xx cannot be resolved
  7. 一文足以了解什么是 Java 中的锁
  8. jquery里判断数组内是否包含了指定的值或元素的方法
  9. 20220525商汤算法岗实习面试经历
  10. CSS布局:多种方案实现固定页脚(sticky footer)
  11. js 数组按奇偶拆分_js数组拆分问题
  12. 一.关于实现浏览器弹窗提示内容几秒后自动消失重定向执行其他函数的两种方法
  13. Mybatis中使用大于小于等于的正确方法
  14. 反汇编系列(一)——工具篇
  15. 表情包制作APP的相关推荐 可以制作表情包的软件
  16. VS2017无法调试Unity
  17. cocos2dx3.17.2之梦幻西游开发日志(三)
  18. 02华为大数据HCIE_Data Mining 数学基础 测试一下
  19. 疯狂鼹鼠 java,神话延续! 光滑镜面时尚MOTO轻薄W220详尽评测
  20. excel两个指标相关性分析_如何用excel分析两组数据的相关性

热门文章

  1. 多目标优化问题中常见分解方法的理解
  2. 解决 required a bean of type ‘com.aliyun.oss.OSSClient‘ that could not be found
  3. const char *p与char * const p区别
  4. Well-ordered String
  5. 【Python】Talk Python To Me Podcast播客
  6. Codeforces Gym 100015H Hidden Code 暴力
  7. python小白系列1
  8. Exploiting Deep Generative Prior for Versatile Image Restoration and Manipulation
  9. c51单片机矩阵键盘1602计算器_基于矩阵键盘1602液晶屏的简易计算器的设计系统-电子信息科学与技术课程设计说明书.doc...
  10. (63)计数器设计(递增计数器)