《擼代碼 學習 IOC注入技術1 》—— 佈局注入 與 控件注入

不詩意的女程序媛不是好廚師~ 轉載請註明出處,From李詩雨---blog.csdn.net/cjm24848365…php

在這裏插入圖片描述

在前面的文章中咱們已經學習了 依賴注入與控制反轉的概念註解、和反射 ,有了這些知識作鋪墊,咱們就能夠 更加深刻的來學習一下 IOC注入技術了。html

今天咱們主要 來學習運行時注入,並親自擼代碼來一步一步的實現 佈局注入控件注入java

文章的邏輯思路講的很細,也很好懂,沒有什麼難點,而且文章篇幅也不長,不妨一讀哦~android

1.概念再理解

溫故而知新,上篇文章中跟你們提到了 控制反轉(IOC) 和 依賴注入的概念,可能你們仍是有點 花非花霧非霧的 感受,今天通過親自的擼代碼以後,我有了新的體會。在此與你們分享~app

【控制反轉(IOC)】:是原來由程序代碼中主動獲取的資源,轉變由第三方獲取並使原來的代碼被動接收的方式,以達到解耦的效果。ide

按照上篇文章的內容,咱們把它當作是一種控制權的反轉。佈局

但其實,咱們還能夠把它當作是一種義務的轉交,即 把咱們本身應該作的事轉交給別人來作,從而讓本身變得更輕鬆。學習

再舉個形象的栗子來講吧:測試

在一個月黑風高的寒冷的夜晚,你有事要出門,因爲天氣太冷你要披肩大棉襖才能出去,因而你就本身乖乖的拿了棉襖再乖乖的穿好出門,消失在寒冷的黑夜中。this

IOC就是你有了一個女友,你只告訴她你要出門,因而貼心的女友便給你拿來棉衣,幫你穿上,才放心讓你出門。因而你在愛的目光中出了門~

恩,女友就比如IOC,把你原本要拿衣服穿衣服的事情 轉交給了女友來作。

畫個圖來幫助你們理解:

在這裏插入圖片描述
好的,如今咱們就開始擼代碼來學習 IOC注入技術吧~

2.佈局注入

在這裏插入圖片描述

咱們都知道在Activity中咱們 經過本身的 setContentView(R.layout.activity_main)來加入、顯示佈局的。

那若是我如今採用ioc,不是本身來注入佈局,而是讓個人女友來注入佈局,該怎麼作呢?

  • ①首先,我得造一個女友出來!她裏面有佈局注入的方法。
  • ②其次,咱們考慮到可能全部的Activity都要用到,因此,咱們在BaseActivity的onCreat中完成注入。
  • ③MainActivity繼承BaseActivity。而且把setContentView(R.layout.activity_main)這句代碼去掉!
//①造了一個女友
public class InjectUtils {
    public static void inject(Object context) {
        //佈局的注入
        injectLayout(context);
    }

    private static void injectLayout(Object context) {

    }
}
複製代碼
public class BaseActivity extends AppCompatActivity {
    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.inject(this);//②在這裏注入
    }
}
複製代碼
//③繼承BaseActivity ,並去掉setContentView(R.layout.activity_main)這句代碼
public class MainActivity extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //setContentView(R.layout.activity_main);//把這行代碼去掉!讓女友來完成
    }
}
複製代碼

好的,到這裏你們應該都沒有什麼問題吧。

如今你們想一想,咱們既然去掉了setContentView(R.layout.activity_main);這句代碼,那此時咱們的MainActivity是不知道須要哪一個佈局的。

這該怎麼辦?怎麼才能知道MainActivity須要哪一個佈局文件呢?

那咱們就要標識出來咱們所須要的佈局文件呀,那怎麼標識呢?

對!用註解。就像這樣:

在這裏插入圖片描述

那接下來咱們就要來自定義這個註解啦~

  • ④自定義註解MyContentView。
//④自定義註解MyContentView
@Target(ElementType.TYPE)   //代表:註解未來是使用在類上面的
@Retention(RetentionPolicy.RUNTIME) //代表註解的存活週期,咱們但願能夠在運行時讀取到它的信息
public @interface MyContentView {
    int value();
}
複製代碼

好了,到目前爲止,咱們的主要邏輯就完成了。可是,此時運行仍是不能加載出佈局的,由於這仍是個假貨,咱們InjectUtils中的injectLayout()仍是空的,裏面什麼都沒有作。

因此,接下來咱們的重點就是實現injectLayout()方法了。

⑤實現injectLayout()方法:

咱們先來分析一下,在該方法中咱們要作什麼:

首先咱們要明確的是,此處咱們確定要 運用反射 去獲取所需信息和執行對應方法了。

  • 第一步 獲取activity對應的Class
  • 第二步 拿到該Class上的MyContentView註解
  • 第三步 取到註解括號後面的內容,即佈局id

再接下來 就要 反射在class上去執行setContentView了:

  • 第四步 利用反射獲取setContentView()對應的method
  • 第五步 反射執行setContentView()方法。
