這裏我帶你們來學習一下註解 而且用來寫下一個模仿xUtils3 中View框架
此框架 能夠省略activity或者fragment的 findViewById 或者設置點擊事件的煩惱
我正參加2016CSDN博客之星的比賽 但願您能投下寶貴的一票,點擊進入投票
個人github上的源碼,包含doc和使用說明html
以下代碼:java
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:
}
}
}
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();
}
}
java註解教學你們點擊進入大體的看一下便可 否則我不知道這篇博客須要寫多久android
這裏咱們先寫一個用於activity框架 你學習完了以後其實你也會fragment了.
1. 實現activity不須要調用setContentView(R.layout.activity_main);此方法完成佈局填充 咱們看下效果
不使用框架:git
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);
}
}
使用框架:github
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);
}
}
第一步:
建立一個註解類以下
@Target —>>此註解在什麼地方可使用 如類仍是變量
ElementType.TYPE只能在類中使用此註解
@Retention(RetentionPolicy.RUNTIME) 註解能夠在運行時經過反射獲取一些信息(這裏若是你疑惑那麼請六個懸念繼續向下看)markdown
/** * 此方註解寫於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();
}
第二步:
寫一個工具類 配合註解使用 當開發者使用此類時激活註解的做用app
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) {
}
}
你們先不用着急看不懂爲何這樣寫緣由框架
核心源碼位於injectContent 咱們來實現此方法dom
/** * 初始化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();
}
}
此方法寫完後工具類的inject()方法調用便可ide
/** * 初始化activity和全部註解 * * @param obj * 你須要初始化的activity */
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局文件
injectContent(obj);
}
完整代碼:
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();
}
}
}
趕快去試試 咱們繼續寫下一步 用法在開始的示例有
效果以下
@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);
}
}
第一步:
繼續寫一個註解
/** * 此方註解寫於activity類中 控件變量上 能夠省去findViewId 的煩惱 * @author 範明毅 * @version 1.0 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FmyViewView {
/** * 保存view控件的id * @return view控件id */
int value();
}
第二步 繼續第一節的」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();
}
}
}
}
}
第三步
在工具類中的inject ()方法調用
/** * 初始化activity和全部註解 * * @param obj 你須要初始化的activity */
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局文件
injectContent(obj);
// 初始化全部控件實例 省去findViewId的痛苦
injectView(obj);
}
這裏須要的知識點 如動態代理等 這裏你們能夠本身百度看下
效果以下
@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", "===>>");
}
}
第一步 :
一樣寫下一個註解
/** * * 設置點擊事件的註解 只須要在某方法 上寫上此註解便可 如@FmyClickView({R.id.bt1,R.id.bt2}) * @version 1.0 * @author 範明毅 * */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface FmyClickView {
/** * 保存全部須要設置點擊事件控件的id * @return */
int [] value();
}
第二步:
寫下一個代理處理類(我寫在工具類中)
/** * 代理處理點擊邏輯代碼 * * @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);
}
}
第三步:
在工具類中寫下一個方法用於初始化點擊事件
/** * 初始化全部控件的點擊事件 只須要某方法上寫上對應註解和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();
}
}
}
}
}
第四部:
在工具類的inject()調用便可
/** * 初始化activity和全部註解 * * @param obj * 你須要初始化的activity */
public static void inject(Object obj) {
activityClass = obj.getClass();
// 初始化activity佈局文件
injectContent(obj);
// 初始化全部控件實例 省去findViewId的痛苦
injectView(obj);
// 初始化全部控件的點擊事件
inijectOnClick(obj);
}