android註解入門 並來自己寫一個框架
阿新 • • 發佈:2019-01-25
介紹
如下程式碼:
fragment
package a.fmy.com.myapplication;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import a.fmy.com.mylibrary.FmyClickView;
import a.fmy.com.mylibrary.FmyContentView;
import a.fmy.com.mylibrary.FmyViewInject;
import a.fmy.com.mylibrary.FmyViewView;
//你的fragment的佈局id Your fragment's LayoutId
@FmyContentView(R.layout.fragment_blank)
public class BlankFragment extends Fragment {
//你想例項化控制元件的id
//Do you want to control instance id
// 等價於 findViewByid
//Equivalent to the findViewByid
@FmyViewView(R.id.tv1)
TextView tv1;
@FmyViewView(R.id.tv2)
TextView tv2;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
//初始化fragment Initialize Fragement
return FmyViewInject.injectfragment(this ,inflater,container);
}
//你想給哪個控制元件新增 新增事件 的id
//Do you want to add add event id to which controls
@FmyClickView({R.id.tv1,R.id.tv2})
public void myOnclick(View view){
switch (view.getId()) {
case R.id.tv1:
tv1.setText("TV1 "+Math.random()*100);
break;
case R.id.tv2:
tv2.setText("TV2 "+Math.random()*100);
break;
default:
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
Activity
package a.fmy.com.myapplication;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v7.app.AppCompatActivity;
import android.widget.FrameLayout;
import a.fmy.com.mylibrary.FmyContentView;
import a.fmy.com.mylibrary.FmyViewInject;
import a.fmy.com.mylibrary.FmyViewView;
@FmyContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@FmyViewView(R.id.fl)
FrameLayout fl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//initActivity
// 初始化activity
FmyViewInject.inject(this);
}
@Override
protected void onResume() {
super.onResume();
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
fragmentTransaction.add(R.id.fl,new BlankFragment());
fragmentTransaction.commit();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
java註解學習
java註解教學大家點選進入大致的看一下即可 不然我不知道這篇部落格需要寫多久
activity設定填充佈局框架
這裡我們先寫一個用於activity框架 你學習完了之後其實你也會fragment了.
1. 實現activity不需要呼叫setContentView(R.layout.activity_main);此方法完成佈局填充 我們看下效果
不使用框架:
package a.fmy.com.mylibrary;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
使用框架:
package a.fmy.com.mylibrary;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
@FmyContentView(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FmyViewInject.inject(this);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
第一步:
建立一個註解類如下
@Target —>>此註解在什麼地方可以使用 如類還是變數
ElementType.TYPE只能在類中使用此註解
@Retention(RetentionPolicy.RUNTIME) 註解可以在執行時通過反射獲取一些資訊(這裡如果你疑惑那麼請六個懸念繼續向下看)
/**
* 此方註解寫於activity類上 可以免去 setContentView()步驟
* @author 範明毅
* @version 1.0
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FmyContentView {
/**
* 儲存佈局檔案的id eg:R.layout.main
* @return 返回 佈局id
*/
int value();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
第二步:
寫一個工具類 配合註解使用 當開發者使用此類時啟用註解的作用
public class FmyViewInject {
/**
* 儲存傳入的activity
*/
private static Class<?> activityClass;
/**
* 初始化activity和所有註解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
}
/**
* 初始化activity佈局檔案 讓其不用呼叫setContentView
*
* @param activity
*/
private static void injectContent(Object obj) {
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
大家先不用著急看不懂為什麼這樣寫原因
核心原始碼位於injectContent 我們來實現此方法
/**
* 初始化activity佈局檔案 讓其不用呼叫setContentView
*
* @param activity
*/
private static void injectContent(Object obj) {
// 獲取註解
FmyContentView annotation = activityClass
.getAnnotation(FmyContentView.class);
if (annotation != null) {
// 獲取註解中的對應的佈局id 因為註解只有個方法 所以@XXX(YYY)時會自動賦值給註解類唯一的方法
int id = annotation.value();
try {
// 得到activity中的方法 第一個引數為方法名 第二個為可變引數 型別為 引數型別的位元組碼
Method method = activityClass.getMethod("setContentView",
int.class);
// 呼叫方法 第一個引數為哪個例項去掉用 第二個引數為 引數
method.invoke(obj, id);
} catch (Exception e) {
e.printStackTrace();
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
此方法寫完後工具類的inject()方法呼叫即可
/**
* 初始化activity和所有註解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局檔案
injectContent(obj);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
完整程式碼:
public class FmyViewInject {
/**
* 儲存傳入的activity
*/
private static Class<?> activityClass;
/**
* 初始化activity和所有註解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局檔案
injectContent(obj);
}
/**
* 初始化activity佈局檔案 讓其不用呼叫setContentView
*
* @param activity
*/
private static void injectContent(Object obj) {
// 獲取註解
FmyContentView annotation = activityClass
.getAnnotation(FmyContentView.class);
if (annotation != null) {
// 獲取註解中的對應的佈局id 因為註解只有個方法 所以@XXX(YYY)時會自動賦值給註解類唯一的方法
int id = annotation.value();
try {
// 得到activity中的方法 第一個引數為方法名 第二個為可變引數 型別為 引數型別的位元組碼
Method method = activityClass.getMethod("setContentView",
int.class);
// 呼叫方法 第一個引數為哪個例項去掉用 第二個引數為 引數
method.invoke(obj, id);
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
趕快去試試 我們繼續寫下一步 用法在開始的示例有
activity查詢控制元件
效果如下
@FmyContentView(R.layout.activity_main)
public class MainActivity extends FragmentActivity {
//直接例項化
@FmyViewView(R.id.fl)
private FrameLayout fl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FmyViewInject.inject(this);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
第一步:
繼續寫一個註解
/**
* 此方註解寫於activity類中 控制元件變數上 可以省去findViewId 的煩惱
* @author 範明毅
* @version 1.0
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FmyViewView {
/**
* 儲存view控制元件的id
* @return view控制元件id
*/
int value();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
第二步 繼續第一節的”activity設定填充佈局框架”中的工具類新增新的方法
/**
* 初始化activity中的所有view控制元件 讓其不用一個findViewid 例項化
*
* @param activity
*/
private static void injectView(Object activityOrFragment) {
// 物件所有的屬性
Field[] declaredFields = null;
// 健壯性
if (activityClass != null) {
// 獲取du所有的屬性 包含私有 保護 預設 共開 但不包含繼承等
// getFields可以獲取到所有公開的包括繼承的 但無法獲取到私有的屬性
declaredFields = activityClass.getDeclaredFields();
}
// 健壯性
if (declaredFields != null) {
// 遍歷所有的屬性變數
for (Field field : declaredFields) {
// 獲取屬性變數上的註解
FmyViewView annotation = field.getAnnotation(FmyViewView.class);
// 如果此屬性變數 包含FMYViewView
if (annotation != null) {
// 獲取屬性id值
int id = annotation.value();
Object obj = null;
try {
// 獲取activity中方法
obj = activityClass.getMethod("findViewById",
int.class).invoke(activityOrFragment, id);
Log.e("FMY", "" + field.getClass());
// 設定屬性變數 指向例項
// 如果修飾符不為公共類 這裡注意了 當activity
// 控制元件變數為private的時候 我們去訪問會失敗的 要麼打破封裝系 要麼變數改為public
//如 private TextView tv 這種情況 如果不打破封裝會直接異常
if (Modifier.PUBLIC != field.getModifiers()) {
// 打破封裝性
field.setAccessible(true);
}
// 這裡相當於 field= acitivity.obj
field.set(activityOrFragment, obj);
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
第三步
在工具類中的inject ()方法呼叫
/**
* 初始化activity和所有註解
*
* @param obj 你需要初始化的activity
*/
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局檔案
injectContent(obj);
// 初始化所有控制元件例項 省去findViewId的痛苦
injectView(obj);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
activity設定控制元件的點選事件
這裡需要的知識點 如動態代理等 這裡大家可以自己百度看下
效果如下
@FmyContentView(R.layout.activity_main)
public class MainActivity extends FragmentActivity {
@FmyViewView(R.id.fl)
private FrameLayout fl;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FmyViewInject.inject(this);
}
//當填充的佈局中 id為R.id.fl 被點選將呼叫如下方法
@FmyClickView({R.id.fl})
public void onClick(View v){
Log.e("fmy", "===>>");
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
第一步 :
同樣寫下一個註解
/**
*
* 設定點選事件的註解 只需要在某方法 上寫上此註解即可 如@FmyClickView({R.id.bt1,R.id.bt2})
* @version 1.0
* @author 範明毅
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FmyClickView {
/**
* 儲存所有需要設定點選事件控制元件的id
* @return
*/
int [] value();
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
第二步:
寫下一個代理處理類(我寫在工具類中)
/**
* 代理處理點選邏輯程式碼
*
* @author 範明毅
*
*/
static class MInvocationHandler implements InvocationHandler {
//這裡我們到時候回傳入activity
private Object target;
// 使用者自定義view 的點選事件方法
private Method method;
public MInvocationHandler(Object target, java.lang.reflect.Method method) {
super();
this.target = target;
this.method = method;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
// 呼叫使用者自定義方法的點選事件 讓activity呼叫中開發者設定的方法
return this.method.invoke(target, args);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
第三步:
在工具類中寫下一個方法用於初始化點選事件
/**
* 初始化所有控制元件的點選事件 只需要某方法上寫上對應註解和id即可
*
* @param activity
*/
private static void inijectOnClick(Object activityOrFragment) {
//獲得所有方法
Method[] methods = null;
methods = activityClass.getMethods();
// 遍歷所有的activity下的方法
for (Method method : methods) {
// 獲取方法的註解
FmyClickView fmyClickView = method
.getAnnotation(FmyClickView.class);
// 如果存在此註解
if (fmyClickView != null) {
// 所有註解的控制元件的id
int[] ids = fmyClickView.value();
// 代理處理類
MInvocationHandler handler = new MInvocationHandler(activityOrFragment,
method);
// 代理例項 這裡也可以返回 new Class<?>[] { View.OnClickListener.class }中的介面類
//第一個引數用於載入其他類 不一定要使用View.OnClickListener.class.getClassLoader() 你可以使用其他的
//第二個引數你所實現的介面
Object newProxyInstance = Proxy.newProxyInstance(
View.OnClickListener.class.getClassLoader(),
new Class<?>[] { View.OnClickListener.class }, handler);
// 遍歷所有的控制元件id 然後設定代理
for (int i : ids) {
try {
Object view = null;
//如果物件是activity
view = activityClass.getMethod("findViewById",
int.class).invoke(activityOrFragment, i);
if (view != null) {
Method method2 = view.getClass().getMethod(
"setOnClickListener",
View.OnClickListener.class);
method2.invoke(view, newProxyInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
第四部:
在工具類的inject()呼叫即可
/**
* 初始化activity和所有註解
*
* @param obj
* 你需要初始化的activity
*/
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局檔案
injectContent(obj);
// 初始化所有控制元件例項 省去findViewId的痛苦
injectView(obj);
// 初始化所有控制元件的點選事件
inijectOnClick(obj);
}