Android-ButterKnife不能注解RatingBar(含ButterKnife部分原理以及View传递机制)
来源:互联网 发布:组合最优化 理论与算法 编辑:程序博客网 时间:2024/05/16 07:56
继续把之前的安卓外包的一些东西写出来。
文章结构:(1)RatingBar基本使用(自定义样式);(2)ButterKnife不能注解RatingBar以及在项目解决的方式;(3)ButterKnife原理以及View机制原理
一、RatingBar基本使用(自定义样式):
(1)一个style样式:
<style name="roomRatingBar" parent="@android:style/Widget.RatingBar"> <item name="android:progressDrawable">@drawable/room_rating_bar</item> <item name="android:minHeight">24dip</item> <item name="android:maxHeight">24dip</item> </style>
(2)一个drawable资源:
<?xml version="1.0" encoding="utf-8"?><layer-list xmlns:android="http://schemas.android.com/apk/res/android"><!-- 第一张图是设置未选中,第二张图是设置半个星星,第三张图则是设置为整个星星。也就是说我们要自己去找三张这样的图咯--> <item android:id="@android:id/background" android:drawable="@drawable/rating_small_empty" /> <item android:id="@android:id/secondaryProgress" android:drawable="@drawable/rating_small_half" /> <item android:id="@android:id/progress" android:drawable="@drawable/rating_small_full" /></layer-list>
(3)调用:
<RatingBar android:id="@+id/ratingBar" style="@style/roomRatingBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginLeft="8dp" android:layout_marginTop="9dp" android:isIndicator="true" />
剩下的就是最基本无聊的 findViewById() 和 setOnClickListener() 等代码了。(非butterknife方式)
二、ButterKnife不能注解RatingBar以及在项目解决的方式:(DEMO见下面给出的源码)
//如果像平常这样使用butterknife去注解它,它是构造不了视图的,进而渲染不了,出异常。@BindView(R.id.retingbar) RatingBar retingbar;
解决的方式:使用以下方式去初始化它:
一会会讲解此原理
LayoutInflater layoutInflater = LayoutInflater.from(getActivity()); View view = layoutInflater.inflate(R.layout.fragment_ability, null); ratingBar = (RatingBar) view.findViewById(R.id.ratingBar);
三、ButterKnife原理以及View机制原理
遇深坑就了解机制和看源码:
ButterKnife 用了Java Annotation Processing技术,就是在Java代码编译成Java字节码的时候就已经处理了 @Bind 、 @OnClick (ButterKnife还支持很多其他的注解)这些注解了。
也就是自定义注解嘛。资料如下:(其实还是javase的内容)
Java Custom Annotations
Java Annotations
根据资料,自定义注解我们需要知道两个基础的元注解@Retention 和 @Target.
ButterKnife 工作流程:参考ButterKnife框架原理,感谢那位博主。
1.明确一个:ButterKnife 中所有的注解都使用 Retention 为 CLASS 保留。
2.当你编译你的Android工程时,ButterKnife工程中 ButterKnifeProcessor 类的 process() 方法会执行以下操作:
开始它会扫描Java代码中所有的ButterKnife注解 @Bind 、 @OnClick 、 @OnItemClicked 等
当它发现一个类中含有任何一个注解时, ButterKnifeProcessor 会帮你生成一个Java类,名字类似 $$ViewBinder ,这个新生成的类实现了 ViewBinder 接口
这个 ViewBinder 类中包含了所有对应的代码,比如 @Bind 注解对应 findViewById() , @OnClick 对应了 view.setOnClickListener() 等等
最后当Activity启动 ButterKnife.bind(this) 执行时,ButterKnife会去加载对应的 ViewBinder 类调用它们的 bind() 方法。
//每个控件的初始化大致生成这样的代码:public class ExampleActivity$$ViewBinder<T extends io.bxbxbai.samples.ui.ExampleActivity> implements ViewBinder<T> { @Override public void bind(final Finder finder, final T target, Object source) { View view; view = finder.findRequiredView(source, 21313618, “field ‘user’”); target.username = finder.castView(view, 21313618, “field ‘user’”); view = finder.findRequiredView(source, 21313618, “field ‘pass’”); target.password = finder.castView(view, 21313618, “field ‘pass’”); view = finder.findRequiredView(source, 21313618, “field ‘submit’ and method ‘submit’”); view.setOnClickListener( new butterknife.internal.DebouncingOnClickListener() { @Override public void doClick(android.view.View p0) { target.submit(); } }); } @Override public void reset(T target) { target.username = null; target.password = null; }}
(3)然后,执行 bind 方法时,我们会调用 ButterKnife.bind(this)
activity调用:
@Nullable @Override public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { //第一个参数就是要加载的布局id,第二个参数是指给该布局的外部再嵌套一层父布局,如果不需要就直接传null。这样就成功成功创建了一个布局的实例 //一会讲解此原理 View view = inflater.inflate(getLayoutId(), container, false); ButterKnife.bind(this, view);//传一个上下文,以及取出来的父view return view; }
源码:
//参数传至此@NonNull @UiThread public static Unbinder bind(@NonNull Object target, @NonNull View source) { return createBinding(target, source); }private static Unbinder createBinding(@NonNull Object target, @NonNull View source) {//先拿到那个上下文 Class<?> targetClass = target.getClass(); if (debug) Log.d(TAG, "Looking up binding for " + targetClass.getName()); // 再拿到上下文所包含的那个视图构造器 Constructor<? extends Unbinder> constructor = findBindingConstructorForClass(targetClass); if (constructor == null) { return Unbinder.EMPTY; } //noinspection TryWithIdenticalCatches Resolves to API 19+ only type. try { //传递给视图构造器去渲染绘制 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); } }
(4)正式加载以及注入
ButterKnife会加载$$ViewBinder.java 类
然后调用 ViewBinder 的 bind 方法,动态注入 该activity类中所有的View属性和
如果Activity中有 @OnClick 注解的方法,ButterKnife会在 ViewBinder 类中给View设置onClickListener,并且将 @OnClick 注解的方法传入其中
注意:
@Bind 、 @OnClick 等注解标注的属性或方法必须是public或protected的,因为ButterKnife是通过 得到上下文跟view然后它预编译的一个类 来注入View的。
为什么要这样呢?有些注入框架比如roboguice你是可以把View设置成private的,答案就是 性能 。如果你把View设置成private,那么框架必须通过反射来注入View,不管现在手机的CPU处理器变得多快,如果有些操作会影响性能,那么是肯定要避免的,这就是ButterKnife与其他注入框架的不同。
接下来,我们来认识下inflate机制:
不过在此之前,我们先理解下android界面架构层,也就是先来讲下view机制:
Android的控件氛围两类:ViewGroup和View控件。ViewGroup控件作为父控件可包含多个View,并管理包含的View控件。通过ViewGroup,我们可以清晰地知道我们的view继承树。
上层控件负责下层子控件的测量和绘制,并传递交互事件。通常在Activity中使用的findViewById()方法买就是在控件树中以树的深度优先遍历来查找对应元素。在每棵控件树的顶部,都有一个ViewParent对象,就是整棵树的控制核心,所有的交互管理事件都交由它来同意调度和分配,从而实现对整个视图进行整体控制。
每个Activity都包含一个window对象,在android中window对象通常由phonewindow来实现,将一个DecorView设置为整个应用窗口的根View。DecorView作为窗口界面的顶层视图,封装了一些窗口的操作通用方法(比如获取屏幕宽度和高度)。DrcorView将要显示的具体内容呈现在PhoneWindow上,这里面的所有View的监听时间,都通过WindowManagerService来进行接收,并通过Activity对象来回调相应的onClickListener。
在显示上,它DrcorView将屏幕屏幕分成两部分,一个是TitleView,另一个是ContentView。
ContentView一个ID是content的Framelayout。
在群英传中有个问题,我觉得解析的不够清晰。–为什么调用requestWindowFeature()方法一定要在调用setContentView()方法之前才可以生效的原因。
原因:
1. 明确setContentView()方法。调用此方法后,ActivityManagerService会回调onResume()方法,这是系统会把整个DecorView添加到PhoneWindow中,并其显示,绘制出界面。
也就是说,此方法调用后会立刻通知 Service去测量视图绘制视图。
2.requestWindowFeature()方法是把标题栏去掉,也就是把TitleView给去掉,也就是要通知给ActivityManagerService告诉它不要测量绘制titleview
如果requestWindowFeature方法在setContentView方法后调用,那么requestWindowFeature将没有机会告诉ActivityManagerService不要去测量绘制titleview,但它确实把这个requestWindowFeature事件强行加入到处理队列而又不能处理,所以会报错!!
好,我们继续了解inflate机制:
先读下源码:有四种的,不过我只讲下面这种,因为多是调用此方法,最终把结果返回给LayoutInflater构建视图。不过我会推荐一些文章给大家。
//返回值View:返回的值是View指向的根节点。/* 第一个参数resource。也就说根据其他几个方法传进来的xml布局文件在这里会被用传进来的parser进行解析 第二个参数root:表示根结点,它的作用主要取决于第三个参数 第三个参数attachToRoot:表示是否将转换后的VIEW直接添加在根结点上,如果是TRUE,那么在转换出来VIEW之后,内部直接会调用root.addView()来将其添加到root结点上,然后返回根结点,当然是我们传进去的ROOT结点。如果设为FALSE,那只会将XML布局转换成对应的VIEW,不会将其添加的我们传进去的root结点上。*//* 1. 如果root为null,attachToRoot将失去作用,设置任何值都没有意义。 2. 如果root不为null,attachToRoot设为true,则会给加载的布局文件的指定一个父布局,即root。 3. 如果root不为null,attachToRoot设为false,则会将布局文件最外层的所有layout属性进行设置,当该view被添加到父view当中时,这些layout属性会自动生效。 4. 在不设置attachToRoot参数的情况下,如果root不为null,attachToRoot参数默认为true。*/public View inflate(@LayoutRes int resource, @Nullable ViewGroup root, boolean attachToRoot) { final Resources res = getContext().getResources(); if (DEBUG) { Log.d(TAG, "INFLATING from resource: \"" + res.getResourceName(resource) + "\" (" + Integer.toHexString(resource) + ")"); } final XmlResourceParser parser = res.getLayout(resource); try { return inflate(parser, root, attachToRoot); } finally { parser.close(); } }
所以说,为什么上面,我们初始化RatingBar采用了那种方式呢??
LayoutInflater中的处理是:createViewFromTag()是用于根据节点名来创建View对象的。确实如此,在createViewFromTag()方法的内部又会去调用createView()方法,然后使用反射的方式创建出View的实例并返回。
最终:同样是createViewFromTag()方法来创建View的实例,然后还会在第24行递归调用rInflate()方法来查找这个View下的子元素,每次递归完成后则将这个View添加到父布局当中。
这样的话,把整个布局文件都解析完成后就形成了一个完整的DOM结构,最终会把最顶层的根布局返回,至此inflate()过程全部结束。
补充:郭霖大大有讲过这个inflate机制的:郭霖大大inflate机制
源码下载:Android-多列表的项目Rxjava+Rtrofit+Recyclerview+Glide+Adapter封装
好了,Android-ButterKnife不能注解RatingBar(含ButterKnife部分原理以及View传递机制)讲完了。本博客是这个系列的第三篇,所以讲得一些细节坑。另外,这个系列还有一些我在外包项目过程中做的优化,以及一些发布签名等等技巧,我会尽快出完给大家,分享经验给大家。欢迎在下面指出错误,共同学习!!你的点赞是对我最好的支持!!
更多内容,可以访问JackFrost的博客
0 0
- Android-ButterKnife不能注解RatingBar(含ButterKnife部分原理以及View传递机制)
- Android 中注解view (仿butterknife)
- Android butterknife注解框架
- Android ButterKnife注解框架
- android注解框架--ButterKnife
- java中的注解以及简单了解ButterKnife原理
- ButterKnife(注解)Android Studio插件安装
- android 注解框架butterknifer以及Butterknife Zelezny的安装
- Android ButterKnife 注解框架的使用详解和原理分析
- Android UI注解框架 ButterKnife源码及原理分析
- Android Butterknife框架 注解攻略
- Android butterknife框架 注解攻略
- Android butterknife框架 注解攻略
- Android Butterknife框架 注解攻略:
- Android ButterKnife注解式开发
- Android注解神器ButterKnife使用说明
- android注解框架ButterKnife学习
- Android 之ButterKnife注解使用
- 深入.NET 第七章上机2上机3 员工工作
- scala的list操作
- 在Eclipse中使用JUnit4进行单元测试(中级篇)
- spring mvc 学习文档1
- 【leetcode】347. Top K Frequent Elements
- Android-ButterKnife不能注解RatingBar(含ButterKnife部分原理以及View传递机制)
- sqlserver -getdate()日期格式化大全
- ACM:蓝桥杯:字符串对比
- 在Eclipse中使用JUnit4进行单元测试(高级篇)
- Qt for ARM_Linux环境搭建-Qt5.7+iTop4412嵌入式平台移植
- jqgrid禁用日期(例:2016-01-01---2016-01-07禁用号到7号的所有日期)
- perl入门基础
- 深圳南山首推神器 专治“中国式过马路” ,什么PI玩意
- MySQL