告别findViewById,android IOC初探

本文介绍了如何通过反转控制IOC(Inversion of Control)简化Android应用界面的布局与事件处理过程,包括注解的定义与使用、反射机制的应用,以及在Activity中的实际应用示例。

一个android app界面由布局加控件组成,初期写代码,都会大量的使用findViewById和setOnClickeListener,控件较多时就会显得繁琐冗余。

示例如下:       

 userName = (EditText)this.findViewById(R.id.userName);
userPwd = (EditText)this.findViewById(R.id.userPwd);
CheckBox checkBox = (CheckBox) findViewById(R.id.login_check);
mClear1 = (ImageButton)findViewById(R.id.clearName);
        mClear2 = (ImageButton)findViewById(R.id.clearPwd);
  
        loginBtn = (Button)this.findViewById(R.id.loginBtn);
   Button register = (Button)this.findViewById(R.id.registerBtn);
   loginBtn.setOnClickListener(new LoginOnClickListener());

今天我们要讨论的内容则可以很好的解决这个问题,那就是大名鼎鼎的反转控制IOC(Inversion of Control,声明控件变量时使用注解(详见http://blog.youkuaiyun.com/beyond0851/article/details/8520993),利用反射(详见http://blog.youkuaiyun.com/tiananma0607/article/details/49002089)实现注入

了解注解:

从jdk1.5开始,Java提供了注解的功能,允许开发者定义和使用自己的注解类型,该功能由一个定义注解类型的语法和描述一个注解声明的语法,读取注解的API,一个使用注解修饰的class文件和一个注解处理工具组成。
首先,你需要接受一个关键字@interface ,噢,它可不是接口定义关键字,更不是OC里面的@interface关键字,是Java中表示声明一个注解类的关键字。
使用@interface 表示我们已经继承了java.lang.annotation.Annotation类,这是一个注解的基类接口,就好像Object类,现在你只需要知道它的存在就行了。
还有一条规定:在定义注解时,不能继承其他的注解或接口。
那么,这就是最简单的一个注解类

?
1
2
3
public  @interface  MyAnnotation {
 
}

然而通常在使用时我们都会给这个注解类加上两个注解:

@Target(ElementType.FIELD)、@Retention(RetentionPolicy.RUNTIME)
ElementType、RetentionPolicy是两个枚举类,ElementType.FIELD表示我们需要注解的是一个字段,以下是摘自JDK1.6文档中的介绍:

使用注解:

以下为KJFrameForAndroid框架中绑定控件注解部分的定义与使用:

?
1
2
3
4
5
6
@Target (ElementType.FIELD)
@Retention (RetentionPolicy.RUNTIME)
public  @interface  BindView {
     public  int  id();
     public  boolean  click()  default  false ;
}
?
1
2
@BindView (id = R.id.x, click =  true )
private  TextView t;

我们可以看到,除了明显减少了代码量,还使得代码结构更加清晰。
其中,定义部分的id() 表示注解接受一个int类型的数据作为id所对应的值(就如使用中的id = R.id.xxx);
同理,定义部分的click表示接受一个Boolean类型的数据作为click对应的值,还可以设置一个默认值使用default修饰;

处理注解:

我们已经知道了注解怎么定义和使用,接下来就应该知道怎么处理了。
上面已经说了,bindview注解可以接受一个int类型的值和一个Boolean类型的值,那么这两个值接受了以后如何获取呢?
其实获取的方式很简单就是通过一个BindView类型的对象,调用这个对象来自声明中定义的两个方法——>id()或click()方法。
现在就有一个问题了,注解类型是不能直接new对象的,那么这个BindView对象从哪里来呢?
这时就需要用到Java的反射机制。我们知道,每一个继承自Object类的类都会继承一个getClass()方法,下面看一下这个方法的原型:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
     /**
      * Returns the unique instance of {@link Class} that represents this
      * object's class. Note that {@code getClass()} is a special case in that it
      * actually returns {@code Class<? extends Foo>} where {@code Foo} is the
      * erasure of the type of the expression {@code getClass()} was called upon.
      * <p>
      * As an example, the following code actually compiles, although one might
      * think it shouldn't:
      * <p>
      * <pre>{@code
      *   List<Integer> l = new ArrayList<Integer>();
      *   Class<? extends List> c = l.getClass();}</pre>
      *
      * @return this object's {@code Class} instance.
      */
     public  final  native  Class<?> getClass();

是一个native方法,根据注释我们知道,这个方法返回的是该类的Class对象,同时也是该类的二进制对象。
Class中有一个方法叫getDeclaredFields(),是用来返回这个类的全部字段,返回类型是Field[]
通过Field对象的getAnnotation(Class<?>)方法,我们可以获取到任何一个Class的对象,通过getAnnotation(Class<?>),我们就可以获取到BindView的对象了。

例如:

?
1
2
3
4
5
6
7
8
9
Field[] fields = currentClass.getClass().getDeclaredFields();
for ( int  i =  0 ; i < fields.length; i++){
 
     BindView bindView = field.getAnnotation(BindView. class );
     
     int  viewId = bindView.id();   //这是我们传的id
     
     boolean  clickLis = bindView.click();  //这是我们传的click
}

在有上面的知识准备后,接下来我们动手写一个类似的IOC框架(同时绑定VIEW和事件)

1,View注解类

// TODO: Auto-generated Javadoc
/**
 * The Interface AfIocView.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME) 
public @interface AfIocView {
	
	/**
	 * Id.
	 *
	 * @return the int
	 */
	public int id();
	
	/**
	 * Click.
	 *
	 * @return the string
	 */
	public String click() default "";
	
	/**
	 * Long click.
	 *
	 * @return the string
	 */
	public String longClick() default "";
	
	/**
	 * Item click.
	 *
	 * @return the string
	 */
	public String itemClick() default "";
	
	/**
	 * Item long click.
	 *
	 * @return the string
	 */
	public String itemLongClick() default "";
	
	/**
	 * Select.
	 *
	 * @return the ab ioc select
	 */
	public AfIocSelect select() default @AfIocSelect(selected="") ;
}
2,Event事件监听类
<pre name="code" class="java">public class AfIocEventListener implements OnClickListener, OnLongClickListener, OnItemClickListener, OnItemSelectedListener,OnItemLongClickListener {

	/** The handler. */
	private Object handler;
	
	/** The click method. */
	private String clickMethod;
	
	/** The long click method. */
	private String longClickMethod;
	
	/** The item click method. */
	private String itemClickMethod;
	
	/** The item select method. */
	private String itemSelectMethod;
	
	/** The nothing selected method. */
	private String nothingSelectedMethod;
	
	/** The item long click mehtod. */
	private String itemLongClickMehtod;
	
	/** The Constant CLICK. */
	public static final int CLICK = 0;
	
	/** The Constant LONGCLICK. */
	public static final int LONGCLICK = 1;
	
	/** The Constant ITEMCLICK. */
	public static final int ITEMCLICK = 2;
	
	/** The Constant ITEMLONGCLICK. */
	public static final int ITEMLONGCLICK = 3;
	
	/**
	 * Instantiates a new ab ioc event listener.
	 *
	 * @param handler the handler
	 */
	public AfIocEventListener(Object handler) {
		this.handler = handler;
	}
	
	/**
	 * Click.
	 *
	 * @param method the method
	 * @return the ab ioc event listener
	 */
	public AfIocEventListener click(String method){
		this.clickMethod = method;
		return this;
	}
	
	/**
	 * Long click.
	 *
	 * @param method the method
	 * @return the ab ioc event listener
	 */
	public AfIocEventListener longClick(String method){
		this.longClickMethod = method;
		return this;
	}
	
	/**
	 * Item long click.
	 *
	 * @param method the method
	 * @return the ab ioc event listener
	 */
	public AfIocEventListener itemLongClick(String method){
		this.itemLongClickMehtod = method;
		return this;
	}
	
	/**
	 * Item click.
	 *
	 * @param method the method
	 * @return the ab ioc event listener
	 */
	public AfIocEventListener itemClick(String method){
		this.itemClickMethod = method;
		return this;
	}
	
	/**
	 * Select.
	 *
	 * @param method the method
	 * @return the ab ioc event listener
	 */
	public AfIocEventListener select(String method){
		this.itemSelectMethod = method;
		return this;
	}
	
	/**
	 * No select.
	 *
	 * @param method the method
	 * @return the ab ioc event listener
	 */
	public AfIocEventListener noSelect(String method){
		this.nothingSelectedMethod = method;
		return this;
	}
	
	/* (non-Javadoc)
	 * @see android.view.View.OnLongClickListener#onLongClick(android.view.View)
	 */
	public boolean onLongClick(View v) {
		return invokeLongClickMethod(handler,longClickMethod,v);
	}
	
	/* (non-Javadoc)
	 * @see android.widget.AdapterView.OnItemLongClickListener#onItemLongClick(android.widget.AdapterView, android.view.View, int, long)
	 */
	public boolean onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
		return invokeItemLongClickMethod(handler,itemLongClickMehtod,arg0,arg1,arg2,arg3);
	}
	
	/* (non-Javadoc)
	 * @see android.widget.AdapterView.OnItemSelectedListener#onItemSelected(android.widget.AdapterView, android.view.View, int, long)
	 */
	public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2,long arg3) {
		
		invokeItemSelectMethod(handler,itemSelectMethod,arg0,arg1,arg2,arg3);
	}
	
	/* (non-Javadoc)
	 * @see android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(android.widget.AdapterView)
	 */
	public void onNothingSelected(AdapterView<?> arg0) {
		invokeNoSelectMethod(handler,nothingSelectedMethod,arg0);
	}
	
	/* (non-Javadoc)
	 * @see android.widget.AdapterView.OnItemClickListener#onItemClick(android.widget.AdapterView, android.view.View, int, long)
	 */
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		
		invokeItemClickMethod(handler,itemClickMethod,arg0,arg1,arg2,arg3);
	}
	
	/* (non-Javadoc)
	 * @see android.view.View.OnClickListener#onClick(android.view.View)
	 */
	public void onClick(View v) {
		
		invokeClickMethod(handler, clickMethod, v);
	}
	
	
	/**
	 * Invoke click method.
	 *
	 * @param handler the handler
	 * @param methodName the method name
	 * @param params the params
	 * @return the object
	 */
	private Object invokeClickMethod(Object handler, String methodName,  Object... params){
		if(handler == null) return null;
		Method method = null;
		try{   
			method = handler.getClass().getDeclaredMethod(methodName,View.class);
			if(method!=null)
				return method.invoke(handler, params);	
			else
				throw new AfAppException("no such method:"+methodName);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return null;
		
	}
	
	
	/**
	 * Invoke long click method.
	 *
	 * @param handler the handler
	 * @param methodName the method name
	 * @param params the params
	 * @return true, if successful
	 */
	private boolean invokeLongClickMethod(Object handler, String methodName,  Object... params){
		if(handler == null) return false;
		Method method = null;
		try{   
			//public boolean onLongClick(View v)
			method = handler.getClass().getDeclaredMethod(methodName,View.class);
			if(method!=null){
				Object obj = method.invoke(handler, params);
				return obj==null?false:Boolean.valueOf(obj.toString());	
			}
			else
				throw new AfAppException("no such method:"+methodName);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return false;
		
	}
	
	/**
	 * Invoke item click method.
	 *
	 * @param handler the handler
	 * @param methodName the method name
	 * @param params the params
	 * @return the object
	 */
	private Object invokeItemClickMethod(Object handler, String methodName,  Object... params){
		if(handler == null) return null;
		Method method = null;
		try{   
			///onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3)
			method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class,View.class,int.class,long.class);
			if(method!=null)
				return method.invoke(handler, params);	
			else
				throw new AfAppException("no such method:"+methodName);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return null;
	}
	
	
	/**
	 * Invoke item long click method.
	 *
	 * @param handler the handler
	 * @param methodName the method name
	 * @param params the params
	 * @return true, if successful
	 */
	private boolean invokeItemLongClickMethod(Object handler, String methodName,  Object... params){
		
		Method method = null;
		try{   
			if(handler == null){
				throw new AfAppException("invokeItemLongClickMethod: handler is null :");
			}
			///onItemLongClick(AdapterView<?> arg0, View arg1, int arg2,long arg3)
			method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class,View.class,int.class,long.class);
			if(method!=null){
				Object obj = method.invoke(handler, params);
				return Boolean.valueOf(obj==null?false:Boolean.valueOf(obj.toString()));	
			}
			else
				throw new AfAppException("no such method:"+methodName);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return false;
	}
	
	
	/**
	 * Invoke item select method.
	 *
	 * @param handler the handler
	 * @param methodName the method name
	 * @param params the params
	 * @return the object
	 */
	private Object invokeItemSelectMethod(Object handler, String methodName,  Object... params){
		if(handler == null) return null;
		Method method = null;
		try{   
			///onItemSelected(AdapterView<?> arg0, View arg1, int arg2,long arg3)
			method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class,View.class,int.class,long.class);
			if(method!=null)
				return method.invoke(handler, params);	
			else
				throw new AfAppException("no such method:"+methodName);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return null;
	}
	
	/**
	 * Invoke no select method.
	 *
	 * @param handler the handler
	 * @param methodName the method name
	 * @param params the params
	 * @return the object
	 */
	private Object invokeNoSelectMethod(Object handler, String methodName,  Object... params){
		if(handler == null) return null;
		Method method = null;
		try{   
			//onNothingSelected(AdapterView<?> arg0)
			method = handler.getClass().getDeclaredMethod(methodName,AdapterView.class);
			if(method!=null)
				return method.invoke(handler, params);	
			else
				throw new AfAppException("no such method:"+methodName);
		}catch(Exception e){
			e.printStackTrace();
		}
		
		return null;
	}
	


 

3,初始化IOC控制,实现View与事件注入

	/**
	 * 初始化为IOC控制的View.
	 */
	private void initIocView(){
		Field[] fields = getClass().getDeclaredFields();
		if(fields!=null && fields.length>0){
			for(Field field : fields){
				try {
					field.setAccessible(true);
					
					if(field.get(this)!= null )
						continue;
				
					AfIocView viewInject = field.getAnnotation(AfIocView.class);
					if(viewInject!=null){
						
						int viewId = viewInject.id();
					    field.set(this,findViewById(viewId));
					
						setListener(field,viewInject.click(),AfIocEventListener.CLICK);
						setListener(field,viewInject.longClick(),AfIocEventListener.LONGCLICK);
						setListener(field,viewInject.itemClick(),AfIocEventListener.ITEMCLICK);
						setListener(field,viewInject.itemLongClick(),AfIocEventListener.ITEMLONGCLICK);
						
						AfIocSelect select = viewInject.select();
						if(!TextUtils.isEmpty(select.selected())){
							setViewSelectListener(field,select.selected(),select.noSelected());
						}
						
					}
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	/**
	 * 设置view的监听器.
	 *
	 * @param field the field
	 * @param select the select
	 * @param noSelect the no select
	 * @throws Exception the exception
	 */
	private void setViewSelectListener(Field field,String select,String noSelect)throws Exception{
		Object obj = field.get(this);
		if(obj instanceof View){
			((AbsListView)obj).setOnItemSelectedListener(new AfIocEventListener(this).select(select).noSelect(noSelect));
		}
	}
	
	/**
	 * 设置view的监听器.
	 *
	 * @param field the field
	 * @param methodName the method name
	 * @param method the method
	 * @throws Exception the exception
	 */
	private void setListener(Field field,String methodName,int method)throws Exception{
		if(methodName == null || methodName.trim().length() == 0)
			return;
		
		Object obj = field.get(this);
		
		switch (method) {
			case AfIocEventListener.CLICK:
				if(obj instanceof View){
					((View)obj).setOnClickListener(new AfIocEventListener(this).click(methodName));
				}
				break;
			case AfIocEventListener.ITEMCLICK:
				if(obj instanceof AbsListView){
					((AbsListView)obj).setOnItemClickListener(new AfIocEventListener(this).itemClick(methodName));
				}
				break;
			case AfIocEventListener.LONGCLICK:
				if(obj instanceof View){
					((View)obj).setOnLongClickListener(new AfIocEventListener(this).longClick(methodName));
				}
				break;
			case AfIocEventListener.ITEMLONGCLICK:
				if(obj instanceof AbsListView){
					((AbsListView)obj).setOnItemLongClickListener(new AfIocEventListener(this).itemLongClick(methodName));
				}
				break;
			default:
				break;
		}
	}
4,在Activity中使用示例如下:

@AfIocView(id = R.id.mBtn,click="btnClick")Button button;
@AfIocView(id = R.id.mText)TextView mTextView;

点击Button控件时就会调用注解里的btnClick方法,从此findViewById和事件处理写起来会简洁清楚许多

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值