Sometimes there is no next time, no time-outs, no second chances; sometimes it's now or never.java
對於註解來講,其實就是用來打標記用的,在別的地方能夠根據特殊的標記獲得標記的東西(屬性、類、方法等),對於這你們應該很是熟悉了,不少第三方框架使用也巨多,好比Dagger、ButterKnife等等,這些第三方註解框架,省略了findviewById(),不由要問,難道真的不須要這行findviewById()代碼就能在xml中獲得控件實例嗎?答案很明確,確定不是如此。本篇文章就會給出答案,主要介紹自定義註解相關的知識,模仿實現findviewById功能。sql
在開始實現 findviewById 功能以前,仍是複習一下怎麼自定義註解,以及註解有啥做用吧。框架
有這樣一種場景:ide
主要工做是,要寫一些註解類來標識SxtStudent類,而後經過註解把類中的信息跟表對應起來。好比上面SxtStudent類中,id爲int類型,對應SQL中int(10);name爲String類型,對應SQL中的varchar(10).。方式就是經過註解對類中的信息進行標註,寫一箇中間程序獲取這個類中的註解信息,把註解解析出來,而後剛好能夠做爲SQL語句中拼接。工具
首先定義JavaBean類:this
public class YdlStudent { private int id; private String studentName; private int age; public int getId() { return id; } public void setId(int id) { this.id = id; } public String getStudentName() { return studentName; } public void setStudentName(String studentName) { this.studentName = studentName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
而後定義類與表對應轉換的註解:code
/** * 針對表的註解 * 表明類與表之間對應轉換 */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.TYPE}) public @interface YdlTable { String value(); }
由於表只須要一個表名便可,因此註解中只須要一個信息,這裏使用String value();標識。xml
這個時候就能夠對類進行註解了,這裏對類的註解能夠經過反射技術獲取註解信息,而後做爲建立的表名稱。對象
而後還能夠對屬性添加註解,定義與屬性關聯的註解類:get
/** * 針對屬性的註解 */ @Retention(RetentionPolicy.RUNTIME) @Target(value = {ElementType.FIELD}) public @interface YdlFiled { String columName();//列名 String type();//數據類型 int length();//年齡 }
這裏註解裏面的內容是根據Student類和SQL來寫的,SQL中的列名稱定義爲String columName();SQL中的數據類型爲String type();SQL中的數據類型佔的長度定義爲int length();
而後在YdlStudent類中對屬性添加註解:
這裏
@YdlFiled(columName = "id",type = "int",length = 10)
private int id;
理解爲:YdlStudent中屬性id對應表中的字段id這一列,sql中的類型爲int,int長度爲10。
@YdlFiled(columName = "sname",type = "vachar",length = 10)
private String studentName;
理解爲:YdlStudent中屬性studentName對應表中的字段sname這一列,sql中的類型爲vachar,vachar長度爲10。
這樣標識之後,就能夠寫一個解析程序來解析YdlStudent類,獲取註解相關信息來建立表了:
public class Main { public static void main(String[] args) { Class clazz = YdlStudent.class; Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { // 獲取全部的【類】註解 System.out.println(annotation); } // 或得類的指定註解 YdlTable table = (YdlTable) clazz.getAnnotation(YdlTable.class); // table.value()直接獲取註解內容名稱---@YdlTable(value = "tb_student")//能夠經過反射讀取這裏,而後建立對應的表 System.out.println(table.value()); try { // 根據名稱獲取屬性對象 Field field = clazz.getDeclaredField("studentName"); // 獲取該屬性上面的註解 YdlFiled ydlFiled = field.getAnnotation(YdlFiled.class); // 獲取註解屬性的內容。 System.out.println(ydlFiled.columName() + "---" + ydlFiled.type() + "---" + ydlFiled.length()); // 同理能夠獲取 YdlStudent 類中 id、age所對應的註解信息 } catch (Exception e) { e.printStackTrace(); } // 根據上方獲取到的表名、屬性(字段)信息等拼接Sql語句。 } }
這裏分段來說:
Class clazz = YdlStudent.class; Annotation[] annotations = clazz.getAnnotations(); for (Annotation annotation : annotations) { // 獲取全部的【類】註解 System.out.println(annotation); }
這裏表示經過反射獲取全部的類註解,因類註解只有一個:
因此打印結果爲:@itydl03.YdlTable(value=tb_student)
// 或得類的指定註解 YdlTable table = (YdlTable) clazz.getAnnotation(YdlTable.class); // table.value()直接獲取註解內容名稱---@YdlTable(value = "tb_student")//能夠經過反射讀取這裏,而後建立對應的表 System.out.println(table.value());
表示經過反射獲取類的指定註解,clazz.getAnnotation(YdlTable.class);表示指定獲取YdlTable類的註解。而後table.value()直接獲取註解內容名稱---@YdlTable(value = "tb_student")。獲取的內容就是tb_student,打印結果:
tb_student
try { // 根據名稱獲取屬性對象 Field field = clazz.getDeclaredField("studentName"); // 獲取該屬性上面的註解 YdlFiled ydlFiled = field.getAnnotation(YdlFiled.class); // 獲取註解屬性的內容。 System.out.println(ydlFiled.columName() + "---" + ydlFiled.type() + "---" + ydlFiled.length()); // 同理能夠獲取 YdlStudent 類中 id、age所對應的註解信息 } catch (Exception e) { e.printStackTrace(); }
clazz.getDeclaredField("studentName");表示根據名稱獲取屬性對象,返回值獲得一個Field field 表示屬性 。 在YdlStudent類中 private String studentName; 屬性對應的註解爲
@YdlFiled(columName = "sname",type = "vachar",length = 10)
field.getAnnotation(YdlFiled.class);就表示獲上邊屬性YdlFiled註解類中的@YdlFiled(columName = "sname",type = "vachar",length = 10)的全部註解信息;返回值爲YdlFiled類型。他能夠理解爲columName = "sname",type = "vachar",length = 10的註解信息的javaBean。最後打印:
sname---vachar---10
最後再把全部log給出:
二、仿造findviewById()
有了上邊的基礎回顧,下面就不寫那麼細緻了。
首先定義一個註解類,ViewById:
@Target(value={ElementType.FIELD})//FIELD表示的是成員變量級別可使用該註解 @Retention(RetentionPolicy.RUNTIME)//RUNTIME級別能夠被反射讀取註解,都是運行時 public @interface ViewById { int value(); }
而後在MainActivity中使用該註解:
public class MainActivity extends AppCompatActivity { @ViewById(R.id.tv) private TextView mTextView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mTextView.setText("自定義註解"); } }
看着好像挺熟悉的,跟使用第三方框架差很少,可是運行程序確定報錯。這是由於MainActivity類跟自定義註解壓根就沒有什麼鏈接關係。那麼接下來就創建鏈接關係。而這個中介就是反射。
自定義ViewUtils類:
public class ViewUtils { public static void inject(Activity activity) { // 1.獲取全部的屬性 Field[] fields = activity.getClass().getDeclaredFields(); // 2.過濾關於 ViewById 屬性 for (Field field : fields) { //獲取註解封裝---ViewById類型的註解。至關於對ViewById所標識屬性的封裝類 ViewById viewById = field.getAnnotation(ViewById.class); if(viewById != null){ // 3.findViewById View view = activity.findViewById(viewById.value()); field.setAccessible(true); try { // 4.反射注入 // activity 屬性所在類,view 表明的是屬性的值 field.set(activity,view); } catch (IllegalAccessException e) { e.printStackTrace(); } } } } }
代碼註釋很是詳細了,主要功能就是反射解析註解,而後在這裏進行了findviewById操做獲取View,最後再注入到屬性對象裏面field.set(activity,view);
此時須要在MainActivity中加入以下代碼:
ViewUtils.inject(this);
表示注入當前的MainActivity,只有注入了才能使用自定義的註解「框架」。
爲了更直觀,把MainActivity中代碼稍微修改以下:
public class MainActivity extends AppCompatActivity { //註解屬性,解析類就是解析這裏帶註解的屬性 @ViewById(R.id.tv) private TextView mTextView; @ViewById(R.id.tv2) private TextView mTextView2; //非註解屬性 private int age; private String name; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ViewUtils.inject(this); mTextView.setText("自定義註解"); mTextView2.setText("我是SuperMan"); } }
其中private int age;和private String name;兩個屬性雖然在註解解析工具類會獲取到,可是咱們已經對其作了過濾處理。此時運行程序:
到此爲止,註解相關的知識就講解完了。