Android依赖注入的实现

1. 目标

通过Java注解代替常规的findViewById()初始化View,和setOnClickListener()设置监听器。

最终实现效果MainActivity.java代码如下:

package com.example.myframework;

import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;

import com.myframework.activity.BaseActivity;
import com.myframework.annotation.ListenerType;
import com.myframework.annotation.ViewId;
import com.myframework.annotation.ViewListener;

public class MainActivity extends BaseActivity {

	@ViewId(id = R.id.tv_hello)
	private TextView tvHello;
	@ViewId(id = R.id.btn_ok)
	private Button btnOK;
	@ViewId(id = R.id.et1)
	private EditText et1;
	@ViewId(id = R.id.et2)
	private EditText et2;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		tvHello.setText("Hello World");
	}

	@ViewListener(viewId = R.id.btn_ok, type = ListenerType.CLICK)
	private void btnOKOnClick(View v) {
		tvHello.setText("OnClick");
	}

	@ViewListener(viewId = R.id.btn_ok, type = ListenerType.LONG_CLICK)
	private boolean btnOKOnLongClick(View v) {
		tvHello.setText("OnLongClick");
		return false;
	}

	@ViewListener(viewId = R.id.et1, type = ListenerType.FOCUS_CHANGE)
	private void et1OnFocusChange(View v, boolean hasFocus) {
		if (hasFocus) {
			tvHello.setText("EditText1 has Focus");
			et1.setText("EditText1 has Focus");
		} else {
			et1.setText("EditText1 loses Focus");
		}
	}

	@ViewListener(viewId = R.id.et2, type = ListenerType.FOCUS_CHANGE)
	private void et2OnFocusChange(View v, boolean hasFocus) {
		if (hasFocus) {
			tvHello.setText("EditText2 has Focus");
			et2.setText("EditText2 has Focus");
		} else {
			et2.setText("EditText2 loses Focus");
		}
	}
}

2. Java注解

要实现上述目标,先要学会Java Annotation的使用。

2.1 注解的类型

1.基本Annotation

@Override  限定重写父类方法

@Deprecated  标示已过时

@Suppress Warnings  抑制编译器警告

@SafeVarargs  


2.JDK的元Annotation

@Retention

用于修饰Annotation,指定被修饰的Annotation可以保留多长时间。@Retention包含一个RetentionPolicy类型的value成员变量,使用@Retention时必须为该value赋值。

value的值有如下3种:

RetentionPolicy.CLASS  编译器将把Annotation记录在class文件中。当运行Java程序时,JVM不再保留Annotation。默认值。

RetentionPolicy.RUNTIME  编译器将把Annotation记录在class文件中。当运行Java程序时,JVM保留Annotation,程序可以通过反射获取该Annotation的信息。

RetentionPolicy.SOURCE  Annotation只保留在源代码中。

@Target

指定修饰类型,如ANNOTATION_TYPE, CONSTRUCTOR, FIELD, LOCAL_VARIABLE, METHOD, PACKAGE, PARAMETER, TYPE。

@Documented

指定被该元Annotation修饰的Annotation类将被javadoc工具提取成文档。

@Inherited

指定被它修饰的Annotation将具有继承性。如果某个类使用了@A修饰,则其子类将自动被@A修饰。


3.自定义Annotation

以本文实例代码中使用的自定义注解@ViewId、@ViewListener为例。

@ViewId注解类成员变量,所以@Target为FIELD。该注解的成员变量id为被注解View的id。使用@ViewId的程序要在运行时获取id信息,所以@Retention的value=RetentionPolicy.RUNTIME。

ViewId.java代码如下:

package com.myframework.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(value=ElementType.FIELD)
public @interface ViewId {
	int id();
}
@ViewListener注解类方法,用于指定事件监听的响应方法。其有两个成员变量viewId和type,分别表示某个view的特定类型监听器。

ViewListener.java代码如下:

package com.myframework.annotation;

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

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ViewListener {
	int viewId();
	ListenerType type();
}
ListenerType为enum类型。

ListenerType.java如下:

package com.myframework.annotation;

public enum ListenerType {
	CLICK,
	LONG_CLICK,
	FOCUS_CHANGE  
}

3. Android依赖注入实现

3.1 实现一个继承于Activity的BaseActivity

