Android面试基础(一)IOC(DI)框架(ViewUtils)讲解_反射和自定义注解类

1. Android中的IOC(DI)框架

1.1 ViewUtils简介(xUtils中的四大部分之一)

  • IOC: Inverse of Controller 控制反转。

  • DI: Dependency Inject 依赖注入

  • 完全注解方式就可以进行UI绑定和事件绑定。

  • 无需findViewById和setClickListener等。

1.2 ViewUtils使用

1compile 'org.xutils:xutils:3.5.0' 2compile 'com.jiechic.library:xUtils:2.6.14' 3 4// xUtils的view注解要求必须提供id,以使代码混淆不受影响。 5@ViewInject(R.id.textView) 6TextView textView; 7 8//@ViewInject(vale=R.id.textView, parentId=R.id.parentView) 9//TextView textView; 10 11@ResInject(id = R.string.label, type = ResType.String) 12private String label; 13 14// 取消了之前使用方法名绑定事件的方式,使用id绑定不受混淆影响 15// 支持绑定多个id @OnClick({R.id.id1, R.id.id2, R.id.id3}) 16// or @OnClick(value={R.id.id1, R.id.id2, R.id.id3}, parentId={R.id.pid1, R.id.pid2, R.id.pid3}) 17// 更多事件支持参见ViewCommonEventListener类和包com.lidroid.xutils.view.annotation.event。 18@OnClick(R.id.test_button) 19public void testButtonClick(View v) { // 方法签名必须和接口中的要求一致 20 ... 21} 22... 23//在Activity中注入: 24@Override 25public void onCreate(Bundle savedInstanceState) { 26 super.onCreate(savedInstanceState); 27 setContentView(R.layout.main); 28 ViewUtils.inject(this); //注入view和事件 29 ... 30 textView.setText("some text..."); 31 ... 32} 33//在Fragment中注入: 34@Override 35public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 36 View view = inflater.inflate(R.layout.bitmap_fragment, container, false); // 加载fragment布局 37 ViewUtils.inject(this, view); //注入view和事件 38 ... 39} 40//在PreferenceFragment中注入: 41public void onActivityCreated(Bundle savedInstanceState) { 42 super.onActivityCreated(savedInstanceState); 43 ViewUtils.inject(this, getPreferenceScreen()); //注入view和事件 44 ... 45} 46// 其他重载 47// inject(View view); 48// inject(Activity activity) 49// inject(PreferenceActivity preferenceActivity) 50// inject(Object handler, View view) 51// inject(Object handler, Activity activity) 52// inject(Object handler, PreferenceGroup preferenceGroup) 53// inject(Object handler, PreferenceActivity preferenceActivity)

1.3 反射:调用(设置)类私有成员和私有方法

定义的User类:

