Android 註解 (Annotation)

      本文題目叫Android反射,其實題目起得稍微有點問題,也可理解成java 反射,但是呢,本文是應用在Android環境上的,示例也以Android示例爲主;其次,Android先在已經有了官方AnnotationProcessor編譯時解析實現,已繼承在API中,由於它是編譯時執行,固然不會出現反射中的性能消耗;

反射還不太瞭解的童鞋,你可以先了解以下 java反射

 

一、什麼是註解

      註解(Annotation),也叫元數據。一種代碼級別的說明。它是JDK1.5及以後版本引入的一個特性,與類、接口、枚舉是在同一個層次。它可以聲明在包、類、字段、方法、局部變量、方法參數等的前面,用來對這些元素進行說明,註釋;

java元註解:(元註解,簡單理解,就是註解上的註解)

@Override                  它的作用是對覆蓋超類中方法的方法進行標記
@Deprecated             它的作用是對不應該再使用的方法添加註解
@SuppressWarnings 它的作用是對當前方法或字段異常時的警告

二、簡單使用註解

2.1 瞭解JDK1.5後加的內部註解

//基類
public abstract class Base {

	public abstract void one();
	
	@Deprecated     //表示該方法爲過時方法,過時不再支持使用
	public void two(){};
 
}

//繼承
public class Base2 extends Base {

	@SuppressWarnings("deprecation")
	@Override //表示該方法時繼承父類的方法
	public void one() {
         //當調備註"過時"標籤時,系統會提示有警報,然後調用SuppressWarnings可以取消工具上的報警狀態
	 super.two(); 
        }  
}

 下圖爲沒加SuppressWarnings標籤的樣子

 

2.2 自定義註解的簡單使用

package annot;

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

/**
 * 自定義註解
 * @author mwt 
 * 
 * 注意了,自定義註解的標誌是“@interface”,要和接口interface區分開噢
 */

//如下代碼,示例中的@Retention指定了什麼時候加載註解類,RetentionPolicy.RUNTIME爲運行時
@Retention(RetentionPolicy.RUNTIME)

//@Target指定當前註解可以加在哪些地方,案例中就是加載到類和方法
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface Test1 {

	//default 表示整型id默認值爲123
	public int id() default 123; 
	// 字符串類型
	public String message() default "默認值";
	
}


//-----------------------註解使用-------------------------------

package annot;

/**
 * 對自定義註解的使用
 * @author mwt 
 */
@Test1(message="自定義註解開始使用了") //改變單個屬性值
public class TestUse {

	public static void main(String[] args) { 
		
		//改行代碼是用來判斷當前 “TestUse”類是否加了Test1註解
		boolean hasAnnotation = TestUse.class.isAnnotationPresent(Test1.class);
		//經過判斷如果加了註解,則進入下一步
        if (hasAnnotation) {
        	//該行代碼是用來獲取當前註解
        	Test1 testAnnotation = TestUse.class.getAnnotation(Test1.class);
        	//獲取當前註解中的屬性
            System.out.println("value:"+testAnnotation.id()+"  \nmessage"+testAnnotation.message()); 
          }
        }

}


//----------------日誌輸出

value:123  
message自定義註解開始使用了

 

三、在Android中簡單使用註解(基於反射)

3.1 首先我們先完成兩個自定義註解;分別是對layout和view的註解

package cn.annto;

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

/**
 * 自定義註解 - layout
 * Created by mwt on 2018/9/26.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
public @interface LayoutBind {

    //當只有一個屬性的時候,可以使用value來表示,在使用時可以省略掉key和=
    public int value();
}


//---------------------------view 註解----------------------

package cn.annto;

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

/**
 * 自定義註解 -
 * Created by mwt on 2018/9/26.
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD,ElementType.PARAMETER})
public @interface ViewBind {

    //當只有一個屬性的時候,可以使用value來表示,在使用時可以省略掉key和=
    public int value();
}

3.2 寫一個解讀activity中註解的工具類

 

package cn.annto;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;

import java.lang.reflect.Field;

/**
 * Created by mwt on 2018/9/26.
 * 爲什麼要寫成抽象類呢,因爲我可以強制性繼承類重寫該方法
 */

public abstract class ActivityUntil extends AppCompatActivity {

    private int layoutId;

    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        initLayout();
        initView();
        doWrok();
    }

    protected abstract void doWrok();

    /**
     * 該方法主要就是從註解中獲取到註解入的值
     */
    private void initLayout() {
        //首先判斷一下是否屬於當前註解的子類 ;如果是提供給別人使用的話,務必要有判空操作
        boolean isannotation = this.getClass().isAnnotationPresent(LayoutBind.class);
        if (isannotation){
            //獲取當前的註解
            LayoutBind annotation = getClass().getAnnotation(LayoutBind.class);
            //獲取註解中的值,並將該值賦給全局變量layoutid,然後供setContentView使用
            layoutId = annotation.value();
            //吧layout佈局引入
            setContentView(layoutId);
        }
    }

    /**
     * 初始化activity中生命的字段
     */
    private void initView() {
        //獲取當前class對象
        Class<? extends ActivityUntil> aClass = this.getClass();
        //獲取到該class中自定義的所有字段
        Field[] declaredFields = aClass.getDeclaredFields();
        //便利所有的字段
        for (Field field: declaredFields) {
            //所有的字段便利出來了,但是我們只需要那些被加過註解的字段
            if(null != field.getAnnotations() && field.isAnnotationPresent(ViewBind.class)){
                field.setAccessible(true);//所有字段都可訪問修改
                //獲取字段上的註解類
                ViewBind viewBind = field.getAnnotation(ViewBind.class);
                //獲取該字段通過註解注入  值,也就是 R.id.textview .....
                int viewid = viewBind.value();
                //現在字段獲取到了,對應view 的id也獲取到了,那就應該賦值了
                try {
                    field.set(this,findViewById(viewid));
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                    Log.e("error","not find view id");
                }
            }
        }
    }
}

3.3 自定義註解和解讀工具類都具備了,讓我們來使用一把吧

package cn.annto;

import android.widget.TextView;

@LayoutBind(R.layout.activity_main)
public class MainActivity extends ActivityUntil {

    @ViewBind(R.id.one)
    public TextView oneText;
    @ViewBind(R.id.two)
    public TextView twoText;


    @Override
    protected void doWrok() {
        oneText.setText("註解測試");
        twoText.setText("註解通過了");
    }
}

運行效果:

本文中第三節Android demo 在github上有完整示例,https://github.com/mawantong/annto/tree/master

 

      

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章