Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發學習之路--Annotation注解簡化view控件之初體驗

Android開發學習之路--Annotation注解簡化view控件之初體驗

編輯:關於Android編程

一般我們在寫android Activity的時候總是會在onCreate方法中加上setContentView方法來加載layout,通過findViewById來實現控件的綁定,每次寫這麼多代碼總覺得很煩躁。近來看了下android中有Annotation來實現這方面的簡化,對於java不是很了解,就簡單的看了下。上次玩web的時候,springmvc也有很多的注解,不知道怎麼實現的,這裡其實基本上類似。

Annotation注解這裡主要還是講講怎麼使用吧,單純的原理會把人繞進去的,沒辦法,java基礎只能後面再補了,c搞久了,很多面向對象的思想只停留在大學的時候,除了linux內核的一些面向對象的思想。說了那麼多的廢話,接著繼續我們的Annotation的學習吧,先新建工程emAnnotationStudy,新建EMLayoutBinder.java,代碼如下:

package com.jared.emannotationstudy;

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

/**
 * Created by jared on 16/3/10.
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface EMLayoutBinder {
    int value();
}
這裡的@Target,@Retention和@interface,先來簡單的介紹下這幾個內容吧。

@Target:說明了Annotation修飾的對象范圍,Annotation可被用於packages、types等類,接口,枚舉,Annotation類型;還可以是類成員方法,構造方法,成員變量,枚舉值;方法參數和本地變量等。其一般有如下幾種類型:

ElementType.CONSTRUCTOR: 構造器聲明;

ElementType.FIELD: 成員變量、對象、屬性;

ElementType.LOCAL_VARIABLE: 局部變量聲明;

ElementType.METHOD: 方法聲明;

ElementType.PACKAGE: 包聲明;

ElementType.PARAMETER: 參數聲明;

ElementType.TYPE: 類、接口(包括注解類型)或enum聲明;

這用到了TYPE。

@Retention:表示在什麼級別保存該注解信息。其一般級別如下:

RetentionPolicy.SOURCE: 停留在java源文件,編譯器被丟掉。

RetentionPolicy.CLASS: 停留在class文件中,但會被VM丟棄。

RetentionPolicy.RUNTIME:內存中的字節碼,VM將在運行時也保留注解,因此可以通過反射機制讀取注解的信息。

這裡給了最高級別RUNTIME。

@interface:這個就表示注解了,和interface很像,不過多了一個@符號。

int value():表示傳入的參數是int類型的。

好了,既然定義好了那麼怎麼使用呢?單單一個注解怎麼個搞搞?其實注解一般都是和java的反射原理一起使用的。還是簡單學習下java的反射吧,網上資料很多,這裡就簡單理解理解了。在java中的反射機制,被稱為Reflection,它允許運行中的java程序對自身進行檢查,並能直接操作程序的內部屬性或方法。

利用Reflection APIs可以獲取任何已知名稱的類的內部信息,包括package、type parameters、superclass、implemented interfaces、inner classes、outer classes、fields constructors、methods、modifiers等。

Class: 表示某個具體的類或接口

Object: 每個類都使用Object 做為超類,所有對象都實現這個類的方法

Constructor:封裝了Class的構造方法

Field: 提供有關類或接口的屬性信息,以及對它的動態訪問權限

Method: 提供類或者接口上的方法的信息

Modifier: 封裝了Class(method、fields)的修飾域。

簡單了解下java的發射機制,其實反射就是通過反向調用類的一些功能,可能會覺得很難理解,還是繼續我們的學習吧,新建EMAnnotationParser類:

 

package com.jared.emannotationstudy;

import android.app.Activity;
import android.view.View;

import java.lang.reflect.Field;

/**
 * Created by jared on 16/3/10.
 */
public class EMAnnotationParser {
    public static void injectActivity(Activity activity) {
        if (null == activity) {
            return;
        }
        Class activityClass = (Class) activity.getClass();
        if (isEMLayoutBinder(activityClass)) {
            EMLayoutBinder layout = activityClass.getAnnotation(EMLayoutBinder.class);
            activity.setContentView(layout.value());
        }
        View decorView = activity.getWindow().getDecorView();
    }

    private static boolean isEMLayoutBinder(Class c) {
        return c.isAnnotationPresent(EMLayoutBinder.class);
    }

這裡實現了injectActivity的方法,通過getClass獲取當前的Activity的class,然後通過isAnnotationPresent查看該Annotation,再通過getAnnotation獲取該注解,接著就是把注解傳入的那個layout通過activity的setContentView方法來加載到activity 中了。好了,那麼我們來實現下MainActivity吧:

package com.jared.emannotationstudy;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

@EMLayout(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EMAnnotationParser.injectActivity(this);
        //setContentView(R.layout.activity_main);
    }
}
去掉了setContentView,直接一個@EMLayout就搞定了,是不是很方便,這裡通過EMAnnotationParser的injectActivity方法。其實一般項目中會定義一個BaseActivity,MainActivity通過繼承BaseActivity來實現,那樣看上去會更加的清晰,那就實現下BaseActivity吧:
package com.jared.emannotationstudy;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;

/**
 * Created by jared on 16/3/10.
 */
public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        EMAnnotationParser.injectActivity(this);
    }
}
然後實現MainActivity繼承BaseActivity:
package com.jared.emannotationstudy;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

@EMLayoutBinder(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @EMViewBinder(R.id.hello)
    private TextView mHello;
    @EMViewBinder(R.id.test1)
    private Button mTest1;
    @EMViewBinder(R.id.test2)
    private Button mTest2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHello.setText("Hello Annotation!");
    }
}

運行後依然沒有任何問題。既然layout通過注解了,那麼控件也是可以通過注解的,接下去就去實現下了。首先新建Annotation為EMViewBinder:

