热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Ioc注入框架注入布局注入控件动态代理注入事件

IOC控制反转注入框架很早之前我们用过Xutils框架里面有通过注解来使用findViewById之前我们只是使用。这样的框架我们要自己实现一遍主要分为三个部分1.注入布局

IOC控制反转注入框架

很早之前我们用过Xutils框架 里面有通过注解来使用findViewById 之前我们只是使用。

这样的框架我们要自己实现一遍

主要分为三个部分
1. 注入布局 (利用注解)
2. 注入控件
3. 注入事件 (利用动态代理注入事件)

注入布局

定义注入布局时注解

package com.jiang.iocxutil.annotion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by jiang on 2017/6/17.
* 注入布局
*
* ElementType.TYPE 表示类的注解
* RetentionPolicy.RUNTIME 运行时注解 一般我们编写的都是基于运行时的注解
*/


@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface ContentView
{

int value();
}

在基类中初始化注入

package com.jiang.iocxutil;

import android.os.Bundle;
import android.os.PersistableBundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;

/**
* Created by jiang on 2017/6/17.
*/


public class BaseActivity extends AppCompatActivity {

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
InjectUtils.inject(this);
}
}

注入布局相关代码

  /**
* 注入布局
* @param context
*/

public static void injectLayout(Context context) {
Class clazz = context.getClass();
ContentView cOntentView= clazz.getAnnotation(ContentView.class);
if (contentView != null) {
int layoutId = contentView.value();
//setContentView
try {
Method method = clazz.getMethod("setContentView", int.class);
method.setAccessible(true);
method.invoke(context, layoutId);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

}

}

在MainActiviy里面编写注入布局

package com.jiang.iocxutil;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import com.jiang.iocxutil.annotion.ContentView;
import com.jiang.iocxutil.annotion.OnClick;
import com.jiang.iocxutil.annotion.OnLongClick;
import com.jiang.iocxutil.annotion.ViewInject;

/**
* IOC控制反转框架
*
*/

@ContentView(value = R.layout.activity_main)
public class MainActivity extends BaseActivity {
private static final String TAG = "MainActivity";

// @Override
// protected void onCreate(Bundle savedInstanceState) {
// super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
// }

@ViewInject(R.id.text_ioc)
TextView textView;

@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "textView ==" + textView.hashCode());

// textView.setOnClickListener(new View.OnClickListener() {
// @Override
// public void onClick(View v) {
//
// }
// });

// textView.setOnLongClickListener(new View.OnLongClickListener() {
// @Override
// public boolean onLongClick(View v) {
// return false;
// }
// });
}


// @OnClick(R.id.text_ioc)
// public void click(View view){
// toast("测试点击");
// }
@OnLongClick(R.id.text_ioc)
public boolean click(View view){
toast("测试点击");
return false;
}

public void toast(String string) {
Toast.makeText(this, string, Toast.LENGTH_SHORT).show();
}
}

这样就可以通过注入布局来代替展示SetContentView()方法

注入控件

定义注解

package com.jiang.iocxutil.annotion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by jiang on 2017/6/17.
* 注入控件注解
*/

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewInject {
int value();
}

2.利用反射找到findViewById来注入控件

 /**
* 依赖注入控件
* @param context
*/

