1.做android开发的都知道,在做项目时,用的最多的组件就是Activity,其实Activity就是对一系列View的封装,View就是最终展现UI的控件,所以在开发中基本上不可能不使用View,View既然是用户接口,那么用户作用在View上的一切动作,我们的程序必须对相应的操作做出相应的响应,在捕获事件,以及做处理之前我们必须完成两件事,1.获取该View的引用或直接创建对象.2.给该View注册事件监听器对象.之所以我们需要完成这两件事直接导致我们的程序中出现很多很多findViewById与set...Listener,重复的事情重复的做必然会导致开发的者们的厌恶,为了消除这种厌恶,所以我下定决心和大家分享一下如何既高效,也快速的解决上述问题。
2.技术知识的储备
a.首先要了解java中的注解,反射的基本概念
b.了解Class这个类,这个类是反射与注解的起点
c.具备一点java设计模式的思想(如此博文使用的动态代理)
3.注解实现思想
通过反射技术,将View,Resource,Event进行Inject
4.架构核心代码
<span style="font-size:18px;">import android.app.Activity;
import android.preference.Preference;
import android.preference.PreferenceActivity;
import android.preference.PreferenceGroup;
import android.view.View;
import com.lidroid.xutils.util.LogUtils;
import com.lidroid.xutils.view.EventListenerManager;
import com.lidroid.xutils.view.ResLoader;
import com.lidroid.xutils.view.ViewFinder;
import com.lidroid.xutils.view.ViewInjectInfo;
import com.lidroid.xutils.view.annotation.ContentView;
import com.lidroid.xutils.view.annotation.PreferenceInject;
import com.lidroid.xutils.view.annotation.ResInject;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.lidroid.xutils.view.annotation.event.EventBase;
import java.lang.annotation.Annotation;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ViewUtils {
private ViewUtils() {
}
public static void inject(View view) {
injectObject(view, new ViewFinder(view));
}
public static void inject(Activity activity) {
injectObject(activity, new ViewFinder(activity));
}
public static void inject(PreferenceActivity preferenceActivity) {
injectObject(preferenceActivity, new ViewFinder(preferenceActivity));
}
public static void inject(Object handler, View view) {
injectObject(handler, new ViewFinder(view));
}
public static void inject(Object handler, Activity activity) {
injectObject(handler, new ViewFinder(activity));
}
public static void inject(Object handler, PreferenceGroup preferenceGroup) {
injectObject(handler, new ViewFinder(preferenceGroup));
}
public static void inject(Object handler, PreferenceActivity preferenceActivity) {
injectObject(handler, new ViewFinder(preferenceActivity));
}
@SuppressWarnings("ConstantConditions")
<span style="color:#FF6666;"> private static void injectObject(Object handler, ViewFinder finder)</span> {
Class<?> handlerType = handler.getClass();
// inject ContentView
ContentView contentView = handlerType.getAnnotation(ContentView.class);
if (contentView != null) {
try {
Method setContentViewMethod = handlerType.getMethod("setContentView", int.class);
setContentViewMethod.invoke(handler, contentView.value());
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
// inject view
Field[] fields = handlerType.getDeclaredFields();
if (fields != null && fields.length > 0) {
for (Field field : fields) {
ViewInject viewInject = field.getAnnotation(ViewInject.class);
if (viewInject != null) {
try {
View view = finder.findViewById(viewInject.value(), viewInject.parentId());
if (view != null) {
field.setAccessible(true);
field.set(handler, view);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
} else {
ResInject resInject = field.getAnnotation(ResInject.class);
if (resInject != null) {
try {
Object res = ResLoader.loadRes(
resInject.type(), finder.getContext(), resInject.id());
if (res != null) {
field.setAccessible(true);
field.set(handler, res);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
} else {
PreferenceInject preferenceInject = field.getAnnotation(PreferenceInject.class);
if (preferenceInject != null) {
try {
Preference preference = finder.findPreference(preferenceInject.value());
if (preference != null) {
field.setAccessible(true);
field.set(handler, preference);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
}
}
}
}
// inject event
Method[] methods = handlerType.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
Annotation[] annotations = method.getDeclaredAnnotations();
if (annotations != null && annotations.length > 0) {
for (Annotation annotation : annotations) {
Class<?> annType = annotation.annotationType();
if (annType.getAnnotation(EventBase.class) != null) {
method.setAccessible(true);
try {
// ProGuard:-keep class * extends java.lang.annotation.Annotation { *; }
Method valueMethod = annType.getDeclaredMethod("value");
Method parentIdMethod = null;
try {
parentIdMethod = annType.getDeclaredMethod("parentId");
} catch (Throwable e) {
}
Object values = valueMethod.invoke(annotation);
Object parentIds = parentIdMethod == null ? null : parentIdMethod.invoke(annotation);
int parentIdsLen = parentIds == null ? 0 : Array.getLength(parentIds);
int len = Array.getLength(values);
for (int i = 0; i < len; i++) {
ViewInjectInfo info = new ViewInjectInfo();
info.value = Array.get(values, i);
info.parentId = parentIdsLen > i ? (Integer) Array.get(parentIds, i) : 0;
EventListenerManager.addEventMethod(finder, info, annotation, handler, method);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
}
}
}
}
}
}
</span>
<span style="font-size:18px;">import android.view.View;
import com.lidroid.xutils.util.LogUtils;
import com.lidroid.xutils.util.DoubleKeyValueMap;
import com.lidroid.xutils.view.annotation.event.EventBase;
import java.lang.annotation.Annotation;
import java.lang.ref.WeakReference;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
public class EventListenerManager {
private EventListenerManager() {
}
/**
* k1: viewInjectInfo
* k2: interface Type
* value: listener
*/
private final static DoubleKeyValueMap<ViewInjectInfo, Class<?>, Object> listenerCache =
new DoubleKeyValueMap<ViewInjectInfo, Class<?>, Object>();
<span style="color:#FF6666;"> public static void addEventMethod(
ViewFinder finder,
ViewInjectInfo info,
Annotation eventAnnotation,
Object handler,
Method method)</span> {
try {
View view = finder.findViewByInfo(info);
if (view != null) {
EventBase eventBase = eventAnnotation.annotationType().getAnnotation(EventBase.class);
Class<?> listenerType = eventBase.listenerType();
String listenerSetter = eventBase.listenerSetter();
String methodName = eventBase.methodName();
boolean addNewMethod = false;
DynamicHandler dynamicHandler = null;
Object listener = listenerCache.get(info, listenerType);
if (listener != null) {
<span style="color:#FF6666;"> dynamicHandler = (DynamicHandler) Proxy.getInvocationHandler(listener);</span>
addNewMethod = handler.equals(dynamicHandler.getHandler());
if (addNewMethod) {
dynamicHandler.addMethod(methodName, method);
}
}
if (!addNewMethod) {
dynamicHandler = new DynamicHandler(handler);
dynamicHandler.addMethod(methodName, method);
<span style="color:#FF6666;"> listener = Proxy.newProxyInstance(
listenerType.getClassLoader(),
new Class<?>[]{listenerType},
dynamicHandler);</span>
listenerCache.put(info, listenerType, listener);
}
Method setEventListenerMethod = view.getClass().getMethod(listenerSetter, listenerType);
setEventListenerMethod.invoke(view, listener);
}
} catch (Throwable e) {
LogUtils.e(e.getMessage(), e);
}
}
public static class DynamicHandler implements InvocationHandler {
private WeakReference<Object> handlerRef;
private final HashMap<String, Method> methodMap = new HashMap<String, Method>(1);
public DynamicHandler(Object handler) {
this.handlerRef = new WeakReference<Object>(handler);
}
public void addMethod(String name, Method method) {
methodMap.put(name, method);
}
public Object getHandler() {
return handlerRef.get();
}
public void setHandler(Object handler) {
this.handlerRef = new WeakReference<Object>(handler);
}
@Override
<span style="color:#FF6666;"> public Object invoke(Object proxy, Method method, Object[] args) throws Throwable </span>{
Object handler = handlerRef.get();
if (handler != null) {
String methodName = method.getName();
method = methodMap.get(methodName);
if (method != null) {
return method.invoke(handler, args);
}
}
return null;
}
}
}</span>
1.红色部分解释:
a.第一处:是注射View,事件的核心方法,该方法主要完成的任务是,获取View的引用,对View进行事件绑定
b.第二处:是真正实现对View事件的注入
c.第三处:是从动态代理对象中获取被代理对象
d.第四处:运行时生成对象,实现动态代理,动态代理与静态代理不理解的可以查一下资料
e.第五处:当用户作用在View的事件时,代理对象调用此方法,进而被代理对象调用被注入的方法
5.实战
说明:该程序很简单,但是包括了整个架构的实现,View的初始化,View事件的注入,资源的绑定,Activity UI的注入
程序代码:
package com.example.viewinject;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.v7.app.ActionBarActivity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import com.lidroid.xutils.ViewUtils;
import com.lidroid.xutils.view.ResType;
import com.lidroid.xutils.view.annotation.ContentView;
import com.lidroid.xutils.view.annotation.ResInject;
import com.lidroid.xutils.view.annotation.ViewInject;
import com.lidroid.xutils.view.annotation.event.OnClick;
@ContentView(value = R.layout.activity_main)
public class MainActivity extends ActionBarActivity {
@ViewInject(value = R.id.btn)
private Button mBtn;
@ResInject(id = R.drawable.m, type = ResType.Drawable)
private Drawable mDrawable;
@ViewInject(value = R.id.img, parentId = R.id.rl)
private ImageView mImg;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ViewUtils.inject(this);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
@OnClick(value = R.id.btn)
public void btnOnClick(View v) {
if(v.getId()==R.id.btn)
mImg.setImageDrawable(mDrawable);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
((BitmapDrawable) mDrawable).getBitmap().recycle();
super.onDestroy();
System.exit(0);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
6.运行效果:
总结:此框架在项目非常实用,不管是客户端,还是java服务端,注解,反射是运用的非常多的,这部分是java高级特性,对于java基础不是很扎实的理解起来可能有点吃力,我建议在阅读此博文之前,先阅读以下java中的注解,反射,设计模式的有关书籍,这样理解起来就比较好.本来这篇博文是接着上篇图像缓存系统的进一步深入剖析,在这里顺序打乱了一下,下篇博文是图像缓存系统的深入讲解,敬请期待...