package com.jared.emannotationstudy;

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

/**
 * Created by jared on 16/3/10.
 */

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EMViewBinder {
    int value();
}

這裡就很好理解了,和LayoutBinder一樣,只是target是FIFLD,因為是成員的變量。接著簡單實現下反射:
package com.jared.emannotationstudy;

import android.app.Activity;
import android.view.View;

import java.lang.reflect.Field;

/**
 * Created by jared on 16/3/10.
 */
public class EMAnnotationParser {
    public static void injectActivity(Activity activity) {
        if (null == activity) {
            return;
        }
        Class activityClass = (Class) activity.getClass();
        if (isEMLayoutBinder(activityClass)) {
            EMLayoutBinder layout = activityClass.getAnnotation(EMLayoutBinder.class);
            activity.setContentView(layout.value());
        }
        View decorView = activity.getWindow().getDecorView();
        initViews(activityClass.getDeclaredFields(), decorView, activity);
    }

    private static boolean isEMLayoutBinder(Class c) {
        return c.isAnnotationPresent(EMLayoutBinder.class);
    }

    private static boolean isEMViewBinder(Field filed) {
        return filed.isAnnotationPresent(EMViewBinder.class);
    }

    private static void initViews(Field[] fields, View view, Object object) {
        View view1;
        for (Field field : fields) {
            if(isEMViewBinder(field)) {
                EMViewBinder emView = field.getAnnotation(EMViewBinder.class);
                view1 = view.findViewById(emView.value());
                if(null != view1) {
                    try {
                        field.setAccessible(true);
                        field.set(object, view1);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (IllegalArgumentException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

這裡通過getDecorView來獲取view,通過getDeclaredFields來獲取Fields,然後通過getAnnotation來獲取EMViewBinder注解,接著調用findViewById來找到這個控件。如果找到了,那麼需要調用setAccessible為true,因為變量一般都是private的。大概的意思就這樣了,下面我們修改下layout:
<!--{cke_protected}{C}%3C!%2D%2D%3Fxml%20version%3D%221.0%22%20encoding%3D%22utf-8%22%3F%2D%2D%3E-->
<linearlayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:layout_margin="10dp" tools:context="com.jared.emannotationstudy.MainActivity">

    <textview android:id="@+id/hello" android:text="Hello World!" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textsize="20sp" android:layout_gravity="center"><button android:id="@+id/test1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="test1" android:textallcaps="false"></button><button android:id="@+id/test2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="test2" android:textallcaps="false"></button></textview></linearlayout>
接著我們在MainActivity中添加代碼如下:
package com.jared.emannotationstudy;

import android.os.Bundle;
import android.widget.Button;
import android.widget.TextView;

@EMLayoutBinder(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @EMViewBinder(R.id.hello)
    private TextView mHello;
    @EMViewBinder(R.id.test1)
    private Button mTest1;
    @EMViewBinder(R.id.test2)
    private Button mTest2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHello.setText("Hello Annotation!");
    }
}
運行看下效果如下:

\

完全達到了我們的預期,而且編寫代碼十分方便,不需要再引入一大堆的findViewById了。即使再添加更多的控件也輕松搞定。既然控件綁定好了,那麼接下去還需要做的就是事件的綁定了。這裡主要實現button的事件,新建EMOnClickBinder:

package com.jared.emannotationstudy;

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

/**
 * Created by jared on 16/3/10.
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface EMOnClickBinder {
    int[] value();
}
這裡實現的是方法,所以使用了METHOD,而且button可能很多,所以用了int[] 數組。接著我們實現具體的反射:
private static boolean isEMOnClickBinder(Method method) {
        return method.isAnnotationPresent(EMOnClickBinder.class);
    }

    private static void initOnClick(Method[] allMethod, View root, Object object) {
        for (Method method : allMethod) {
            if (isEMOnClickBinder(method)) {
                EMOnClickBinder onClick = method.getAnnotation(EMOnClickBinder.class);
                MyOnClickListener click = new MyOnClickListener(method, object);
                int[] ids = onClick.value();
                for (int id : ids) {
                    root.findViewById(id).setOnClickListener(click);
                }
            }
        }
    }

    static class MyOnClickListener implements View.OnClickListener {
        private Method mMethod;
        private Object mReceiver;

        public MyOnClickListener(Method method, Object receiver) {
            mMethod = method;
            mReceiver = receiver;
        }

        @Override
        public void onClick(View v) {
            try {
                mMethod.setAccessible(true);
                mMethod.invoke(mReceiver, v);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (IllegalArgumentException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }
        }
    }
這裡使用ID查找,調用setOnClickListener方法來注冊方法,通過MyOnClickListener來實現具體的操作。當有事件觸發的時候會調用onClick方法,進而調用method的invoke方法。就會調用到注解下的自定義方法了,這裡傳入的就是View。接著具體MainActivity的實現如下:
package com.jared.emannotationstudy;

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

@EMLayoutBinder(R.layout.activity_main)
public class MainActivity extends BaseActivity {

    @EMViewBinder(R.id.hello)
    private TextView mHello;
    @EMViewBinder(R.id.test1)
    private Button mTest1;
    @EMViewBinder(R.id.test2)
    private Button mTest2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mHello.setText("Hello Annotation!");
    }

    @EMOnClickBinder({R.id.test1, R.id.test2})
    public void myOnClick(View view) {
        switch (view.getId()) {
            case R.id.test1:
                mHello.setText("I am test1");
                break;
            case R.id.test2:
                mHello.setText("I am test2");
            default:
                break;
        }
    }
}
是不是非常的簡單清晰,以後把這幾個文件當作工具,簡單封裝下,就可以不用每次寫那麼多的findViewById和setOnClickListener了。基本上Annotation就先學習到這裡了。
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved