介绍

这里我带大家来学习一下注解 并且用来写下一个模仿xUtils3 中View框架

此框架 可以省略activity或者fragment的 findViewById 或者设置点击事件的烦恼

我正参加2016CSDN博客之星的比赛 希望您能投下宝贵的一票,点击进入投票

我的github上的源码,包含doc和使用说明

如下代码:

fragment

  1. package a.fmy.com.myapplication;
  2. import android.os.Bundle;
  3. import android.support.v4.app.Fragment;
  4. import android.view.LayoutInflater;
  5. import android.view.View;
  6. import android.view.ViewGroup;
  7. import android.widget.TextView;
  8. import a.fmy.com.mylibrary.FmyClickView;
  9. import a.fmy.com.mylibrary.FmyContentView;
  10. import a.fmy.com.mylibrary.FmyViewInject;
  11. import a.fmy.com.mylibrary.FmyViewView;
  12. //你的fragment的布局id Your fragment's LayoutId
  13. @FmyContentView(R.layout.fragment_blank)
  14. public class BlankFragment extends Fragment {
  15. //你想实例化控件的id
  16. //Do you want to control instance id
  17. // 等价于 findViewByid
  18. //Equivalent to the findViewByid
  19. @FmyViewView(R.id.tv1)
  20. TextView tv1;
  21. @FmyViewView(R.id.tv2)
  22. TextView tv2;
  23. @Override
  24. public View onCreateView(LayoutInflater inflater, ViewGroup container,
  25. Bundle savedInstanceState) {
  26. //初始化fragment Initialize Fragement
  27. return FmyViewInject.injectfragment(this,inflater,container);
  28. }
  29. //你想给哪个控件添加 添加事件 的id
  30. //Do you want to add add event id to which controls
  31. @FmyClickView({R.id.tv1,R.id.tv2})
  32. public void myOnclick(View view){
  33. switch (view.getId()) {
  34. case R.id.tv1:
  35. tv1.setText("TV1 "+Math.random()*100);
  36. break;
  37. case R.id.tv2:
  38. tv2.setText("TV2 "+Math.random()*100);
  39. break;
  40. default:
  41. }
  42. }
  43. }

Activity

  1. package a.fmy.com.myapplication;
  2. import android.os.Bundle;
  3. import android.support.v4.app.FragmentTransaction;
  4. import android.support.v7.app.AppCompatActivity;
  5. import android.widget.FrameLayout;
  6. import a.fmy.com.mylibrary.FmyContentView;
  7. import a.fmy.com.mylibrary.FmyViewInject;
  8. import a.fmy.com.mylibrary.FmyViewView;
  9. @FmyContentView(R.layout.activity_main)
  10. public class MainActivity extends AppCompatActivity {
  11. @FmyViewView(R.id.fl)
  12. FrameLayout fl;
  13. @Override
  14. protected void onCreate(Bundle savedInstanceState) {
  15. super.onCreate(savedInstanceState);
  16. //initActivity
  17. // 初始化activity
  18. FmyViewInject.inject(this);
  19. }
  20. @Override
  21. protected void onResume() {
  22. super.onResume();
  23. FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
  24. fragmentTransaction.add(R.id.fl,new BlankFragment());
  25. fragmentTransaction.commit();
  26. }
  27. }

java注解学习

java注解教学大家点击进入大致的看一下即可 不然我不知道这篇博客需要写多久

activity设置填充布局框架

这里我们先写一个用于activity框架 你学习完了之后其实你也会fragment了.

1. 实现activity不需要调用setContentView(R.layout.activity_main);此方法完成布局填充 我们看下效果

不使用框架:

  1. package a.fmy.com.mylibrary;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.os.Bundle;
  4. public class MainActivity extends AppCompatActivity {
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. setContentView(R.layout.activity_main);
  9. }
  10. }

使用框架:

  1. package a.fmy.com.mylibrary;
  2. import android.support.v7.app.AppCompatActivity;
  3. import android.os.Bundle;
  4. @FmyContentView(R.layout.activity_main)
  5. public class MainActivity extends AppCompatActivity {
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. FmyViewInject.inject(this);
  10. }
  11. }

第一步:

创建一个注解类如下

@Target —>>此注解在什么地方可以使用 如类还是变量

