1. 程式人生 > >實現注入view,簡單案例剖析butterknife原理

實現注入view,簡單案例剖析butterknife原理

ButterKnife注入框架我相信大家都用過至少聽過吧,它的出現簡直是android開發人員的一大福利,徹底解放了無止境的findViewById,而是通過優雅的註解來實現view的賦值,如果你真沒聽過那我在這裡只能貼上一張github的截圖,自己好好反思吧~

這裡寫圖片描述

1w5的star你還有什麼不學的理由!!!

好的,今天我們不說butterfnife怎麼用,而是仿照框架中的實現原理,用最簡單的例子自己來實現view的注入!

1、編寫自定義註解類

既然是注入的形式對view賦值,那麼當然少不了註解,我們最常用的莫過於setContentView和findViewById,我們先來定義這兩個註解(註釋中順便幫同學們補一些註解的知識):

setContentView

/**
 * @Target描述註解的使用範圍
 * 1.CONSTRUCTOR:用於描述構造器
  2.FIELD:用於描述域
  3.LOCAL_VARIABLE:用於描述區域性變數
  4.METHOD:用於描述方法
  5.PACKAGE:用於描述包
  6.PARAMETER:用於描述引數
  7.TYPE:用於描述類、介面(包括註解型別) 或enum宣告
 */
@Target(ElementType.TYPE)
/**
 *  @Retention 該Annotation被保留的時間長短
 *   1.SOURCE:在原始檔中有效(即原始檔保留)
   2.CLASS:在class檔案中有效(即class保留)
   3.RUNTIME:在執行時有效(即執行時保留)
 */
@Retention(RetentionPolicy.CLASS) public @interface SetContent { int value() default -1; }

findViewById

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.CLASS)
public @interface BindView {
    int value();
}

註解定義好之後,我們的目標是在activity中這樣使用對不對

@SetContent(R.layout.activity_main)
public
class MainActivity extends AppCompatActivity { @BindView(R.id.tv_text) private TextView textView; }

這是我們需要一個解析器。

2、編寫註解解析器,為這些view賦值

/**
 * Created by admin on 2017/4/2.
 * 注入解析器
 * 單例
 */
public class MyAnnotationInject {
    private static MyAnnotationInject INSTANCE;
    private List<Object> mInjectObjs = new ArrayList<Object>();

    public static MyAnnotationInject init() {
        if (INSTANCE == null) {
            INSTANCE = new MyAnnotationInject();
        }
        return INSTANCE;
    }

    /**
     * 註冊需要注入的類物件
     * @param injectObj
     */
    public void regist(@NonNull Object injectObj) {
        if(mInjectObjs.contains(injectObj)){
            return;
        }
        if (injectObj instanceof Activity) {
            Activity activity = (Activity) injectObj;
            //拿到注入的class物件
            Class<Activity> clz = (Class<Activity>) activity.getClass();
            try {
                injectContentView(activity, clz);
                injectView(activity, clz);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
        mInjectObjs.add(injectObj);
    }

    /**
     * 注入view
     */
    private void injectView(Activity activity, Class<Activity> clz) throws IllegalAccessException {
        //獲得所有的屬性,包括public、protected、private
        Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) {
            //遍歷所有屬性,找到標有Bindview註解的屬性
            if (field.isAnnotationPresent(BindView.class)) {
                //拿到BindView註解的值
                BindView annotation = field.getAnnotation(BindView.class);
                int viewId = annotation.value();
                //如果是私有屬性,修改屬性賦值之前必須加可修改許可權
                field.setAccessible(true);
                //給屬性重新賦值,引數1:要修改的屬性的物件 , 引數2 : 新值
                field.set(activity, activity.findViewById(viewId));
            }
        }
    }

    /**
     * 注入activity佈局
     */
    private void injectContentView(Activity activity, Class<Activity> clz) {
        if (clz.isAnnotationPresent(SetContent.class)) {
            //拿到SetContent註解的值
            SetContent annotation = clz.getAnnotation(SetContent.class);
            int resId = annotation.value();
            if (resId != -1) {
                activity.setContentView(resId);
            }
        }
    }

    /**
     * 登出需要注入的類物件
     * @param injectObj
     */
    public void unRegist(Object injectObj) {
        if (mInjectObjs.contains(injectObj))
            mInjectObjs.remove(injectObj);
    }
}

3、Activity中使用我們的註解來代替findViewById

@SetContent(R.layout.activity_main)
public class MainActivity extends AppCompatActivity {
    @BindView(R.id.tv_text)
    private TextView textView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        MyAnnotationInject.init().regist(this);
        textView.setText("通過註解注入view成功!");
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        MyAnnotationInject.init().unRegist(this);
    }
}