1package cn.Douzi.Annotation_Test; 2 3public class User { 4 5 @ViewInject(age=23, name="张三") 6 private String name; 7 8 private int age; 9 10 private String eat(String eat) { 11 System.out.println("eat: " + eat); 12 return eat + "真好吃"; 13 } 14 15 @Override 16 public String toString() { 17 // TODO Auto-generated method stub 18 return "User [name =" + name + ", age = " + age + "]"; 19 } 20}

反射调用私有对象,设置该类对象:

1Class cls = User.class; 2 3 /** 4 * 2. 将这个字节码中的name字段获取到 5 */ 6// cls.getField(name); //这个方法只能获取声明为public的字段 7 Field declaredField = cls.getDeclaredField("name"); 8 Field declaredFieldAge = cls.getDeclaredField("age");      declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射 9 declaredFieldAge.setAccessible(true); 10 11 //给user对象的declaredField设置为name 12 declaredField.set(user, name); 13 declaredFieldAge.set(user, age);

反射调用私有函数,设置该类函数:

1 //通过反射调用User对象的eat方法 2 Method declaredMethod = cls.getDeclaredMethod("eat", String.class); 3 //暴力反射调用改方法 4 declaredMethod.setAccessible(true); 5 Object result = declaredMethod.invoke(user, "牛肉拉面"); # 设置函数参数列表 6 7 System.out.println(result);

1.4 反射 和 自定义注解类

自定义注解类:

1package cn.Douzi.Annotation_Test; 2 3import java.lang.annotation.ElementType; 4import java.lang.annotation.Retention; 5import java.lang.annotation.RetentionPolicy; 6import java.lang.annotation.Target; 7 8import javax.lang.model.element.Element; 9 10/** 11 * @Taget(ElementType.METHOD) //用于方法 12 * 13 */ 14@Target(ElementType.FIELD) //用于限制当前自定义注解类作用的对象 15//@Retention(RetentionPolicy.SOURCE) //该注解只会作用在源码阶段,当源码编译成字节码时注解信息就被清除 16//@Retention(RetentionPolicy.CLASS) //该注解只会作用在字节码阶段,但是当虚拟机加载这个字节码时,注解信息被清除 17@Retention(RetentionPolicy.RUNTIME) //该注解类一直保留到加载到虚拟机中 18public @interface ViewInject { 19 20 int age(); 21 String name(); 22 23} 24 25package cn.Douzi.Annotation_Test; 26 27public class User { 28 29 @ViewInject(age=23, name="张三") 30 private String name; 31 32 private int age; 33 34 private String eat(String eat) { 35 System.out.println("eat: " + eat); 36 return eat + "真好吃"; 37 } 38 39 @Override 40 public String toString() { 41 // TODO Auto-generated method stub 42 return "User [name =" + name + ", age = " + age + "]"; 43 } 44}

1.5 测试自定义注解类

如何获取自定义注解对象(User)中参数

1Class cls = User.class; 2 3 /** 4 * 2. 将这个字节码中的name字段获取到 5 */ 6// cls.getField(name); //这个方法只能获取声明为public的字段 7 Field declaredField = cls.getDeclaredField("name"); 8 Field declaredFieldAge = cls.getDeclaredField("age"); 9 10 /** 11 * 3. 将当前字段上的注解对象获取到 12 */ 13 ViewInject viewInject = declaredField.getAnnotation(ViewInject.class); 14 if (viewInject != null) { 15 /* 16 * 4.获取自定义注解对象中的参数 17 */ 18 int age = viewInject.age(); 19 String name = viewInject.name(); 20 System.out.println("name: " + name + ", age = " + age); 21 }

测试是否能获取到注解对象(完整代码)

1package cn.Douzi.Annotation_Test; 2 3import java.lang.reflect.Field; 4 5public class AnnotationMainTest { 6 7 public static void main(String[] args) throws NoSuchFieldException, SecurityException { 8 9 //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性 10 //将name设置给User对象的name属性 11 User user = new User(); 12 /** 13 * 反射: 14 * 1. 先去获取到User的字节码 15 */ 16// user.getClass(); 17// User.class 18// Class.forName("xxxx") 19 Class cls = User.class; 20 21 /** 22 * 2. 将这个字节码中的name字段获取到 23 */ 24// cls.getField(name); //这个方法只能获取声明为public的字段 25 Field declaredField = cls.getDeclaredField("name"); 26 27 /** 28 * 3. 将当前字段上的注解对象获取到 29 */ 30 ViewInject viewInject = declaredField.getAnnotation(ViewInject.class); 31 if (viewInject != null) { 32 /* 33 * 4.获取自定义注解对象中的参数 34 */ 35 int age = viewInject.age(); 36 String name = viewInject.name(); 37 System.out.println("name: " + name + ", age = " + age); 38 } 39 else { 40 System.out.println("字段上无自定义注解"); 41 } 42 43 } 44 45}

1.6 通过暴力反射将获取到的注解值设置给User对象 (完整代码)

1package cn.Douzi.Annotation_Test; 2 3import java.lang.reflect.Field; 4 5public class AnnotationMainTest { 6 7 public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException { 8 9 //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性 10 //将name设置给User对象的name属性 11 User user = new User(); 12 /** 13 * 反射: 14 * 1. 先去获取到User的字节码 15 */ 16// user.getClass(); 17// User.class 18// Class.forName("xxxx") 19 Class cls = User.class; 20 21 /** 22 * 2. 将这个字节码中的name字段获取到 23 */ 24// cls.getField(name); //这个方法只能获取声明为public的字段 25 Field declaredField = cls.getDeclaredField("name"); 26 Field declaredFieldAge = cls.getDeclaredField("age"); 27 28 /** 29 * 3. 将当前字段上的注解对象获取到 30 */ 31 ViewInject viewInject = declaredField.getAnnotation(ViewInject.class); 32 if (viewInject != null) { 33 /* 34 * 4.获取自定义注解对象中的参数 35 */ 36 int age = viewInject.age(); 37 String name = viewInject.name(); 38 System.out.println("name: " + name + ", age = " + age); 39 40 /* 41 * 5. 通过反射将这个两个值设置给User对象 42 */ 43 declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射 44 declaredFieldAge.setAccessible(true); 45 46 //给user对象的declaredField设置为name 47 declaredField.set(user, name); 48 declaredFieldAge.set(user, age); 49 50 System.out.println(user.toString()); 51 52 53 } 54 else { 55 System.out.println("字段上无自定义注解"); 56 } 57 58 } 59 60}

1.7 通过反射调用User对象的eat方法 (完整代码)

1package cn.Douzi.Annotation_Test; 2 3import java.lang.reflect.Field; 4import java.lang.reflect.InvocationTargetException; 5import java.lang.reflect.Method; 6 7public class AnnotationMainTest { 8 9 public static void main(String[] args) throws NoSuchFieldException, SecurityException, IllegalArgumentException, IllegalAccessException, NoSuchMethodException, InvocationTargetException { 10 11 //获取User类中name字段上的自定义注解的值,然后将该值的age通过反射设置给User对象的age属性 12 //将name设置给User对象的name属性 13 User user = new User(); 14 /** 15 * 反射: 16 * 1. 先去获取到User的字节码 17 */ 18// user.getClass(); 19// User.class 20// Class.forName("xxxx") 21 Class cls = User.class; 22 23 /** 24 * 2. 将这个字节码中的name字段获取到 25 */ 26// cls.getField(name); //这个方法只能获取声明为public的字段 27 Field declaredField = cls.getDeclaredField("name"); 28 Field declaredFieldAge = cls.getDeclaredField("age"); 29 30 /** 31 * 3. 将当前字段上的注解对象获取到 32 */ 33 ViewInject viewInject = declaredField.getAnnotation(ViewInject.class); 34 if (viewInject != null) { 35 /* 36 * 4.获取自定义注解对象中的参数 37 */ 38 int age = viewInject.age(); 39 String name = viewInject.name(); 40 System.out.println("name: " + name + ", age = " + age); 41 42 /* 43 * 5. 通过反射将这个两个值设置给User对象 44 */ 45 declaredField.setAccessible(true); //设置允许访问,其实就是允许暴力反射 46 declaredFieldAge.setAccessible(true); 47 48 //给user对象的declaredField设置为name 49 declaredField.set(user, name); 50 declaredFieldAge.set(user, age); 51 52 System.out.println(user.toString()); 53 } 54 else { 55 System.out.println("字段上无自定义注解"); 56 } 57 58 //通过反射调用User对象的eat方法 59 Method declaredMethod = cls.getDeclaredMethod("eat", String.class); 60 //暴力反射调用改方法 61 declaredMethod.setAccessible(true); 62 Object result = declaredMethod.invoke(user, "牛肉拉面"); 63 64 System.out.println(result); 65 } 66 67}

2. 自定义ViewUtils (Android)

布局文件:

1<?xml version="1.0" encoding="utf-8"?> 2<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 xmlns:tools="http://schemas.android.com/tools" 4 android:layout_width="match_parent" 5 android:layout_height="match_parent" 6 android:orientation="vertical" 7 tools:context="com.douzi.myviewutils.MainActivity"> 8 9 <TextView 10 android:id="@+id/tv1" 11 android:layout_width="wrap_content" 12 android:layout_height="wrap_content" 13 android:text="Hello World!!!!" 14 android:textSize="20dp" 15 /> 16 17 <TextView 18 android:id="@+id/tv2" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:text="Hello World2222222222!!!!" 22 android:textSize="20dp" 23 /> 24 25 26 <Button 27 android:id="@+id/btn" 28 android:layout_width="match_parent" 29 android:layout_height="wrap_content" 30 android:text="OKKKK" 31 /> 32 33</LinearLayout>

注解类:

1package com.douzi.myviewutils; 2 3import java.lang.annotation.ElementType; 4import java.lang.annotation.Retention; 5import java.lang.annotation.RetentionPolicy; 6import java.lang.annotation.Target; 7 8/** 9 * Created by Douzi on 2019/3/11. 10 */ 11 12@Retention(RetentionPolicy.RUNTIME) 13@Target(ElementType.FIELD) 14public @interface ViewInject { 15 16 int value(); //这里必须叫value,否则绑定的时候需要写: xx=R.id.xx 17 18}

实现 ViewUtils的inject方法(使用反射)

1package com.douzi.myviewutils; 2 3import android.app.Activity; 4import android.view.View; 5 6import java.lang.reflect.Field; 7 8/** 9 * Created by Douzi on 2019/3/11. 10 */ 11 12public class ViewUtils { 13 14 public static void inject(Activity activity) { 15 16 //绑定控件 17 try { 18 bindView(activity); 19 } catch (IllegalAccessException e) { 20 e.printStackTrace(); 21 } 22 23 } 24 25 private static void bindView(Activity activity) throws IllegalAccessException { 26 /** 27 * 1. 获取Activity的字节码 28 */ 29 Class clazz = activity.getClass(); 30 /** 31 * 2. 获取到该字节码中所有的Filed 32 */ 33 Field[] declaredFields = clazz.getDeclaredFields(); 34 35 /** 36 * 3. 判断哪些是我们想要的字段(只有添加了ViewInject注解的字段) 37 */ 38 for (Field field : declaredFields) { 39 //获取字段上面的注解 40 ViewInject viewInject = field.getAnnotation(ViewInject.class); 41 if (viewInject != null) { 42 /** 43 * 4. 获取当前注解的值 44 */ 45 int resId = viewInject.value(); 46 /** 47 * 5. 通过调用Activity的findViewById方法,获取当前id为resId的控件 48 */ 49 View view = activity.findViewById(resId); 50 /** 51 * 6. 将当前的View设置给当前的Filed 52 */ 53 field.setAccessible(true); 54 //给Activity对象的filed字段设置值为view对象 55 field.set(activity, view); 56 57 } 58 else { 59 // nothing 60 } 61 } 62 } 63 64}

测试注解的功能

1package com.douzi.myviewutils; 2 3import android.support.v7.app.AppCompatActivity; 4import android.os.Bundle; 5import android.widget.TextView; 6 7public class MainActivity extends AppCompatActivity { 8 9 @ViewInject(R.id.tv1) 10 TextView textView; 11 12 @ViewInject(R.id.tv2) 13 TextView textView2; 14 15 private int count; 16 17 @Override 18 protected void onCreate(Bundle savedInstanceState) { 19 super.onCreate(savedInstanceState); 20 setContentView(R.layout.activity_main); 21 22 ViewUtils.inject(this); 23 24 textView.setText("成功了!!"); 25 textView2.setText("成功了2!!"); 26 27 } 28}

绑定点击事件实现(实现ViewUtils的OnClick实现)

注解类

1package com.douzi.myviewutils; 2 3import java.lang.annotation.ElementType; 4import java.lang.annotation.Retention; 5import java.lang.annotation.RetentionPolicy; 6import java.lang.annotation.Target; 7 8/** 9 * Created by Douzi on 2019/3/11. 10 */ 11 12@Target(ElementType.METHOD) 13@Retention(RetentionPolicy.RUNTIME) 14public @interface OnClick { 15 16 int value(); 17 18}

ViewUtils里添加函数

1public static void bindOnClick(final Activity activity) { 2 /** 3 * 1. 获取字节码 4 */ 5 Class clazz = activity.getClass(); 6 /** 7 * 2. 获取字节码中所有的方法 8 */ 9 Method[] methods = clazz.getDeclaredMethods(); 10 11 /** 12 * 3. 遍历方法, 找出方法上声明了OnClick注解的方法 13 */ 14 for (final Method method : methods) { 15 16 /** 17 * 4. 获取当前方法的上的注解 18 */ 19 OnClick onClick = method.getAnnotation(OnClick.class); 20 21 if (onClick != null) { 22 /** 23 * 5. 获取注解中的值 24 */ 25 int resId = onClick.value(); 26 /** 27 * 6. 获取到id为resId的View 28 */ 29 final View view = activity.findViewById(resId); 30 /** 31 * 7. 将当前的View绑定点击事件 32 */ 33 view.setOnClickListener(new View.OnClickListener() { 34 @Override 35 public void onClick(View v) { 36 /** 37 * 8. 通过反射调用当前的用户的方法 38 */ 39 //设置可以 40 method.setAccessible(true); 41 42 try { 43 method.invoke(activity, view); 44 } catch (IllegalAccessException e) { 45 e.printStackTrace(); 46 } catch (InvocationTargetException e) { 47 e.printStackTrace(); 48 } 49 } 50 }); 51 } 52 } 53 54 } 55 56public static void inject(Activity activity) { 57 58 //绑定控件 59 try { 60 //绑定界面 61 bindView(activity); 62 63 //绑定方法 64 bindOnClick(activity); 65 } catch (IllegalAccessException e) { 66 e.printStackTrace(); 67 } 68 69 }

测试注解功能

1package com.douzi.myviewutils; 2 3import android.support.v7.app.AppCompatActivity; 4import android.os.Bundle; 5import android.view.View; 6import android.widget.TextView; 7import android.widget.Toast; 8 9public class MainActivity extends AppCompatActivity { 10 11 @ViewInject(R.id.tv1) 12 TextView textView; 13 14 @ViewInject(R.id.tv2) 15 TextView textView2; 16 17 private int count; 18 19 @Override 20 protected void onCreate(Bundle savedInstanceState) { 21 super.onCreate(savedInstanceState); 22 setContentView(R.layout.activity_main); 23 24 ViewUtils.inject(this); 25 26 textView.setText("成功了!!"); 27 textView2.setText("成功了2!!"); 28 29 } 30 31 @OnClick(R.id.btn) 32 private void clickMe(View view) { 33 Toast.makeText(this, "点击我了", Toast.LENGTH_LONG).show(); 34 } 35 36}

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )