实例一 反射+多态:生成容器实例化并装好view子类

简单实现步骤:

两个方法,一个提供完整包名.类以及资源所在引用(android的话就是xml的id);

另外一个方法就是用反射加上多态实现的装载;

private List<View> getViewList(List<View> list){
if(list == null)
list = new ArrayList<View>();
String vName[] = {"com.sansui.Memo.MemoView",
"com.sansui.Calendar.CalendarView","com.sansui.ClassRoom.ClassRoom"};
int vNo[] = {R.layout.memoview, R.layout.calendarview , R.layout.classroomview};
for(int i = 0 ; i < vNo.length ; i++)
list.add(getInstanceView(vName[i],vNo[i]));
return list;
}

这里要解释下: 装载在vNames的三个类是自定义控件的类,其最上层父类是View(类似于j2se的object吧);

private View getInstanceView(String className,int resouces){
XmlPullParser parser = this.getResources().getXml(resouces);
AttributeSet attributes = Xml.asAttributeSet(parser);
Object view = null;
try {
Class clazz = Class.forName(className);
Constructor con = clazz.getDeclaredConstructor(Context.class,AttributeSet.class);
view = con.newInstance(MainView.this,attributes);
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return (View)view;
}

再用一个简单的反射装载好了类(也就是具体类和xml的匹配以及实例化),然后再装入list容器中

作用就是对于viewpager的设置吧,当时写这个就是这样打算的;是不是觉得很方便呢? 要是有多个view的话就很简单灵活的可以装载了,而且此类方法复用性相当高;
 

实例二 通过反射获取控件对象

@ViewInject(R.id.pull_to_refresh_listview)
private PullToRefreshListView pullToRefreshListView;


不再使用一下代码初始化


pullToRefreshListView = (PullToRefreshListView) findViewById(R.id.pullToRefreshListView);

实现方式(网上有很多代码):一个interface和一个class

interface

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


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

class

import android.app.Activity;
import java.lang.reflect.Field;


@SuppressWarnings("unused")
public class ViewInjectClass {
public static void autoInjectAllField(Activity activity) {
//得到Activity对应的Class
Class clazz = activity.getClass();
//得到该Activity的所有字段
Field[] fields = clazz.getDeclaredFields();
try {
for (Field field : fields) {
//判断字段是否标注InjectView
if (field.isAnnotationPresent(ViewInject.class)) {
//如果标注了,就获得它的id
ViewInject inject = field.getAnnotation(ViewInject.class);
int id = inject.value();
if (id > 0) {
//反射访问私有成员,必须加上这句
field.setAccessible(true);
try {
//然后对这个属性复制
field.set(activity, activity.findViewById(id));
} catch (Exception ex) {
}
}
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
}


使用方式及建议: 统一写一个BaseActivity


import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import com.anykaalibrary.ViewInjectClass;


public class BaseActivity extends AppCompatActivity {

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}

@Override
public void setContentView(int layoutResID) {
super.setContentView(layoutResID);
ViewInjectClass.autoInjectAllField(this);
}
}


public class MainActivity extends BaseActivity {

@ViewInject(R.id.pull_to_refresh_listview)
private PullToRefreshListView pullToRefreshListView;

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

}
}

其实我并不建议这样使用,我倒是希望开发工具能够根据类似这种代码,在编译前自动将代码改成
 pullToRefreshListView = (PullToRefreshListView) findViewById(R.id.pullToRefreshListView);
这种形式。

 

 

 

 


延伸阅读:

巧妙利用反射机制得到ListView中的view

我们在使用ListView的时候,经常会使用的ViewHolder方式作为缓存,每次都需要手动的通过viewholder.icon = convertview.findVIewByid(id);一般的app都会有好几个页面会用到ListView,这样的话我们的代码量就会很大,有没有好点的办法呢,其实今天学习了反射机制,利用所有的控件的超类都是View这个特点,循环给ViewHolder里边的控件赋值,下边是具体代码:
 

public final class ResouceHandleUtil {
public interface ReflectResouce{
public View findViewById(int viewId);
}
/**
* 注意Java中只有值传递。
* @param target
* @param mconContext
*/
public static final void reflectView(ReflectResouce target ,Context mContext){
Field[] fields = target.getClass().getDeclaredFields();//得到所有属性,注意target.getClass() 得到是具体堆内存里边的对象的类型
for (Field field : fields) {
final String fieldName = field.getName();//得到属性名称
View childView = target.findViewById(getResoucesId(mContext, fieldName));//得到view控件对象
try {
if(View.class.isAssignableFrom(childView.getClass())){//如果childView是View的子类
boolean accessible = field.isAccessible();//得到属性的访问权限
field.setAccessible(true);//打开修改权限
try {
field.set(target, childView);//这一步是关键,最后的结果是target.field=childView
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
field.setAccessible(accessible);
}
} catch (Exception e) {
new IllegalArgumentException("the childView is not the child of View").printStackTrace();
}
}
}
/**
* get the id value of view, According to view name
* @param mContext
* @param fName
* @return 根据反射得到view的id值
*/
public static final int getResoucesId(Context mContext ,String fName){
return mContext.getResources().getIdentifier(fName, "id", mContext.getPackageName());
}


/**
* 适配器的通用性使用泛型
* @author Administrator
*
* @param <P>
*/
public abstract class CommonAdapter<P> extends BaseAdapter {
private List<P> data = null;
protected Context mContext = null;
protected LayoutInflater layoutInflater = null;
public CommonAdapter(List<P> list, Context mContext){
this.data = list;
this.mContext = mContext;
layoutInflater = LayoutInflater.from(this.mContext);
}
@Override
public int getCount() {
return data==null?0:data.size();
}

@Override
public P getItem(int position) {
return (data==null||data.size()==0)?null:data.get(position);
}

@Override
public long getItemId(int position) {
return position;
}

public void setDatas(List<P> da){
this.data = da;
notifyDataSetChanged();
}

public void appendData(List<P> da){
if(this.data == null){
setDatas(da);
}else {
this.data.addAll(da);
notifyDataSetChanged();
}
}
}
public class StudentAdapter extends CommonAdapter<Students> {

public StudentAdapter(Context mContext, List<Students> list) {
super(list, mContext);
}

@SuppressLint("InflateParams")
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder = null;
if (convertView == null) {
convertView = layoutInflater.inflate(R.layout.student_item, null);
viewHolder = new ViewHolder(convertView);
ResouceHandleUtil.reflectView(viewHolder, mContext);
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
Students student = getItem(position);
viewHolder.head_icon.setImageResource(R.drawable.ic_launcher);
viewHolder.banji_view.setText(student.getBanji());
viewHolder.name_view.setText(student.getName());
return convertView;
}

class ViewHolder implements ReflectResouce {
private View rootView;

public ViewHolder(View rootView) {
this.rootView = rootView;
}

@Override
public View findViewById(int viewId) {
return rootView.findViewById(viewId);
}

/**
* 注意下边的属性名字必须和xml里边的id名字一样
*/
private ImageView head_icon;
private TextView name_view, banji_view;
}
}
}

student_item.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<ImageView
android:id="@+id/head_icon"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_marginRight="10dp" />

<TextView
android:id="@+id/name_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/head_icon"
android:maxLength="10"
android:singleLine="true"
android:textSize="14sp" />
<TextView
android:id="@+id/banji_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/name_view"
android:layout_toRightOf="@id/head_icon"
android:maxLength="10"
android:singleLine="true"
android:textSize="14sp" />

</RelativeLayout>

这样的话 ListView的适配性很好的扩展性能,ViewHolder中view的具体值很好的封装起来了。


注解,反射在android View,Event中的奇妙实现

此博文产生的背景:

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.架构核心代码

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);
}
}
}
}
}
}
}

}
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>();