我們看一下效果:
這裡寫圖片描述

當然我這只是butterknife中冰山一角,如果想要具體學習整個框架可以去學習butterknife的原始碼~
butterKnife地址,點我

相關推薦

實現注入view簡單案例剖析butterknife原理

ButterKnife注入框架我相信大家都用過至少聽過吧,它的出現簡直是android開發人員的一大福利,徹底解放了無止境的findViewById,而是通過優雅的註解來實現view的賦值,如果你真沒聽過那我在這裡只能貼上一張github的截圖,自己好好反思吧~

python實現DES加密簡單案例

環境:python3.6 庫:pyDes from pyDes import des, CBC, PAD_PKCS5 import binascii # 祕鑰 KEY='mHAxsLYz' def

python實現RSA加密簡單案例

環境:python3.6 第三方庫:M2Crypto  這個庫windows上很難裝,linux上直接用pip install 首先獲取一對祕鑰,具體生產祕鑰方式百度上有線上工具。 然後將生成

使用 vue 實現拖拽的簡單案例不會超出可視區域

實現拖拽之前,先了解幾個小常識: 這兩種獲取滑鼠座標的方法,區別在於基於的物件不同: pageX和pageY獲取的是滑鼠指標距離文件(HTML)的左上角距離,不會隨著滾動條滾動而改變; clientX和clientY獲取的是滑鼠指標距離可視視窗(不包括上面的位址列和滑動條)的距離

Mybatis實現Mapper介面的簡單案例

用了很久mybatis,一直猜想mybatis是使用動態代理的方式實現mapper介面的,通過實踐探索之後,果然如此,但是還是遇到了不少問題,在此粘出來供初學者參考。 首先我將案例程式碼粘出來,這段程式碼簡單實現了從service層開始呼叫mapper介面的過程

MATLAB實現FCM演算法簡單程式碼實現

先來看看結果好了,這是一幅原圖,經過FCM演算法之後,假設選擇3個center,將這幅彩色影象轉化後得到一個只有三種灰度值的影象,還能將每一種灰度單獨提取出來,得到對應的只含有一種灰度的三個圖。 它的數學原理為         這裡Vk取v1,v2,v3三個任意初始值

JAVA實現排列組合簡單的一比