public static void injectView(Context context) {
Class aClass = context.getClass();
// 拿到成员变量 数组
Field[] fields = aClass.getDeclaredFields();
// 遍历所有的属性
for (Field field: fields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);

if (viewInject != null) {
int valueID = viewInject.value();
try {
Method method = aClass.getMethod("findViewById", int.class);
View view = (View) method.invoke(context, valueID);
field.setAccessible(true);
field.set(context, view);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
  1. 在MainActivity中我们使用 测试一下注入控件是否可用
    @ViewInject(R.id.text_ioc)
TextView textView;

@Override
protected void onResume() {
super.onResume();
Log.d(TAG, "textView ==" + textView.hashCode());
}

注入事件(这个比较复杂也是难点)

为了事件的扩展性 我们要拿到事件的三个要素
1.事件的注册
2.事件的类型
3.事件的回调方法

package com.jiang.iocxutil.annotion;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by jiang on 2017/6/17.
* 注解的注解 事件的三要素
*/

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EventBase {
/**
* 监听事件的方法
* @return String
*/

String listenerSetter();

/**
* 事件的类型
* @return Class
*/

Class listenerType();

/**
* 事件被触发后的回调方法
* @return
*/

String callBackMethod();
}

我们先写点击事件

package com.jiang.iocxutil.annotion;

import android.app.Dialog;
import android.view.View;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by jiang on 2017/6/17.
* 以EventBase做注解
*
* 目前需要View.OnClickListener.class
* 为了扩展可能还需要Dialog.OnClickListener.class
*/

@EventBase(listenerSetter = "setOnClickListener"
, listenerType = View.OnClickListener.class

, callBackMethod = "onClick")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {

int[] value();
}

然后通过反射和动态代理去执行回调

 /**
* 注入事件
*
* public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。
* public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,
* 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。
* @param context
*/

private static void injectEvent(Context context) {
Class clazz = context.getClass();
Method[] methods = clazz.getMethods();

for (Method method: methods) {
/**
* 扩展性
*/

Annotation[] anns = method.getAnnotations();

// 循环拿到方法类型注解
for (Annotation ann: anns) {
// 拿到注解的类 先拿到OnlickC注解
Class anntiOnType= ann.annotationType();
//然后再拿到注解的注解EvenBase
EventBase eventBase = anntionType.getAnnotation(EventBase.class);
if (eventBase == null) {
continue;
}
//拿到事件的三要素
// 设置事件 拿到 setOnclickListener
String listenerSetter = eventBase.listenerSetter();
// 事件类型 拿到 View.OnClickListener.class
Class listenerType = eventBase.listenerType();
// 回调方法 拿到 callBackMethod onClick
String callBackMethod = eventBase.callBackMethod();
// 下一步 通过反射 给View 设置
// 继续反射拿到 注解里面的id

Map methodMap = new HashMap<>();
// 得到当前callBackMethod 对应 Onclick method -- clikText
methodMap.put(callBackMethod, method);
try {
//
Method declaredMethod = anntionType.getMethod("value");
// 注解上的方法 找到Id数组
int[] valuesId = (int[]) declaredMethod.invoke(ann);
for (int viewId: valuesId) {
Method findViewById = clazz.getMethod("findViewById", int.class);
View view = (View) findViewById.invoke(context, viewId);
//
if (view == null) {
continue;
}
//上面的事件三个要素全部拿到了 View的Class setOnClickListener 对应 view的setOnClickListener
Method setOnClickListener= view.getClass().getMethod(listenerSetter, listenerType);

ListenerInvocationHandler handler = new ListenerInvocationHandler(context, methodMap);
// 拿到动态代理
Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{ listenerType }, handler);
// 将我们的方法执行
setOnClickListener.invoke(view, proxyInstance);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

}
}

/**
* 如何给其设置一个监听 onClick又不在InjectUtils回调 而是在MainActivity里面回调
* 动态代理 今天的需求是要给按钮设置一个监听 我要执行的一个回调监听不能卸载InjectUtils里面
* 而是想回调 MainActivity
*/

动态代理类的设计

package com.jiang.iocxutil;

import android.content.Context;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.Map;

/**
* Created by jiang on 2017/6/17.
*/


public class ListenerInvocationHandler implements InvocationHandler {

private Context context;



private Map methodMap;

public ListenerInvocationHandler(Context context, Map methodMap) {
this.cOntext= context;
this.methodMap = methodMap;
}

/**
* 处理代理对象的代理方法
* 我们要代理谁 MainActivity OnClickListener
* 持有一个真正的对象引用就是MainActivity
* @param proxy
* @param method
* @param args
* @return
* @throws Throwable
*/

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String name = method.getName();
Method mtd = methodMap.get(name);
if (mtd == null) {
//不需要代理
return method.invoke(proxy, args);
} else {
//真正的代理方法
return mtd.invoke(context, args);
}
}
}

然后再MainActivity里面测试注册事件

 @OnClick(R.id.text_ioc)
public void click(View view){
toast("测试点击");
}

此时我们的注册事件就已经完工。
我们看一下其扩展性

然后写长按点击事件 首先定义注解类似于点击事件

package com.jiang.iocxutil.annotion;

import android.view.View;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Created by jiang on 2017/6/17.
* 长按事件的注解
*/

@EventBase(listenerSetter = "setOnLongClickListener"
, listenerType = View.OnLongClickListener.class
, callBackMethod = "onLongClick")
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnLongClick {
int[] value();
}

在Manactivity里面 添加

 @OnLongClick(R.id.text_ioc)
public boolean longLlick(View view){
toast("测试点击");
return false;
}

长按点击也就可以成功了

注入工具类 包含注入布局 注入控件 和 注入事件

package com.jiang.iocxutil;

import android.content.Context;
import android.view.View;

import com.jiang.iocxutil.annotion.ContentView;
import com.jiang.iocxutil.annotion.EventBase;
import com.jiang.iocxutil.annotion.ViewInject;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;

/**
* Created by jiang on 2017/6/17.
* 依赖注入工具类、
* 分别为注入布局
* 注入控件
* 注入事件
*/


public class InjectUtils {

/**
* 初始化注入
* @param context
*/

public static void inject(Context context) {
injectLayout(context);
injectView(context);
injectEvent(context);
}

/**
* 依赖注入控件
* @param context
*/

public static void injectView(Context context) {
Class aClass = context.getClass();
// 拿到成员变量 数组
Field[] fields = aClass.getDeclaredFields();
// 遍历所有的属性
for (Field field: fields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);

if (viewInject != null) {
int valueID = viewInject.value();
try {
Method method = aClass.getMethod("findViewById", int.class);
View view = (View) method.invoke(context, valueID);
field.setAccessible(true);
field.set(context, view);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}

/**
* 注入布局
* @param context
*/

public static void injectLayout(Context context) {
Class clazz = context.getClass();
ContentView cOntentView= clazz.getAnnotation(ContentView.class);
if (contentView != null) {
int layoutId = contentView.value();
//setContentView
try {
Method method = clazz.getMethod("setContentView", int.class);
method.setAccessible(true);
method.invoke(context, layoutId);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}

}

}


/**
* 注入事件
*
* public Method[] getMethods()返回某个类的所有公用(public)方法包括其继承类的公用方法,当然也包括它所实现接口的方法。
* public Method[] getDeclaredMethods()对象表示的类或接口声明的所有方法,
* 包括公共、保护、默认(包)访问和私有方法,但不包括继承的方法。当然也包括它所实现接口的方法。
* @param context
*/

private static void injectEvent(Context context) {
Class clazz = context.getClass();
Method[] methods = clazz.getMethods();

for (Method method: methods) {
/**
* 扩展性
*/

Annotation[] anns = method.getAnnotations();

// 循环拿到方法类型注解
for (Annotation ann: anns) {
// 拿到注解的类 先拿到OnlickC注解
Class anntiOnType= ann.annotationType();
//然后再拿到注解的注解EvenBase
EventBase eventBase = anntionType.getAnnotation(EventBase.class);
if (eventBase == null) {
continue;
}
//拿到事件的三要素
// 设置事件 拿到 setOnclickListener
String listenerSetter = eventBase.listenerSetter();
// 事件类型 拿到 View.OnClickListener.class
Class listenerType = eventBase.listenerType();
// 回调方法 拿到 callBackMethod onClick
String callBackMethod = eventBase.callBackMethod();
// 下一步 通过反射 给View 设置
// 继续反射拿到 注解里面的id

Map methodMap = new HashMap<>();
// 得到当前callBackMethod 对应 Onclick method -- clikText
methodMap.put(callBackMethod, method);
try {
//
Method declaredMethod = anntionType.getMethod("value");
// 注解上的方法 找到Id数组
int[] valuesId = (int[]) declaredMethod.invoke(ann);
for (int viewId: valuesId) {
Method findViewById = clazz.getMethod("findViewById", int.class);
View view = (View) findViewById.invoke(context, viewId);
//
if (view == null) {
continue;
}
//上面的事件三个要素全部拿到了 View的Class setOnClickListener 对应 view的setOnClickListener
Method setOnClickListener= view.getClass().getMethod(listenerSetter, listenerType);

ListenerInvocationHandler handler = new ListenerInvocationHandler(context, methodMap);
// 拿到动态代理
Object proxyInstance = Proxy.newProxyInstance(listenerType.getClassLoader(), new Class[]{ listenerType }, handler);
// 将我们的方法执行
setOnClickListener.invoke(view, proxyInstance);
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}

}
}

/**
* 如何给其设置一个监听 onClick又不在InjectUtils回调 而是在MainActivity里面回调
* 动态代理 今天的需求是要给按钮设置一个监听 我要执行的一个回调监听不能卸载InjectUtils里面
* 而是想回调 MainActivity
*/

}

匆匆编写 细节还需大家验证 有疑问
邮箱 zhangdanjiang_java@163.com
GitBub地址 https://github.com/JiangGeJavaAndroid/IocXutil


推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • PHP中的单例模式与静态变量的区别及使用方法
    本文介绍了PHP中的单例模式与静态变量的区别及使用方法。在PHP中,静态变量的存活周期仅仅是每次PHP的会话周期,与Java、C++不同。静态变量在PHP中的作用域仅限于当前文件内,在函数或类中可以传递变量。本文还通过示例代码解释了静态变量在函数和类中的使用方法,并说明了静态变量的生命周期与结构体的生命周期相关联。同时,本文还介绍了静态变量在类中的使用方法,并通过示例代码展示了如何在类中使用静态变量。 ... [详细]
  • Android开发实现的计时器功能示例
    本文分享了Android开发实现的计时器功能示例,包括效果图、布局和按钮的使用。通过使用Chronometer控件,可以实现计时器功能。该示例适用于Android平台,供开发者参考。 ... [详细]
  • 本文介绍了在处理不规则数据时如何使用Python自动提取文本中的时间日期,包括使用dateutil.parser模块统一日期字符串格式和使用datefinder模块提取日期。同时,还介绍了一段使用正则表达式的代码,可以支持中文日期和一些特殊的时间识别,例如'2012年12月12日'、'3小时前'、'在2012/12/13哈哈'等。 ... [详细]
  • 本文介绍了在MFC下利用C++和MFC的特性动态创建窗口的方法,包括继承现有的MFC类并加以改造、插入工具栏和状态栏对象的声明等。同时还提到了窗口销毁的处理方法。本文详细介绍了实现方法并给出了相关注意事项。 ... [详细]
  • 本文介绍了RxJava在Android开发中的广泛应用以及其在事件总线(Event Bus)实现中的使用方法。RxJava是一种基于观察者模式的异步java库,可以提高开发效率、降低维护成本。通过RxJava,开发者可以实现事件的异步处理和链式操作。对于已经具备RxJava基础的开发者来说,本文将详细介绍如何利用RxJava实现事件总线,并提供了使用建议。 ... [详细]
  • 本文介绍了Java中Currency类的getInstance()方法,该方法用于检索给定货币代码的该货币的实例。文章详细解释了方法的语法、参数、返回值和异常,并提供了一个示例程序来说明该方法的工作原理。 ... [详细]
  • Spring框架《一》简介
    Spring框架《一》1.Spring概述1.1简介1.2Spring模板二、IOC容器和Bean1.IOC和DI简介2.三种通过类型获取bean3.给bean的属性赋值3.1依赖 ... [详细]
  • Java图形化计算器设计与实现
    本文介绍了使用Java编程语言设计和实现图形化计算器的方法。通过使用swing包和awt包中的组件,作者创建了一个具有按钮监听器和自定义界面尺寸和布局的计算器。文章还分享了在图形化界面设计中的一些心得体会。 ... [详细]
  • 1简介本文结合数字信号处理课程和Matlab程序设计课程的相关知识,给出了基于Matlab的音乐播放器的总体设计方案,介绍了播放器主要模块的功能,设计与实现方法.我们将该设 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 网络请求模块选择——axios框架的基本使用和封装
    本文介绍了选择网络请求模块axios的原因,以及axios框架的基本使用和封装方法。包括发送并发请求的演示,全局配置的设置,创建axios实例的方法,拦截器的使用,以及如何封装和请求响应劫持等内容。 ... [详细]
  • 本文介绍了在iOS开发中使用UITextField实现字符限制的方法,包括利用代理方法和使用BNTextField-Limit库的实现策略。通过这些方法,开发者可以方便地限制UITextField的字符个数和输入规则。 ... [详细]
  • MPLS VP恩 后门链路shamlink实验及配置步骤
    本文介绍了MPLS VP恩 后门链路shamlink的实验步骤及配置过程,包括拓扑、CE1、PE1、P1、P2、PE2和CE2的配置。详细讲解了shamlink实验的目的和操作步骤,帮助读者理解和实践该技术。 ... [详细]
author-avatar
遗留下的痛cc-x_393
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有