ElementType.TYPE只能在类中使用此注解

@Retention(RetentionPolicy.RUNTIME) 注解可以在运行时通过反射获取一些信息(这里如果你疑惑那么请六个悬念继续向下看)

  1. /**
  2. * 此方注解写于activity类上 可以免去 setContentView()步骤
  3. * @author 范明毅
  4. * @version 1.0
  5. */
  6. @Target(ElementType.TYPE)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. @Documented
  9. public @interface FmyContentView {
  10. /**
  11. * 保存布局文件的id eg:R.layout.main
  12. * @return 返回 布局id
  13. */
  14. int value();
  15. }

第二步:

写一个工具类 配合注解使用 当开发者使用此类时激活注解的作用

  1. public class FmyViewInject {
  2. /**
  3. * 保存传入的activity
  4. */
  5. private static Class<?> activityClass;
  6. /**
  7. * 初始化activity和所有注解
  8. *
  9. * @param obj
  10. * 你需要初始化的activity
  11. */
  12. public static void inject(Object obj) {
  13. }
  14. /**
  15. * 初始化activity布局文件 让其不用调用setContentView
  16. *
  17. * @param activity
  18. */
  19. private static void injectContent(Object obj) {
  20. }
  21. }

大家先不用着急看不懂为什么这样写原因

