Android中用反射获取View

本文介绍了两种在Android开发中利用反射和多态提高代码效率的方法。一是通过反射和多态动态创建并填充自定义View,简化ViewPager的设置;二是使用注解和反射自动注入View,避免手动查找并初始化控件。此外,还展示了如何通过反射机制批量处理ListView中ViewHolder的控件,提高适配器的灵活性。
摘要由CSDN通过智能技术生成

实例一 反射+多态:生成容器实例化并装好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的具体值很好的封装起来了。

源码地址: http://download.csdn.net/detail/zhanwei_30/8842573


注解,反射在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.运行效果: 

 

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值