若某个Activity需要View的依赖注入,则需继承BaseActivity。BaseActivity重载setContentView(int layoutResID)方法, 并添加initView()方法和setListener()方法。

public class BaseActivity extends Activity {
	public static final String TAG = "BaseActivity";

	...

	@Override
	public void setContentView(int layoutResID) {
		super.setContentView(layoutResID);
		initView();
		setListener();
	}

	private void initView() {
		...
	}
	
	private void setListener() {
		...
	}
	
	...
}


3.2 实现initView()方法

public class BaseActivity extends Activity {
    ...
    private void initView() {
        Class<?> cls = this.getClass();
        // 获取类的所有Fields
        Field[] fields = cls.getDeclaredFields();
        for (Field f : fields) {
            // 遍历所有的Fields,处理被@ViewId修饰的Field
            if (f.isAnnotationPresent(ViewId.class)) {
                f.setAccessible(true);
                ViewId viewId = f.getAnnotation(ViewId.class);
                // 得到@ViewId的成员变量id值
                int id = viewId.id();
                try {
                    // 相当于View v = findViewById(id);
                    f.set(this, findViewById(id));
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

3.3 实现setListener()方法

private void setListener() {
	Class<?> cls = this.getClass();
	// 获取类所有的Methods
	Method[] methods = cls.getDeclaredMethods();
	for (Method m : methods) {
		// 遍历所有的Methods,处理被@ViewListener修饰的Method
		if (m.isAnnotationPresent(ViewListener.class)) {
			ViewListener viewListener = m.getAnnotation(ViewListener.class);
			// 获取@ViewListener的成员变量id值,并获取相关View
			View v = findViewById(viewListener.viewId());
			// 获取@ViewListener的成员变量type值,并根据监听器类型为该View设置监听器。
			ListenerType type = viewListener.type();
			switch (type) {
			case CLICK:
				setOnClickListener(v, m);
				break;
			case LONG_CLICK:
				setOnLongClickListener(v, m);
				break;
			case FOCUS_CHANGE:
				setOnFocusChangeListener(v, m);
				break;
			}
		}
	}
}


3.4 设置监听器方法的实现。

以setOnClickListener(View v, Method m)方法的实现为例。实现原理,将所有的CLICK方法添加进mOnClickMethodArray,该值为SparseArray类型,key为View的id,value为onClick事件处理方法。当执行OnClickListener的onClick(View v)方法时,根据View的id获取相应的执行方法。相关代码如下:

public class BaseActivity extends Activity {
    public static final String TAG = "BaseActivity";

    private OnClickListener mOnClickListener;

    // OnClick事件处理方法数组
    private SparseArray<Method> mOnClickMethodArray;

    private void setOnClickListener(View v, Method m) {
        if (mOnClickListener == null) {
            // 初始化mOnClickListener
            mOnClickListener = new OnClickListener() {

                @Override
                public void onClick(View v) {
                    onClickImp(v);
                }

            };
            // 初始化OnClick事件处理方法数组
            mOnClickMethodArray = new SparseArray<Method>();
        }
        // 将OnClick事件处理方法m添加进数组,key为View的id。
        mOnClickMethodArray.append(v.getId(), m);
        v.setOnClickListener(mOnClickListener);
    }

    private void onClickImp(View v) {
        // 根据id从OnClick事件处理方法数组mOnClickMethodArray中取出要执行的方法
        Method m = mOnClickMethodArray.get(v.getId());
        // 将该方法设为可访问
        m.setAccessible(true);
        try {
            // 调用该方法
            m.invoke(this, v);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (IllegalArgumentException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
    }

}


4. 总结

BaseActivity.java的完整代码如下:

package com.myframework.activity;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import android.app.Activity;
import android.util.SparseArray;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnLongClickListener;

import com.myframework.annotation.ListenerType;
import com.myframework.annotation.ViewId;
import com.myframework.annotation.ViewListener;

public class BaseActivity extends Activity {
	public static final String TAG = "BaseActivity";

	private OnClickListener mOnClickListener;
	private OnLongClickListener mOnLongClickListener;
	private OnFocusChangeListener mOnFocusChangeListener;
	
	// OnClick事件处理方法数组
	private SparseArray<Method> mOnClickMethodArray;
	// OnLongClick事件处理方法数组
	private SparseArray<Method> mOnLongClickMethodArray;
	// OnFocusChange事件处理方法数组
	private SparseArray<Method> mOnFocusChangeMethodArray;

	@Override
	public void setContentView(int layoutResID) {
		super.setContentView(layoutResID);
		initView();
		setListener();
	}

	private void initView() {
		Class<?> cls = this.getClass();
		// 获取类的所有Fields
		Field[] fields = cls.getDeclaredFields();
		for (Field f : fields) {
			// 遍历所有的Fields,处理被@ViewId修饰的Field
			if (f.isAnnotationPresent(ViewId.class)) {
				f.setAccessible(true);
				ViewId viewId = f.getAnnotation(ViewId.class);
				// 得到@ViewId的成员变量id值
				int id = viewId.id();
				try {
					// 相当于View v = findViewById(id);
					f.set(this, findViewById(id));
				} catch (Exception e) {
					e.printStackTrace();
				}
			}
		}
	}
	
	private void setListener() {
		Class<?> cls = this.getClass();
		// 获取类所有的Methods
		Method[] methods = cls.getDeclaredMethods();
		for (Method m : methods) {
			// 遍历所有的Methods,处理被@ViewListener修饰的Method
			if (m.isAnnotationPresent(ViewListener.class)) {
				ViewListener viewListener = m.getAnnotation(ViewListener.class);
				// 获取@ViewListener的成员变量id值,并获取相关View
				View v = findViewById(viewListener.viewId());
				// 获取@ViewListener的成员变量type值,并根据监听器类型为该View设置监听器。
				ListenerType type = viewListener.type();
				switch (type) {
				case CLICK:
					setOnClickListener(v, m);
					break;
				case LONG_CLICK:
					setOnLongClickListener(v, m);
					break;
				case FOCUS_CHANGE:
					setOnFocusChangeListener(v, m);
					break;
				}
			}
		}
	}
	
	private void setOnClickListener(View v, Method m) {
		if (mOnClickListener == null) {
			// 初始化mOnClickListener
			mOnClickListener = new OnClickListener() {
	
				@Override
				public void onClick(View v) {
					onClickImp(v);
				}
	
			};
			// 初始化OnClick事件处理方法数组
			mOnClickMethodArray = new SparseArray<Method>();
		}
		// 将OnClick事件处理方法m添加进数组,key为View的id。
		mOnClickMethodArray.append(v.getId(), m);
		v.setOnClickListener(mOnClickListener);
	}
	
	private void onClickImp(View v) {
		// 根据id从OnClick事件处理方法数组mOnClickMethodArray中取出要执行的方法
		Method m = mOnClickMethodArray.get(v.getId());
		// 将该方法设为可访问
		m.setAccessible(true);
		try {
			// 调用该方法
			m.invoke(this, v);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}

	private void setOnLongClickListener(View v, Method m) {
		if (mOnLongClickListener == null) {
			mOnLongClickListener = new OnLongClickListener() {

				@Override
				public boolean onLongClick(View v) {
					return onLongClickImp(v);
				}

			};
			mOnLongClickMethodArray = new SparseArray<Method>();
		}
		mOnLongClickMethodArray.append(v.getId(), m);
		v.setOnLongClickListener(mOnLongClickListener);
	}

	private boolean onLongClickImp(View v) {
		Method m = mOnLongClickMethodArray.get(v.getId());
		m.setAccessible(true);
		try {
			return (boolean) m.invoke(this, v);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
		return false;
	}

	private void setOnFocusChangeListener(View v, Method m) {
		if (mOnFocusChangeListener == null) {
			mOnFocusChangeListener = new OnFocusChangeListener() {

				@Override
				public void onFocusChange(View v, boolean hasFocus) {
					onFocusChangeImp(v, hasFocus);
				}

			};
			mOnFocusChangeMethodArray = new SparseArray<Method>();
		}
		mOnFocusChangeMethodArray.append(v.getId(), m);
		v.setOnFocusChangeListener(mOnFocusChangeListener);
	}

	private void onFocusChangeImp(View v, boolean hasFocus) {
		Method m = mOnFocusChangeMethodArray.get(v.getId());
		m.setAccessible(true);
		try {
			m.invoke(this, v, hasFocus);
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		}
	}
}


  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值