Android中布局通过xutils注解方式源码剖析
来源:互联网 发布:亚加装饰 知乎 编辑:程序博客网 时间:2024/06/04 18:37
首先,如果我们自己不想用setContentView()和findViewById的方式加载布局和查找控件,那思路必须是要有控件的id,然后通过反射加载方法,实现控件的初始化。
很自然,我们会在用到setContenView时使用一个我们自己写的方法,这个自己写的方法的参数要有布局的id,还要有activity,在通过反射实现即可,在想一下,如果在这个方法中,将所有成员变量view初始化,岂不更妙,思路就是通过反射获取field,再得到field的注解的属性值,在通过view = activity.findviewbyid(注解的value),field.set(activity,view)完成初始化。
通过这个例子开始理解
public class YunBooksActivity extends AppCompatActivity { @ViewInject(R.id.gridView) private GridView gridView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_yun_books); x.view().inject(this); }}
我们看到gridview上加了个注解@ViewInject(R.id.gridView),通过上一篇博客,我们可以知道有一个注解类@ViewInject,它有一个值是R.id.gridView,很明显,是个整型。
再看x.view().inject(this),x.view()代码如下
public static ViewInjector view() { if (Ext.viewInjector == null) { ViewInjectorImpl.registerInstance(); } return Ext.viewInjector;}
注意这里的viewInjector不是@ViewInject,这是个接口,实现的类是ViewInjectorImpl。Ext是x的一个静态内部类,代码如下
public static class Ext { private static boolean debug; private static Application app; private static TaskController taskController; private static HttpManager httpManager; private static ImageManager imageManager; private static ViewInjector viewInjector; private Ext() { } static { TaskControllerImpl.registerInstance(); // 默认信任所有https域名 HttpsURLConnection.setDefaultHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String hostname, SSLSession session) { return true; } }); } public static void init(Application app) { if (Ext.app == null) { Ext.app = app; } } public static void setDebug(boolean debug) { Ext.debug = debug; } public static void setTaskController(TaskController taskController) { if (Ext.taskController == null) { Ext.taskController = taskController; } } public static void setHttpManager(HttpManager httpManager) { Ext.httpManager = httpManager; } public static void setImageManager(ImageManager imageManager) { Ext.imageManager = imageManager; } public static void setViewInjector(ViewInjector viewInjector) { Ext.viewInjector = viewInjector; } }
可以看到Ext有个成员变量viewInject,
ViewInjectorImpl.registerInstance(); //就是给Ext的静态成员设置ViewInjector再看ViewInjectorImpl.regiserInstance()
public static void registerInstance() { if (instance == null) { synchronized (lock) { if (instance == null) { instance = new ViewInjectorImpl(); } } } x.Ext.setViewInjector(instance); }
通过x.view()得到viewInjecor,之后是viewInjecorImpl.inject(this)
@Override public void inject(Activity activity) { //获取Activity的ContentView的注解 Class<?> handlerType = activity.getClass(); try { ContentView contentView = findContentView(handlerType); if (contentView != null) { int viewId = contentView.value(); if (viewId > 0) { Method setContentViewMethod = handlerType.getMethod("setContentView", int.class); setContentViewMethod.invoke(activity, viewId); } } } catch (Throwable ex) { LogUtil.e(ex.getMessage(), ex); } injectObject(activity, handlerType, new ViewFinder(activity)); }在这个方法中,我们看到了通过反射执行方法,setContentView,activity传进来了,再看viewId如何获取的,通过一个findContentView(handlerType)获取到contentView,大概可以知道ContentView也是一个自定义的注解,里面的属性value,就是布局的id。在这个演示的例子中,没有使用,想要使用,很简单在当前类加个注解@ContentView(R.layout.xxx),则在activity中就可以不用使用setContentView()了。再看这个方法,findContentView(handlerType)
/** * 从父类获取注解View */ private static ContentView findContentView(Class<?> thisCls) { if (thisCls == null || IGNORED.contains(thisCls)) { return null; } ContentView contentView = thisCls.getAnnotation(ContentView.class); if (contentView == null) { return findContentView(thisCls.getSuperclass()); } return contentView; }
如果thisCls为空,返回空,如果IGNORED中包含thisCls,返回空,IGNORED包含的值有Object.class和Activity.class,因为这里使用了递归,如果当前activity没有使用@ContentView,判断父类有没有使用,如果有,获取返回,如果没有,一直递归直到父类是activity,同时为了方法不限于activity这个家族,还使用了Object,使该方法有更大的通用性。获取到这个contentview后,就通过反射调用setContentView设置布局。
接着有injectObject(activity,handlerType,new ViewFider(activity)),开始为成员变量view赋值
private static void injectObject(Object handler, Class<?> handlerType, ViewFinder finder) { if (handlerType == null || IGNORED.contains(handlerType)) { return; } // inject view Field[] fields = handlerType.getDeclaredFields(); if (fields != null && fields.length > 0) { for (Field field : fields) { Class<?> fieldType = field.getType(); if ( // 不注入静态字段 Modifier.isStatic(field.getModifiers()) || // 不注入final字段 Modifier.isFinal(field.getModifiers()) || // 仅注入view字段 (!fieldType.isInterface() && !View.class.isAssignableFrom(fieldType))) { continue; } ViewInject viewInject = field.getAnnotation(ViewInject.class); if (viewInject != null) { try { View view = finder.findViewById(viewInject.value(), viewInject.parentId()); if (view != null) { field.setAccessible(true); field.set(handler, view); } else { throw new RuntimeException("Invalid id(" + viewInject.value() + ") for @ViewInject!" + handlerType.getSimpleName()); } } catch (Throwable ex) { LogUtil.e(ex.getMessage(), ex); } } } }
这里就是得到每个控件的注解,得到id值,通过反射给控件设置值,field.set(handler,view),这个view又是如何获取的?也很简单,在类ViewFinder中
public View findViewById(int id, int pid) { View pView = null; if (pid > 0) { pView = this.findViewById(pid); } View view = null; if (pView != null) { view = pView.findViewById(id); } else { view = this.findViewById(id); } return view; }
如果有父控件,先获取父控件,在获取view,否则直接获取view。
直到这里获取控件结束,初始化布局初步完成,当然在
private static void injectObject(Object handler, Class<?> handlerType, ViewFinder finder)方法中还有其它的操作,比如完成带注解的方法,此次不在讨论,只用了解加载布局和控件的流程即可
下面是ContentView和ViewInject代码
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface ContentView { int value();}
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)public @interface ViewInject { int value(); /* parent view id */ int parentId() default 0;}
其实还是调用了setContView和findviewbyid,只是通过了一种框架的方式,简化了开发时的代码量,更简洁。
1 0
- Android中布局通过xutils注解方式源码剖析
- Android通过Xutils注解实例化以及事件绑定
- 注解初始化控件(XUtils方式)
- 深入理解Spring 之 源码剖析AOP(注解方式)
- android xutils 注解 include 标签
- xutils,了解注解和实现方式
- 剖析Android线性布局中 Layout_weight(权重)
- android中xutils配置
- xUtils框架中关于注解的使用
- Android中通过注解代替findViewById方法
- Android中通过注解代替findViewById方法
- Android中通过注解代替findViewById方法
- Android中通过注解代替findViewById方法
- Android中Toast使用总结,源码剖析
- 【Mybatis源码剖析】Spring中获取 Mybatis Mapper接口(注解Autowired),并调用过程剖析
- Android中注解详解,通过注解代替findViewById方法
- xUtils如何通过注解对FindViewById进行封装
- 通过注解方式向Dao中注入Sessionfactory
- How to install install gcc 4.8 under ubuntu operating system
- "我在这,你在哪"为啥手机地图定位总不准?
- 简简单单学TI 多核DSP(2):TMS320C6678的时钟配置
- 前向星式prim模板
- Android数据存储
- Android中布局通过xutils注解方式源码剖析
- Structural Types
- linux 下如何处理命令行参数 getopt函数
- abs
- ios学习--ios9 之 ReplayKit框架入门
- 眨眼检测
- [编程题] 小易的升级之路
- |Tyvj|NOIP1999|动态规划|贪心|P1878 拦截导弹
- listview如何实现分页加载