注解支持的版本
Android support library從19.1版本開始就引入了一個新的注解庫。
添加支持注解庫依賴項
要在您的項目中啟用注解,請向您的庫或應用添加 support-annotations 依賴項。
支持注解庫是Android 支持庫的一部分。要向您的項目添加注解,您必須下載支持存儲庫并向 build.gradle文件中添加 support-annotations依賴項。
添加支持注解庫依賴項
1,打開 SDK 管理器,方法是點擊工具欄中的 SDK Manager或者選擇 Tools > Android > SDK Manager;
2,點擊 SDK Tools 標簽;
3,展開 Support Repository 并選中 Android Support Repository 復選框;
4,點擊 OK;
5,將以下代碼行添加到 build.gradle 文件的 dependencies 塊中,向您的項目添加 support-annotations 依賴項:
dependencies { compile 'com.android.support:support-annotations:24.2.0' }
6,Sync Now;
PS:
如果您使用appcompat庫,則無需添加support-annotations依賴項。因為 appcompat庫已經依賴注解庫。
那有關注解的環境配置就已經構建好了,那接下來就是創建自己的注解的時候了。
介紹注解
在實現自己的注解時,我們需要來了解其中兩個注解。
@Retention(RetentionPolicy.*)
Retention,保存策略。傳入的RetentionPolicy參數有如下三種:
package java.lang.annotation;
/**
* Annotation retention policy. The constants of this enumerated type
* describe the various policies for retaining annotations. They are used
* in conjunction with the {@link Retention} meta-annotation type to specify
* how long annotations are to be retained.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum RetentionPolicy {
/**
* Annotations are to be discarded by the compiler.
*/
SOURCE,
/**
* Annotations are to be recorded in the class file by the compiler
* but need not be retained by the VM at run time. This is the default
* behavior.
*/
CLASS,
/**
* Annotations are to be recorded in the class file by the compiler and
* retained by the VM at run time, so they may be read reflectively.
*
* @see java.lang.reflect.AnnotatedElement
*/
RUNTIME
}
PS:
1,SOURCR:只在*.java源文件的時候有效;
2,CLASS:只在*.java或者*.class中的文件有效,但是在運行環境無效;
3,RUNTIME:包含以上兩種,并且運行時也會有效果,一般我們都會選用該參數。
@Target(ElementType.*)
Target,注解的目標類型。傳入的ElementType類型有如下幾種:
package java.lang.annotation;
/**
* A program element type. The constants of this enumerated type
* provide a simple classification of the declared elements in a
* Java program.
*
* <p>These constants are used with the {@link Target} meta-annotation type
* to specify where it is legal to use an annotation type.
*
* @author Joshua Bloch
* @since 1.5
*/
public enum ElementType {
/** Class, interface (including annotation type), or enum declaration */
TYPE,
/** Field declaration (includes enum constants) */
FIELD,
/** Method declaration */
METHOD,
/** Formal parameter declaration */
PARAMETER,
/** Constructor declaration */
CONSTRUCTOR,
/** Local variable declaration */
LOCAL_VARIABLE,
/** Annotation type declaration */
ANNOTATION_TYPE,
/** Package declaration */
PACKAGE,
/**
* Type parameter declaration
*
* @since 1.8
* @hide 1.8
*/
TYPE_PARAMETER,
/**
* Use of a type
*
* @since 1.8
* @hide 1.8
*/
TYPE_USE
}
PS:
1,TYPE:作用于接口、類、枚舉、注解;
2,FIELD:作用于成員變量(字段、枚舉的常量);
3,METHOD:作用于方法;
4,PARAMETER:作用于方法的參數;
5,CONSTRUCTOR:作用于構造函數;
6,LOCAL_VARIABLE:作用于局部變量;
7,ANNOTATION_TYPE:作用于Annotation。例如如下代碼:
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Retention {
RetentionPolicy value();
}
8,PACKAGE:作用于包名;
9,TYPE_PARAMETER:java8新增,但無法訪問到;
10,TYPE_USE:java8新增,但無法訪問到;
下面做個DEMO,用注解來做以下兩件事:
1,查找控件;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 根據ID查看View的注解 2017/4/14 08:54
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FindViewById {
// 使用value命名,則使用的時候可以忽略,否則使用時就得把參數名加上 2017/4/14 08:57
int value();
}
2,設置點擊事件;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 設置點擊事件 2017/4/14 09:20
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SetOnClickListener {
int id();
String methodName();
}
PS:
1,變量命名,使用value命名,則使用的時候可以忽略,否則使用時就得把參數名加上,所以我在兩個注解上分別使用了不同的變量方式,下面使用的時候,大家記得看清楚。
使用自定義注解
1,在布局文件添加Button,如下:
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/btnSay"
android:text="Hello World!" />
2,在Activity中,申明Button變量以及click方法,如下:
@FindViewById(R.id.btnSay)
@SetOnClickListener(id = R.id.btnSay, methodName = "click")
private Button _btnSay;
/**
* 點擊事件 2017/4/14 10:00
*/
public void click() {
Toast.makeText(this, "Hello world", Toast.LENGTH_SHORT).show();
}
3,創建注解解析器,代碼如下:
import android.app.Activity;
import android.view.View;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
/**
* Annotation解析 2017/4/14 09:00
*/
public class AnnotationParse {
/**
* 解析 2017/4/14 09:01
* @param target 解析目標
*/
public static void parse(final Activity target) {
try {
Class<?> clazz = target.getClass();
Field[] fields = clazz.getDeclaredFields(); // 獲取所有的字段 2017/4/14 09:34
FindViewById byId;
SetOnClickListener clkListener;
View view;
String name;
String methodName;
int id;
for (Field field : fields){
Annotation[] annotations = field.getAnnotations();
for(Annotation annotation:annotations) {
if (annotation instanceof FindViewById) {
byId = field.getAnnotation(FindViewById.class); // 獲取FindViewById對象 2017/4/14 09:10
field.setAccessible(true); // 反射訪問私有成員,必須進行此操作 2017/4/14 09:13
name = field.getName(); // 字段名 2017/4/14 09:18
id = byId.value();
// 查找對象 2017/4/14 09:15
view = target.findViewById(id);
if (view != null)
field.set(target, view);
else
throw new Exception("Cannot find.View name is " + name + ".");
} else if (annotation instanceof SetOnClickListener) { // 設置點擊方法 2017/4/14 09:59
clkListener = field.getAnnotation(SetOnClickListener.class);
field.setAccessible(true);
// 獲取變量 2017/4/14 10:00
id = clkListener.id();
methodName = clkListener.methodName();
name = field.getName();
view = (View) field.get(target);
if (view == null) { // 如果對象為空,則重新查找對象 2017/4/14 09:45
view = target.findViewById(id);
if (view != null)
field.set(target, view);
else
throw new Exception("Cannot find.View name is " + name + ".");
}
// 設置執行方法 2017/4/14 09:55
Method[] methods = clazz.getDeclaredMethods();
boolean isFind = false;
for (final Method method:methods) {
if (method.getName().equals(methodName)) {
isFind = true;
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
try {
method.invoke(target);
} catch (Exception e) {
e.printStackTrace();
}
}
});
break;
}
}
// 沒有找到 2017/4/14 09:59
if (!isFind) {
throw new Exception("Cannot find.Method name is " + methodName + ".");
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
PS:
上面的注解解析器注釋比較詳細,仔細看下,應該就可以讀懂上面的解析器作用。它大概做了以下幾個事情:
a,通過field.getAnnotation(FindViewById.class);反射獲得FindViewById注解對象,進而得到我們設置的參數值R.id.btnSay,再通過Target對象去查找控件,并設置;
b,同1,SetOnClickListener也是這樣設置,只不過多了一個通過反射獲得的方法,并調用的過程;
c,field.setAccessible(true);一定要設置,因為反射訪問的是私有成員變量,不設置會報異常;
4,調用
在OnCreate()方法中,如下調用:
// 解析注解 2017/4/14 09:22
AnnotationParse.parse(this);
this._btnSay.setText("CCB"); // 只是做下改變
5,演示效果
總結
總上面的注解解析器中,可以看得出來注解Annotation往往是跟反射配合起來使用的,就連Android現在都很多地方使用到了注解,例如AppcompatActivity類:
就寫框架的人也常說,無反射,不框架,那現在其實也可以說,無反射,不注解。
所以,要想自己有所進階,學會創建使用自己的Annotaion就是基礎中的基礎了。
跟著上面的思路走,那么就應該能正常理解并使用Annotation庫了.