/** * 思路: * 遞迴,第一層把迴圈把n個數中的第i個裝入結果的第一個位置 * 把剩下的n-2個數迴圈裝入第二個位置 * 把剩下的n-3個數迴圈裝入第三個位置 * .....

壓縮稀疏矩陣以及使用三元組實現矩陣乘法簡單易懂

思路:既然使用三元組去實現,所以首先要定義一個三元組 typedef struct node { int row, col, v;//分別代表行數,列數,以及元素的值,整個式子表示在原矩陣的第row行,第col列,有一個值為v的數 } node;

DNS域名系統簡單描述其工作原理

DNS域名系統:當DNS客戶機需要在程式中使用名稱時,它會查詢DNS伺服器來解析該名稱。客戶機發送的每條查詢資訊包括三條資訊:包括:指定的DNS域名,指定的查詢型別,DNS域名的指定類別。基於UDP服務,埠53. 該應用

C語言實現printf函式即引數可變函式原理

我們在C語言程式設計中會遇到一些引數個數可變的函式,例如printf() 這個函式,它的定義是這樣的: int printf( const char* format, ...); 它除了有一個引數format固定以外,後面跟的引數的個數和型別是 可變的,例如我們可以有以下不

Android之---ButterKnife-View注入框架(簡單介紹和在Studio中安裝)

ButterKnife-View注入框架(簡單介紹和在Studio中安裝) 1.簡單介紹 Butter Knife使用簡單介紹 作為一名Android開發,是不是經常厭煩了大量的findViewById以及setOnClickListener程式碼,而

c#實現簡單注入容器認識注入容器的基本原理

在學習了反射和注入的概念後,加上專案中也用到 比如 AutoFuc 還有 unity 等容器。就有點想寫自己的容器的想法。 然後 搜尋了下 注入容器相關的文章,大多是多某個成熟的注入容器的程式碼進行解析,或者是對 注入概念的解析。成熟的框架考慮到方方面面,原始碼對於新手來說

Kotlin編譯時註解簡單實現ButterKnife

ButterKnife在之前的Android開發中還是比較熱門的工具,幫助Android開發者減少程式碼編寫,而且看起來更加的舒適,於是簡單實現一下ButterKnife,相信把下面的程式碼都搞懂,看ButterKnife的難度就小很多。 今天實現的是編譯時註解,其實執行時註解也一樣能實現ButterKnif

自己定義View用到Paint Canvas的一些溫故簡單的幀動畫(動畫一 &quot;掏糞男孩Gif&quot;順便再提提onWindowFocusChanged)

eat android 內容 rri generated 簡單的 fadein spl onclick 轉載請註明出處:王亟亟的大牛之路 之前在繪畫的過程中提到了靜態的旋轉啊,縮放啊,平移等一些效果。那麽自己定義的View當然也有動態的效果也就是我們的

Android 簡單案例:繼承BaseAdapter實現Adapter

for ack import apt ret bsp position hang layout import android.view.LayoutInflater; import android.view.View; import android.view.ViewGr

Android 簡單案例:可移動的View

bool fst boolean store import cup tcl etc last CrossCompatibility.rar 1. VersionedGestureDetector.java import android.content.Context; i

簡單實現頂部固定中部自適應布局

col position fix enter span text auto pos otto 最近在重構web導航的時候就發現一個問題,如何實現頂部固定,中部自適應的布局。 很多人會認為這很簡單啊,頂部使用position: fixed;就可

用心剖析詳解如何搭建百萬PV網站架構簡單易懂!!!

socket cache sad nco sla tom 百萬 redis主從 debug 簡介: 本項目案例結合SVN、LNMP和MySQL三種環境,部署一個社交網站,本社交網站采用PHP語言開發,搭建SVN服務器進行版本控制和集中管理PHP程序員開發的代碼,以Nginx

C#實現軟體授權限定MAC執行(軟體license管理簡單軟體註冊機制)

最近做了一個綠色免安裝軟體,領導臨時要求加個註冊機制,不能讓現場工程師隨意複製。事出突然,只能在現場開發(離開現場軟體就不受我們控了)。花了不到兩個小時實現了簡單的註冊機制,稍作整理。 基本原理:1.軟體一執行就把計算機的CPU、主機板、BIOS、MAC地址記錄下來,然後加密(key=key1)生成

一個小小的 Shell 管道符內部實現可真不簡單

管道命令我們經常使用,將一個指令的輸出匯入另一個指令的輸入,也就是屁股對上嘴,這個原理連程式設計小學生都知道。但是如果要深入問進去,一個指令的輸出是如何匯入到另一個指令的輸入,管道又起到什麼角色,估計能回答這個問題的人不足 1%。下面我們來深入分析一下管道指令的實現原理,對於下面的這條指令,shell 到底幹