public static void addEventMethod(
ViewFinder finder,
ViewInjectInfo info,
Annotation eventAnnotation,
Object handler,
Method method) {
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) {
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);
listener = Proxy.newProxyInstance(
listenerType.getClassLoader(),
new Class<?>[]{listenerType},
dynamicHandler);
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
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
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;
}
}
}

部分解释:

a.第一处:injectObject(Object handler, ViewFinder finder)

此方法是注射View,事件的核心方法,该方法主要完成的任务是,获取View的引用,对View进行事件绑定

b.第二处:

addEventMethod(
            ViewFinder finder,
            ViewInjectInfo info,
            Annotation eventAnnotation,
            Object handler,
            Method method)

是真正实现对View事件的注入

c.第三处: dynamicHandler = (DynamicHandler) Proxy.getInvocationHandler(listener);

是从动态代理对象中获取被代理对象

d.第四处:

listener = Proxy.newProxyInstance(
                            listenerType.getClassLoader(),
                            new Class<?>[]{listenerType},
                            dynamicHandler);

运行时生成对象,实现动态代理,动态代理与静态代理不理解的可以查一下资料

e.第五处: invoke(Object proxy, Method method, Object[] args) throws Throwable

当用户作用在View的事件时,代理对象调用此方法,进而被代理对象调用被注入的方法

5.实战

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.运行效果: 

 

Android中用反射获取View巧妙利用反射机制得到ListView中的view_android