核心源码位于injectContent 我们来实现此方法

  1. /**
  2. * 初始化activity布局文件 让其不用调用setContentView
  3. *
  4. * @param activity
  5. */
  6. private static void injectContent(Object obj) {
  7. // 获取注解
  8. FmyContentView annotation = activityClass
  9. .getAnnotation(FmyContentView.class);
  10. if (annotation != null) {
  11. // 获取注解中的对应的布局id 因为注解只有个方法 所以@XXX(YYY)时会自动赋值给注解类唯一的方法
  12. int id = annotation.value();
  13. try {
  14. // 得到activity中的方法 第一个参数为方法名 第二个为可变参数 类型为 参数类型的字节码
  15. Method method = activityClass.getMethod("setContentView",
  16. int.class);
  17. // 调用方法 第一个参数为哪个实例去掉用 第二个参数为 参数
  18. method.invoke(obj, id);
  19. } catch (Exception e) {
  20. e.printStackTrace();
  21. }
  22. }

此方法写完后工具类的inject()方法调用即可

  1. /**
  2. * 初始化activity和所有注解
  3. *
  4. * @param obj
  5. * 你需要初始化的activity
  6. */
  7. public static void inject(Object obj) {
  8. activityClass = obj.getClass();
  9. // 初始化activity布局文件
  10. injectContent(obj);
  11. }

完整代码:

  1. public class FmyViewInject {
  2. /**
  3. * 保存传入的activity
  4. */
  5. private static Class<?> activityClass;
  6. /**
  7. * 初始化activity和所有注解
  8. *
  9. * @param obj
  10. * 你需要初始化的activity
  11. */
  12. public static void inject(Object obj) {
  13. activityClass = obj.getClass();
  14. // 初始化activity布局文件
  15. injectContent(obj);
  16. }
  17. /**
  18. * 初始化activity布局文件 让其不用调用setContentView
  19. *
  20. * @param activity
  21. */
  22. private static void injectContent(Object obj) {
  23. // 获取注解
  24. FmyContentView annotation = activityClass
  25. .getAnnotation(FmyContentView.class);
  26. if (annotation != null) {
  27. // 获取注解中的对应的布局id 因为注解只有个方法 所以@XXX(YYY)时会自动赋值给注解类唯一的方法
  28. int id = annotation.value();
  29. try {
  30. // 得到activity中的方法 第一个参数为方法名 第二个为可变参数 类型为 参数类型的字节码
  31. Method method = activityClass.getMethod("setContentView",
  32. int.class);
  33. // 调用方法 第一个参数为哪个实例去掉用 第二个参数为 参数
  34. method.invoke(obj, id);
  35. } catch (Exception e) {
  36. e.printStackTrace();
  37. }
  38. }
  39. }

赶快去试试 我们继续写下一步 用法在开始的示例有

activity查找控件

效果如下

  1. @FmyContentView(R.layout.activity_main)
  2. public class MainActivity extends FragmentActivity {
  3. //直接实例化
  4. @FmyViewView(R.id.fl)
  5. private FrameLayout fl;
  6. @Override
  7. protected void onCreate(Bundle savedInstanceState) {
  8. super.onCreate(savedInstanceState);
  9. FmyViewInject.inject(this);
  10. }
  11. }

第一步:

继续写一个注解

  1. /**
  2. * 此方注解写于activity类中 控件变量上 可以省去findViewId 的烦恼
  3. * @author 范明毅
  4. * @version 1.0
  5. */
  6. @Target(ElementType.FIELD)
  7. @Retention(RetentionPolicy.RUNTIME)
  8. public @interface FmyViewView {
  9. /**
  10. * 保存view控件的id
  11. * @return view控件id
  12. */
  13. int value();
  14. }

第二步 继续第一节的”activity设置填充布局框架”中的工具类添加新的方法

  1. /**
  2. * 初始化activity中的所有view控件 让其不用一个findViewid 实例化
  3. *
  4. * @param activity
  5. */
  6. private static void injectView(Object activityOrFragment) {
  7. // 对象所有的属性
  8. Field[] declaredFields = null;
  9. // 健壮性
  10. if (activityClass != null) {
  11. // 获取du所有的属性 包含私有 保护 默认 共开 但不包含继承等
  12. // getFields可以获取到所有公开的包括继承的 但无法获取到私有的属性
  13. declaredFields = activityClass.getDeclaredFields();
  14. }
  15. // 健壮性
  16. if (declaredFields != null) {
  17. // 遍历所有的属性变量
  18. for (Field field : declaredFields) {
  19. // 获取属性变量上的注解
  20. FmyViewView annotation = field.getAnnotation(FmyViewView.class);
  21. // 如果此属性变量 包含FMYViewView
  22. if (annotation != null) {
  23. // 获取属性id值
  24. int id = annotation.value();
  25. Object obj = null;
  26. try {
  27. // 获取activity中方法
  28. obj = activityClass.getMethod("findViewById",
  29. int.class).invoke(activityOrFragment, id);
  30. Log.e("FMY", "" + field.getClass());
  31. // 设置属性变量 指向实例
  32. // 如果修饰符不为公共类 这里注意了 当activity
  33. // 控件变量为private的时候 我们去访问会失败的 要么打破封装系 要么变量改为public
  34. //如 private TextView tv 这种情况 如果不打破封装会直接异常
  35. if (Modifier.PUBLIC != field.getModifiers()) {
  36. // 打破封装性
  37. field.setAccessible(true);
  38. }
  39. // 这里相当于 field= acitivity.obj
  40. field.set(activityOrFragment, obj);
  41. } catch (Exception e) {
  42. // TODO Auto-generated catch block
  43. e.printStackTrace();
  44. }
  45. }
  46. }
  47. }
  48. }

第三步

在工具类中的inject ()方法调用

  1. /**
  2. * 初始化activity和所有注解
  3. *
  4. * @param obj 你需要初始化的activity
  5. */
  6. public static void inject(Object obj) {
  7. activityClass = obj.getClass();
  8. // 初始化activity布局文件
  9. injectContent(obj);
  10. // 初始化所有控件实例 省去findViewId的痛苦
  11. injectView(obj);
  12. }

activity设置控件的点击事件

这里需要的知识点 如动态代理等 这里大家可以自己百度看下

效果如下

  1. @FmyContentView(R.layout.activity_main)
  2. public class MainActivity extends FragmentActivity {
  3. @FmyViewView(R.id.fl)
  4. private FrameLayout fl;
  5. @Override
  6. protected void onCreate(Bundle savedInstanceState) {
  7. super.onCreate(savedInstanceState);
  8. FmyViewInject.inject(this);
  9. }
  10. //当填充的布局中 id为R.id.fl 被点击将调用如下方法
  11. @FmyClickView({R.id.fl})
  12. public void onClick(View v){
  13. Log.e("fmy", "===>>");
  14. }
  15. }

第一步 :

同样写下一个注解

  1. /**
  2. *
  3. * 设置点击事件的注解 只需要在某方法 上写上此注解即可 如@FmyClickView({R.id.bt1,R.id.bt2})
  4. * @version 1.0
  5. * @author 范明毅
  6. *
  7. */
  8. @Target(ElementType.METHOD)
  9. @Retention(RetentionPolicy.RUNTIME)
  10. public @interface FmyClickView {
  11. /**
  12. * 保存所有需要设置点击事件控件的id
  13. * @return
  14. */
  15. int [] value();
  16. }

第二步:

写下一个代理处理类(我写在工具类中)

  1. /**
  2. * 代理处理点击逻辑代码
  3. *
  4. * @author 范明毅
  5. *
  6. */
  7. static class MInvocationHandler implements InvocationHandler {
  8. //这里我们到时候回传入activity
  9. private Object target;
  10. // 用户自定义view 的点击事件方法
  11. private Method method;
  12. public MInvocationHandler(Object target, java.lang.reflect.Method method) {
  13. super();
  14. this.target = target;
  15. this.method = method;
  16. }
  17. @Override
  18. public Object invoke(Object proxy, Method method, Object[] args)
  19. throws Throwable {
  20. // 调用用户自定义方法的点击事件 让activity调用中开发者设定的方法
  21. return this.method.invoke(target, args);
  22. }
  23. }

第三步:

在工具类中写下一个方法用于初始化点击事件

  1. /**
  2. * 初始化所有控件的点击事件 只需要某方法上写上对应注解和id即可
  3. *
  4. * @param activity
  5. */
  6. private static void inijectOnClick(Object activityOrFragment) {
  7. //获得所有方法
  8. Method[] methods = null;
  9. methods = activityClass.getMethods();
  10. // 遍历所有的activity下的方法
  11. for (Method method : methods) {
  12. // 获取方法的注解
  13. FmyClickView fmyClickView = method
  14. .getAnnotation(FmyClickView.class);
  15. // 如果存在此注解
  16. if (fmyClickView != null) {
  17. // 所有注解的控件的id
  18. int[] ids = fmyClickView.value();
  19. // 代理处理类
  20. MInvocationHandler handler = new MInvocationHandler(activityOrFragment,
  21. method);
  22. // 代理实例 这里也可以返回 new Class<?>[] { View.OnClickListener.class }中的接口类
  23. //第一个参数用于加载其他类 不一定要使用View.OnClickListener.class.getClassLoader() 你可以使用其他的
  24. //第二个参数你所实现的接口
  25. Object newProxyInstance = Proxy.newProxyInstance(
  26. View.OnClickListener.class.getClassLoader(),
  27. new Class<?>[] { View.OnClickListener.class }, handler);
  28. // 遍历所有的控件id 然后设置代理
  29. for (int i : ids) {
  30. try {
  31. Object view = null;
  32. //如果对象是activity
  33. view = activityClass.getMethod("findViewById",
  34. int.class).invoke(activityOrFragment, i);
  35. if (view != null) {
  36. Method method2 = view.getClass().getMethod(
  37. "setOnClickListener",
  38. View.OnClickListener.class);
  39. method2.invoke(view, newProxyInstance);
  40. }
  41. } catch (Exception e) {
  42. e.printStackTrace();
  43. }
  44. }
  45. }
  46. }
  47. }

第四部:

在工具类的inject()调用即可


  1. /**
  2. * 初始化activity和所有注解
  3. *
  4. * @param obj
  5. * 你需要初始化的activity
  6. */
  7. public static void inject(Object obj) {
  8. activityClass = obj.getClass();
  9. // 初始化activity布局文件
  10. injectContent(obj);
  11. // 初始化所有控件实例 省去findViewId的痛苦
  12. injectView(obj);
  13. // 初始化所有控件的点击事件
  14. inijectOnClick(obj);
  15. }

android注解入门 并来自己写一个框架的更多相关文章

  1. Android开发之手把手教你写ButterKnife框架(三)

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52672188 本文出自:[余志强的博客] 一.概述 上一篇博客讲了, ...

  2. Android开发之手把手教你写ButterKnife框架(二)

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52664112 本文出自:[余志强的博客] 上一篇博客Android开 ...

  3. caffe 入门实例2 如何写一个模型

    占坑,记录如何写一个基于lenet5的模型,并进行测试.

  4. javascript入门 之 用bootstrap-table写一个表格

    方法1(对普通的 table 设置 data-toggle="table" 即可): <!DOCTYPE html> <html> <head> ...

  5. Android开发之手把手教你写ButterKnife框架(一)

    欢迎转载,转载请标明出处: http://blog.csdn.net/johnny901114/article/details/52662376 本文出自:[余志强的博客] 一.概述 JakeWhar ...

  6. 自己写一个java的mvc框架吧(二)

    自己写一个mvc框架吧(二) 自己写代码的习惯 写一个框架吧,如果这个框架会用到一些配置上的东西,我自己习惯是先不用考虑这个配置文件应该是怎样的,什么形式的,先用一个java对象(比如叫 Config ...

  7. 手把手教你写一个java的orm(一)

    写之前的说明 其实吧. 这个东西已经写好了,地址在:https://github.com/hjx601496320/JdbcPlus 这系列文章算是我写的过程的总结吧.(恩系列,说明我可能会写好久,╮ ...

  8. 写一个ORM框架的第一步

    新一次的内部提升开始了,如果您想写一个框架从Apache Commons DbUtils开始学习是一种不错的选择,我们先学习应用这个小“框架”再把源代码理解,然后写一个属于自己的ORM框架不是梦. 一 ...

  9. 写一个ORM框架的第一步(Apache Commons DbUtils)

    新一次的内部提升开始了,如果您想写一个框架从Apache Commons DbUtils开始学习是一种不错的选择,我们先学习应用这个小“框架”再把源代码理解,然后写一个属于自己的ORM框架不是梦. 一 ...

随机推荐

  1. Oracle数据库(3-7)

    显式游标使用主要有四个步骤: 声明/定义游标打开游标读取数据关闭游标 CASE 条件表达式 WHEN 条件表达式结果1 THEN 语句1 WHEN 条件表达式结果2 THEN 语句2 ...... W ...

  2. ansible+packer+terraform在aws上布署web服务器

    各工具所扮演的角色 ansible: 配合packer生成安装有apache的基础镜像 packer: 生成amazon AMI terraform: 以packer生成的镜像为基础,布署web服务器 ...

  3. ACE入门——ACE构建

    ACE(ADAPTIVE Communication Environment),ACE入门的第一课就是要学习怎么在自己的系统上构建ACE. ACE是跨平台的,这是它的一个很重要的特性,ACE支持很多的 ...

  4. 【SQL.基础构建-第一节(1/4)】

    --        Tips:数据库与sql--    一.What's 数据库-- 1.数据库(Database,DB):将大量数据保存起来,通过计算机加工而成的可以进行高效访问的数据集合.--   ...

  5. [LeetCode] K-diff Pairs in an Array 数组中差为K的数对

    Given an array of integers and an integer k, you need to find the number of unique k-diff pairs in t ...

  6. 页面中引入mui 地址选择,点击页面中其他input时页面回到顶部

    问题:在页面中引入mui地址选择时,点击页面中的input页面会滚到顶部(谷歌浏览器中出现的bug),在手机上点击input会出现跳动.开始的时候是想修改mui.min.js里的滚动事件,但是后来找到 ...

  7. Windows 2012服务器安装GPU版TensorFlow完全攻略

    一.首先,推荐用Anaconda安装 因为Anaconda本身就已经默认安装了很多常用的Python库,可以省去大量的库安装过程,并且解决兼容性问题. Anaconda本身的安装也非常简单,搜索Ana ...

  8. [BZOJ 3456]城市规划

    Description 题库链接( bzoj 权限题,可以去 cogs 交♂ 题库链接2 求含有 \(n\) 个点有标号的简单无向联通图的个数.方案数对 \(1004535809(479\times ...

  9. [ZJOI 2007]Hide 捉迷藏

    Description 捉迷藏 Jiajia和Wind是一对恩爱的夫妻,并且他们有很多孩子.某天,Jiajia.Wind和孩子们决定在家里玩捉迷藏游戏.他们的家很大且构造很奇特,由N个屋子和N-1条双 ...

  10. [HAOI2008]圆上的整点

    题目描述 求一个给定的圆(x^2+y^2=r^2),在圆周上有多少个点的坐标是整数. 输入输出格式 输入格式: r 输出格式: 整点个数 输入输出样例 输入样例#1: 4 输出样例#1: 4 说明 n ...