一般我們在寫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:說明了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<Activity> activityClass = (Class<Activity>) 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);
}
}
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);
}
}
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();
}
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<Activity> activityClass = (Class<Activity>) 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();
}
}
}
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<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
android:id="@+id/test2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="test2"
android:textAllCaps="false"/>
</LinearLayout>
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();
}
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();
}
}
}
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;
}
}
}
本站转载的文章为个人学习借鉴使用,本站对版权不负任何法律责任。如果侵犯了您的隐私权益,请联系我们删除。