介绍

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

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

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

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

如下代码:

fragment

package a.fmy.com.myapplication;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import a.fmy.com.mylibrary.FmyClickView;
import a.fmy.com.mylibrary.FmyContentView;
import a.fmy.com.mylibrary.FmyViewInject;
import a.fmy.com.mylibrary.FmyViewView; //你的fragment的布局id Your fragment's LayoutId
@FmyContentView(R.layout.fragment_blank)
public class BlankFragment extends Fragment {
//你想实例化控件的id
//Do you want to control instance id
// 等价于 findViewByid
//Equivalent to the findViewByid
@FmyViewView(R.id.tv1)
TextView tv1;
@FmyViewView(R.id.tv2)
TextView tv2;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//初始化fragment Initialize Fragement
return FmyViewInject.injectfragment(this,inflater,container);
}
//你想给哪个控件添加 添加事件 的id
//Do you want to add add event id to which controls
@FmyClickView({R.id.tv1,R.id.tv2})
public void myOnclick(View view){
switch (view.getId()) {
case R.id.tv1:
tv1.setText("TV1 "+Math.random()*100);
break;
case R.id.tv2:
tv2.setText("TV2 "+Math.random()*100);
break;
default: } }
}

Activity

package a.fmy.com.myapplication;

import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.widget.FrameLayout;
import a.fmy.com.mylibrary.FmyContentView;
import a.fmy.com.mylibrary.FmyViewInject;
import a.fmy.com.mylibrary.FmyViewView; @FmyContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity { @FmyViewView(R.id.fl)
FrameLayout fl; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initActivity
// 初始化activity
FmyViewInject.inject(this);
} @Override
protected void onResume() {
super.onResume();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fl,new BlankFragment());
fragmentTransaction.commit();
}
}

java注解学习

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

activity设置填充布局框架

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

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

不使用框架:

package a.fmy.com.mylibrary;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}

使用框架:

package a.fmy.com.mylibrary;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@FmyContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity { @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FmyViewInject.inject(this);
}
}

第一步:

创建一个注解类如下

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

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

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

/**
* 此方注解写于activity类上 可以免去 setContentView()步骤
* @author 范明毅
* @version 1.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FmyContentView {
/**
* 保存布局文件的id eg:R.layout.main
* @return 返回 布局id
*/
int value();
}

第二步:

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

public class FmyViewInject {
/**
* 保存传入的activity
*/
private static Class<?> activityClass;
/**
* 初始化activity和所有注解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
} /**
* 初始化activity布局文件 让其不用调用setContentView
*
* @param activity
*/
private static void injectContent(Object obj) {
}
}

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

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

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

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

    /**
* 初始化activity和所有注解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity布局文件
injectContent(obj);
}

完整代码:

public class FmyViewInject {
/**
* 保存传入的activity
*/
private static Class<?> activityClass;
/**
* 初始化activity和所有注解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity布局文件
injectContent(obj);
}
/**
* 初始化activity布局文件 让其不用调用setContentView
*
* @param activity
*/
private static void injectContent(Object obj) { // 获取注解
FmyContentView annotation = activityClass
.getAnnotation(FmyContentView.class); if (annotation != null) {
// 获取注解中的对应的布局id 因为注解只有个方法 所以@XXX(YYY)时会自动赋值给注解类唯一的方法
int id = annotation.value();
try {
// 得到activity中的方法 第一个参数为方法名 第二个为可变参数 类型为 参数类型的字节码
Method method = activityClass.getMethod("setContentView",
int.class); // 调用方法 第一个参数为哪个实例去掉用 第二个参数为 参数
method.invoke(obj, id);
} catch (Exception e) { e.printStackTrace();
}
}
}

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

activity查找控件

效果如下

@FmyContentView(R.layout.activity_main)
public class MainActivity extends FragmentActivity {
//直接实例化
@FmyViewView(R.id.fl)
private FrameLayout fl; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FmyViewInject.inject(this); }
}

第一步:

继续写一个注解

/**
* 此方注解写于activity类中 控件变量上 可以省去findViewId 的烦恼
* @author 范明毅
* @version 1.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FmyViewView {
/**
* 保存view控件的id
* @return view控件id
*/
int value();
}

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

 /**
* 初始化activity中的所有view控件 让其不用一个findViewid 实例化
*
* @param activity
*/
private static void injectView(Object activityOrFragment) { // 对象所有的属性
Field[] declaredFields = null; // 健壮性
if (activityClass != null) {
// 获取du所有的属性 包含私有 保护 默认 共开 但不包含继承等
// getFields可以获取到所有公开的包括继承的 但无法获取到私有的属性
declaredFields = activityClass.getDeclaredFields();
} // 健壮性
if (declaredFields != null) {
// 遍历所有的属性变量
for (Field field : declaredFields) { // 获取属性变量上的注解
FmyViewView annotation = field.getAnnotation(FmyViewView.class); // 如果此属性变量 包含FMYViewView
if (annotation != null) {
// 获取属性id值
int id = annotation.value(); Object obj = null;
try { // 获取activity中方法
obj = activityClass.getMethod("findViewById",
int.class).invoke(activityOrFragment, id); Log.e("FMY", "" + field.getClass());
// 设置属性变量 指向实例 // 如果修饰符不为公共类 这里注意了 当activity
// 控件变量为private的时候 我们去访问会失败的 要么打破封装系 要么变量改为public
//如 private TextView tv 这种情况 如果不打破封装会直接异常
if (Modifier.PUBLIC != field.getModifiers()) {
// 打破封装性
field.setAccessible(true);
}
// 这里相当于 field= acitivity.obj
field.set(activityOrFragment, obj);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
} }
}
} }

第三步

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

    /**
* 初始化activity和所有注解
*
* @param obj 你需要初始化的activity
*/
public static void inject(Object obj) { activityClass = obj.getClass(); // 初始化activity布局文件
injectContent(obj); // 初始化所有控件实例 省去findViewId的痛苦
injectView(obj); }

activity设置控件的点击事件

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

效果如下

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

第一步 :

同样写下一个注解

/**
*
* 设置点击事件的注解 只需要在某方法 上写上此注解即可 如@FmyClickView({R.id.bt1,R.id.bt2})
* @version 1.0
* @author 范明毅
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FmyClickView {
/**
* 保存所有需要设置点击事件控件的id
* @return
*/
int [] value();
}

第二步:

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

/**
* 代理处理点击逻辑代码
*
* @author 范明毅
*
*/
static class MInvocationHandler implements InvocationHandler {
//这里我们到时候回传入activity
private Object target; // 用户自定义view 的点击事件方法
private Method method; public MInvocationHandler(Object target, java.lang.reflect.Method method) {
super();
this.target = target;
this.method = method;
} @Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 调用用户自定义方法的点击事件 让activity调用中开发者设定的方法
return this.method.invoke(target, args);
} }

第三步:

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

    /**
* 初始化所有控件的点击事件 只需要某方法上写上对应注解和id即可
*
* @param activity
*/
private static void inijectOnClick(Object activityOrFragment) { //获得所有方法
Method[] methods = null; methods = activityClass.getMethods(); // 遍历所有的activity下的方法
for (Method method : methods) {
// 获取方法的注解
FmyClickView fmyClickView = method
.getAnnotation(FmyClickView.class);
// 如果存在此注解
if (fmyClickView != null) { // 所有注解的控件的id
int[] ids = fmyClickView.value(); // 代理处理类
MInvocationHandler handler = new MInvocationHandler(activityOrFragment,
method); // 代理实例 这里也可以返回 new Class<?>[] { View.OnClickListener.class }中的接口类
//第一个参数用于加载其他类 不一定要使用View.OnClickListener.class.getClassLoader() 你可以使用其他的
//第二个参数你所实现的接口
Object newProxyInstance = Proxy.newProxyInstance(
View.OnClickListener.class.getClassLoader(),
new Class<?>[] { View.OnClickListener.class }, handler); // 遍历所有的控件id 然后设置代理
for (int i : ids) {
try {
Object view = null; //如果对象是activity view = activityClass.getMethod("findViewById",
int.class).invoke(activityOrFragment, i); if (view != null) {
Method method2 = view.getClass().getMethod(
"setOnClickListener",
View.OnClickListener.class);
method2.invoke(view, newProxyInstance);
}
} catch (Exception e) { e.printStackTrace();
} } }
} }