//⑤實現injectLayout()方法
private static void injectLayout(Object context) {
    // a.獲取到Activity對應的Class
    Class<?> clazz = context.getClass();
    // b.拿到該Class上的MyContentView註解
    MyContentView myContentView = clazz.getAnnotation(MyContentView.class);
    if (myContentView != null) { //若是有MyContentView註解就執行如下操做
        // c.取到註解括號後面的內容,即佈局id
        int layoutId = myContentView.value();
        //====== 接下來就要 反射去執行setContentView
        try {
            // d.利用反射獲取setContentView()對應的method
            Method method = clazz.getMethod("setContentView", int.class);
            // e.反射執行setContentView()方法。即至關於context.method(layoutId);
            method.invoke(context, layoutId);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
複製代碼

好的,那如今我來運行程序,若是能夠正常顯示出來佈局是否是就能夠證實,我注入佈局成功啦!

先給你們看一下個人佈局文件:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical" android:padding="10dp" tools:context=".MainActivity">

    <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center_horizontal" android:text="ioc注入技術,哈哈哈~" />

    <Button android:id="@+id/button1" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按鈕1" />

    <Button android:id="@+id/button2" android:layout_width="match_parent" android:layout_height="wrap_content" android:text="按鈕2" />

</LinearLayout>
複製代碼

好的,下面就是見證奇蹟的時刻啦:

在這裏插入圖片描述

成功啦!我成功啦,啊哈哈哈哈~

完成了佈局注入,那咱們下面繼續控件注入吧~

3.控件注入

上面咱們的佈局已經注入成功,而且能夠正常顯示了。

咱們能夠看到佈局中有2個按鈕,那若是我想把這兩個按鈕注入該怎麼辦呢?

即:我如今不想本身經過findViewById來注入按鈕,而是想讓個人【ioc女友】來幫我實現按鈕的注入~

咱們先來看一下個人預期想達到的效果:

在這裏插入圖片描述

那要達到這種效果咱們該怎麼實現呢?

有了佈局注入的經驗,相信對於 控件注入 你們仍是會有大致的思路的:

咱們還用以前的女友InjectUtils,仍是在BaseActivity中進行注入。

那咱們就要在InjectUtils裏添加一個控件注入的方法injectView():

在這裏插入圖片描述

如今咱們既然不想本身使用findViewById來獲取控件,而是想用這種形式來注入控件:

在這裏插入圖片描述

那咱們確定仍是要經過使用註解,而且在註解後面傳入對應控件的id。

因此第①步,咱們要自定義一個BindView註解:

//① 自定義一個BindView註解
@Target(ElementType.FIELD) //說明該註解是用在屬性上的
@Retention(RetentionPolicy.RUNTIME)//該註解能夠保留到程序運行的時候
public @interface BindView {
    int value();
}
複製代碼

第②步,具體實現injectView()方法。

實現injectView()方法是重點,讓咱們來仔細分析一下思路:

  • 首先,咱們確定仍是要經過反射,因此要先拿到Activity對應的Class.
  • 拿到了clazz後,咱們還要拿到clazz上的全部屬性字段(Fields)。▲▲▲
  • 而後咱們就要循環遍歷屬性,看屬性上是否有BindView註解。
  • 若是屬性上確實拿到了BindView註解,那咱們就要繼續拿到註解後面的viewId了。
  • 再接着就是反射執行findViewById方法,獲得對應的view.
  • 最後要注意,對於私有屬性,不管是對它進行讀寫,都要調用field.setAccessible(true)。▲
private static void injectView(Object context) {
    //獲取clazz
    Class<?> clazz = context.getClass();
    //獲取clazz上的全部屬性
    Field[] fields = clazz.getDeclaredFields();
    //循環遍歷每個屬性
    for (Field field : fields) {
        //獲取屬性上的BindView註解
        BindView bindView = field.getAnnotation(BindView.class);
        if (bindView != null) {//若是該屬性上找到了BindView註解
            //拿到註解後面的viewId
            int viewId = bindView.value();
            //運行到這裏,每一個按鈕的ID已經取到了
            //下面就是反射執行findViewById方法
            try {
                Method method = clazz.getMethod("findViewById", int.class);
                View view = (View) method.invoke(context, viewId);
                //對 field 作相關操做
                //注意:若是獲取的字段是私有的,不論是讀仍是寫,都要先 field.setAccessible(true);才能夠。不然會報:IllegalAccessException。
                field.setAccessible(true);
                field.set(context, view);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }
}
複製代碼

好的,如今咱們控件注入的相關操做就完成了,那讓咱們來改個button的名稱測試一下吧:

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

    @BindView(R.id.button1)
    Button btn1;
    @BindView(R.id.button2)
    Button btn2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //檢測 控件注入 是否成功
        btn1.setText("我是注入的按鈕01");
        btn2.setText("我是注入的按鈕02");
    }
}
複製代碼

下面仍是見證奇蹟的時刻:

在這裏插入圖片描述

怎麼樣是否是不擼不知道,一擼代碼才知道原來這就是IOC技術啊,也蠻容易的嘛~

在這裏插入圖片描述

是的,佈局注入和控件注入咱們都輕鬆搞定啦。

還有一個事件注入咱們沒有實現,這個事件注入就會有點小難度了喲。

懼怕文檔太長,你們懶得看(PS:實際上是由於我懶),

那咱們就在下篇繼續來擼代碼一步一步實現 事件注入 吧~~~

積累點滴,作好本身~

相關文章
相關標籤/搜索