android注解入门 并来自己写一个框架
2017-03-01 13:56
507 查看
介绍
这里我带大家来学习一下注解 并且用来写下一个模仿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: } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
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); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
使用框架:
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); } }1
2
3
4
5
6
7
8
9
10
11
12
13
1
2
3
4
5
6
7
8
9
10
11
12
13
第一步:
创建一个注解类如下
@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(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
第二步:
写一个工具类 配合注解使用 当开发者使用此类时激活注解的作用
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) { } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
大家先不用着急看不懂为什么这样写原因
核心源码位于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(); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
此方法写完后工具类的inject()方法调用即可
/** * 初始化activity和所有注解 * * @param obj * 你需要初始化的activity */ public static void inject(Object obj) { activityClass = obj.getClass(); // 初始化activity布局文件 injectContent(obj); }1
2
3
4
5
6
7
8
9
10
11
1
2
3
4
5
6
7
8
9
10
11
完整代码:
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(); } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
赶快去试试 我们继续写下一步 用法在开始的示例有
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); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
第一步:
继续写一个注解
/** * 此方注解写于activity类中 控件变量上 可以省去findViewId 的烦恼 * @author 范明毅 * @version 1.0 */ @Target(ElementType.FIELD) @Retention(RetentionPolicy.RUNTIME) public @interface FmyViewView { /** * 保存view控件的id * @return view控件id */ int value(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
1
2
3
4
5
6
7
8
9
10
11
12
13
14
第二步 继续第一节的”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(); } } } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
第三步
在工具类中的inject ()方法调用
/** * 初始化activity和所有注解 * * @param obj 你需要初始化的activity */ public static void inject(Object obj) { activityClass = obj.getClass(); // 初始化activity布局文件 injectContent(obj); // 初始化所有控件实例 省去findViewId的痛苦 injectView(obj); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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", "===>>"); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
第一步 :
同样写下一个注解
/** * * 设置点击事件的注解 只需要在某方法 上写上此注解即可 如@FmyClickView({R.id.bt1,R.id.bt2}) * @version 1.0 * @author 范明毅 * */ @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface FmyClickView { /** * 保存所有需要设置点击事件控件的id * @return */ int [] value(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
第二步:
写下一个代理处理类(我写在工具类中)
/** * 代理处理点击逻辑代码 * * @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); } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
第三步:
在工具类中写下一个方法用于初始化点击事件
/** * 初始化所有控件的点击事件 只需要某方法上写上对应注解和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(); } } } } }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
第四部:
在工具类的inject()调用即可
/** * 初始化activity和所有注解 * * @param obj * 你需要初始化的activity */ public static void inject(Object obj) { activityClass = obj.getClass(); // 初始化activity布局文件 injectContent(obj); // 初始化所有控件实例 省去findViewId的痛苦 injectView(obj); // 初始化所有控件的点击事件 inijectOnClick(obj); }
相关文章推荐
- [置顶] android注解入门 并来自己写一个框架
- android注解入门 并来自己写一个框架
- Android 打造编译时注解解析框架 这只是一个开始
- Android自己动手做查找控件、绑定监听的注解框架
- Android之注解框架Annotations从配置到简单一个demo
- Android 仿ButterKnife写自己的IOC注解框架
- Android 打造编译时注解解析框架 这只是一个开始
- 自己动手写一个轻量级的Android网络请求框架
- 在Android开发过程中搭建一个自己的应用框架有几个步骤?需要注意什么?
- 自己动手写一个简单的IOC框架,使用注解绑定资源和事件
- 自己写一个IOC注解框架,里面有分析xutils3跟utterknife的IOC的源码分析--binbinyang
- Android 打造编译时注解解析框架 这只是一个开始
- Android 打造编译时注解解析框架 这只是一个开始
- 来,咱们自己写一个Android的IOC框架!
- 自己整理的一个Android数据库工具框架
- Android 打造编译时注解解析框架 这只是一个开始
- Android 打造编译时注解解析框架 这只是一个开始
- Android 打造编译时注解解析框架 这只是一个开始
- Android中使用注解打造自己的IOC框架
- 通过注解和反射编写一个android注解框架