第四部:

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


/**
* 初始化activity和所有注解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) { activityClass = obj.getClass(); // 初始化activity布局文件
injectContent(obj); // 初始化所有控件实例 省去findViewId的痛苦
injectView(obj); // 初始化所有控件的点击事件
inijectOnClick(obj);
}

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. uva 10917 Walk Through The Forest

    题意: 一个人从公司回家,他可以从A走到B如果从存在从B出发到家的一条路径的长度小于任何一条从A出发到家的路径的长度. 问这样的路径有多少条. 思路: 题意并不好理解,存在从B出发到家的一条路径的长度 ...

  2. git的理论基础

    GIT是目前世界上最先进最牛逼的分布式版本控制系统git维护的三棵树分别是工作区域.暂存区域.git仓库工作区域:就是你平时存放项目代码的地方暂存区域:用于临时存放你的改动,事实上它只是一个文件,保存 ...

  3. slf4j 与各个 logging框架的适配器说明

    在java领域里,日志框架纷杂繁多,项目中必然要使用很多的第三方库,而这些第三方库所使用的log框架又不尽相同.想要打出合理有效的日志,就必须在你的项目中将这些日志框架统一才行.幸好,slf4j, c ...

  4. react-router 4实现代码分割(code spliting)

    官方一开始推荐的使用bundle-loader来做代码分割的方式感觉有点麻烦,而且代码看起来有点不舒服.而且需要一直依赖bunder-loader 一开始我想为什么不能像vue一样,直接使用ES的新特 ...

  5. [BZOJ 4403]序列统计

    Description 给定三个正整数N.L和R,统计长度在1到N之间,元素大小都在L到R之间的单调不降序列的数量.输出答案对10^6+3取模的结果. Input 输入第一行包含一个整数T,表示数据组 ...

  6. hihocoder 1419 重复旋律4

    描述 小Hi平时的一大兴趣爱好就是演奏钢琴.我们知道一个音乐旋律被表示为长度为 N 的数构成的数列.小Hi在练习过很多曲子以后发现很多作品中的旋律有重复的部分. 我们把一段旋律称为(k,l)-重复的, ...

  7. [ZJOI2008]瞭望塔

    题目描述 致力于建设全国示范和谐小村庄的H村村长dadzhi,决定在村中建立一个瞭望塔,以此加强村中的治安. 我们将H村抽象为一维的轮廓.如下图所示 我们可以用一条山的上方轮廓折线(x1, y1), ...

  8. [ZJOI2006]超级麻将

    题目描述 很多人都知道玩麻将,当然也有人不知道,呵呵,不要紧,我在这里简要地介绍一下麻将规则: 普通麻将有砣.索.万三种类型的牌,每种牌有1~9个数字,其中相同的牌每个有四张,例如1砣~9砣,1索~9 ...

  9. bzoj 2339: [HNOI2011]卡农

    Description Solution 比较难想.... 我们先考虑去掉无序的这个条件,改为有序,最后除 \(m!\) 即可 设 \(f[i]\) 表示前\(i\)个合法集合的方案数 明确一点: 如 ...

  10. ●HDU 5608 function

    题链: http://acm.hdu.edu.cn/showproblem.php?pid=5608 题解: 莫比乌斯反演,杜教筛 已知$$N^2-3N+2=\sum_{d|N} f(d)$$ 多次询 ...