Butterknife詳解——自己實現(xiàn)編譯時注解框架

ButterKnife注解框架相信大家都是用過,記得以前的老大老是說黃油刀毁菱,黃油刀的瑰谜,那里面的實現(xiàn)原理是什么呢?其實內(nèi)部原理比較簡單侈离, 簡單的你看文本文也可以自己寫

編譯時注解實現(xiàn)

1,定義接口试幽,成員變量的

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

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

這個injectView 接口的名字可以自己定義,但一般做到見名知意里面的@Target 等注解看統(tǒng)一專題里面的詳解卦碾。都是注解的內(nèi)容铺坞,里面的是生命周期等屬性的限制
2.定義點擊事件的接口

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int[] value();
}

3.寫一個工具類也就是相當(dāng)于 Butterknife.bind(this)效果:


/**
 * 文件描述:   代碼注入工具類
 * Created by  xn069392
 * 創(chuàng)建時間    2018/8/2
 */

public class InjectViewUtils {

    /**
     * 代碼注入方法,相當(dāng)于Butterknife的 bind()
     *
     * @param activity
     */
    public static void inject(final Activity activity) {
        //反射拿到類對象
        Class<? extends Activity> clazz = activity.getClass();
        //這里我們需要的是所有的成員變量的參數(shù)
        Field[] Fileds = clazz.getDeclaredFields();
        //遍歷集合
        for (int i = 0; i < Fileds.length; i++) {
            //獲取每一個成員變量
            Field filed = Fileds[i];
            // 如果私有 暴力反射
            filed.setAccessible(true);
            //獲取到成員變量的注解洲胖,傳入?yún)?shù)就是之前我們定義的接口的類對象
            InjectView inject = filed.getAnnotation(InjectView.class);
            if (inject != null) {
                //我們的接口中定義了一個int  類型的value 值  康震,獲取到value 也就是我們成員變量的id
                int id = inject.value();
                //通過這個ID,也是需要在傳入Activity中去找到對應(yīng)view的
                View view = activity.findViewById(id);
                //成員變量找到了宾濒,這個要去設(shè)置
                try {
                    filed.set(activity, view);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "inject: 設(shè)置成員變量失斖榷獭!");
                    e.printStackTrace();
                }

            }
        }

        //處理點擊事件
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            final Method method = declaredMethods[i];
            method.setAccessible(true);
            OnClick onClick = method.getAnnotation(OnClick.class);
            //為什么是一個數(shù)組呢,因為我們的點擊事件 有事后就寫一個方法橘忱,多個id .
            int[] value = onClick.value();
            for (int j = 0; j < value.length; j++) {
                int idValue = value[j];
                final View viewById = activity.findViewById(idValue);
                viewById.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        try {
                            method.invoke(activity, viewById);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        } catch (InvocationTargetException e) {
                            e.printStackTrace();
                        }
                    }
                });
            }
        }
    }
}
上面的代碼主要是用到暴力反射赴魁,

4.在主方法中調(diào)用

public class MainActivity extends AppCompatActivity {

    @InjectView(R.id.button1)
    Button  mButton;
    @InjectView(R.id.button2)
    Button  mButton2;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        InjectViewUtils.inject(this);
        mButton.setText("我是第一個button");
    }

    @OnClick({R.id.button1,R.id.button2})
    private void  XXX(View view ){
        switch (view.getId()){
            case R.id.button1:
                Toast.makeText(this,"第一個按鈕的手動編譯時注解",Toast.LENGTH_LONG).show();
                break;
            case R.id.button2:
                Toast.makeText(this,"第222個按鈕的手動編譯時注解,主要用到反射",Toast.LENGTH_LONG).show();
                break;
        }
    }
}package com.xiaoniu.finance.butterknifesimple;

import android.app.Activity;
import android.util.Log;
import android.view.View;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import static android.content.ContentValues.TAG;

/**
 * 文件描述:   代碼注入工具類
 * Created by  xn069392
 * 創(chuàng)建時間    2018/8/2
 */

public class InjectViewUtils {

    /**
     * 代碼注入方法,相當(dāng)于Butterknife的 bind()
     *
     * @param activity
     */
    public static void inject(final Activity activity) {
        //反射拿到類對象
        Class<? extends Activity> clazz = activity.getClass();
        //這里我們需要的是所有的成員變量的參數(shù)
        Field[] Fileds = clazz.getDeclaredFields();
        //遍歷集合
        for (int i = 0; i < Fileds.length; i++) {
            //獲取每一個成員變量
            Field filed = Fileds[i];
            // 如果私有 暴力反射
            filed.setAccessible(true);
            //獲取到成員變量的注解钝诚,傳入?yún)?shù)就是之前我們定義的接口的類對象
            InjectView inject = filed.getAnnotation(InjectView.class);
            if (inject != null) {
                //我們的接口中定義了一個int  類型的value 值  颖御,獲取到value 也就是我們成員變量的id
                int id = inject.value();
                //通過這個ID,也是需要在傳入Activity中去找到對應(yīng)view的
                View view = activity.findViewById(id);
                //成員變量找到了凝颇,這個要去設(shè)置
                try {
                    filed.set(activity, view);
                } catch (IllegalAccessException e) {
                    Log.e(TAG, "inject: 設(shè)置成員變量失斉斯啊!");
                    e.printStackTrace();
                }

            }
        }

        //處理點擊事件
        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (int i = 0; i < declaredMethods.length; i++) {
            final Method method = declaredMethods[i];
            method.setAccessible(true);
            OnClick onClick = method.getAnnotation(OnClick.class);
            //為什么是一個數(shù)組呢拧略,因為我們的點擊事件 有事后就寫一個方法芦岂,多個id .
            //這里有的方法沒有注解,必須做空判斷
            if(onClick !=null){
                int[] value = onClick.value();
                for (int j = 0; j < value.length; j++) {
                    int idValue = value[j];
                    final View viewById = activity.findViewById(idValue);
                    viewById.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(activity, viewById);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }

        }
    }
}

5.效果的實現(xiàn)

inject.gif

6.源碼地址:
https://github.com/zh2016hz/ButterknifeSimple.git

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末垫蛆,一起剝皮案震驚了整個濱河市禽最,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌袱饭,老刑警劉巖川无,帶你破解...
    沈念sama閱讀 221,695評論 6 515
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異虑乖,居然都是意外死亡懦趋,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,569評論 3 399
  • 文/潘曉璐 我一進店門疹味,熙熙樓的掌柜王于貴愁眉苦臉地迎上來仅叫,“玉大人,你說我怎么就攤上這事佛猛。” “怎么了坠狡?”我有些...
    開封第一講書人閱讀 168,130評論 0 360
  • 文/不壞的土叔 我叫張陵继找,是天一觀的道長。 經(jīng)常有香客問我逃沿,道長婴渡,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,648評論 1 297
  • 正文 為了忘掉前任凯亮,我火速辦了婚禮边臼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘假消。我一直安慰自己柠并,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 68,655評論 6 397
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著臼予,像睡著了一般鸣戴。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上粘拾,一...
    開封第一講書人閱讀 52,268評論 1 309
  • 那天窄锅,我揣著相機與錄音,去河邊找鬼缰雇。 笑死入偷,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的械哟。 我是一名探鬼主播疏之,決...
    沈念sama閱讀 40,835評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼戒良!你這毒婦竟也來了体捏?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,740評論 0 276
  • 序言:老撾萬榮一對情侶失蹤糯崎,失蹤者是張志新(化名)和其女友劉穎几缭,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體沃呢,經(jīng)...
    沈念sama閱讀 46,286評論 1 318
  • 正文 獨居荒郊野嶺守林人離奇死亡年栓,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,375評論 3 340
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了薄霜。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片某抓。...
    茶點故事閱讀 40,505評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖惰瓜,靈堂內(nèi)的尸體忽然破棺而出否副,到底是詐尸還是另有隱情,我是刑警寧澤崎坊,帶...
    沈念sama閱讀 36,185評論 5 350
  • 正文 年R本政府宣布备禀,位于F島的核電站,受9級特大地震影響奈揍,放射性物質(zhì)發(fā)生泄漏曲尸。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,873評論 3 333
  • 文/蒙蒙 一男翰、第九天 我趴在偏房一處隱蔽的房頂上張望另患。 院中可真熱鬧,春花似錦蛾绎、人聲如沸昆箕。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,357評論 0 24
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽为严。三九已至敛熬,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間第股,已是汗流浹背应民。 一陣腳步聲響...
    開封第一講書人閱讀 33,466評論 1 272
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留夕吻,地道東北人诲锹。 一個月前我還...
    沈念sama閱讀 48,921評論 3 376
  • 正文 我出身青樓,卻偏偏與公主長得像涉馅,于是被迫代替她去往敵國和親归园。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 45,515評論 2 359

推薦閱讀更多精彩內(nèi)容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,288評論 25 707
  • 1.什么是注解2.注解的分類3.編譯時注解的原理4.APT5.創(chuàng)建項目及依賴6.編碼實現(xiàn)7.總結(jié) 我們首先了解一下...
    慕涵盛華閱讀 657評論 0 6
  • 晨曦稚矿,我被一聲清脆的蟬鳴驚起庸诱,沒想到在現(xiàn)代文明絢爛的廣州還存息著大自然的妙語,或許還有更多的淳樸被我完美錯過...
    在公園放牛閱讀 413評論 3 6
  • 姓名:富智燚 361期努力一組 單位:深圳市蔚藍時代商業(yè)管理有限公司海南分公司 【日精進打卡第82天】 【知~學(xué)習(xí)...
    復(fù)制1閱讀 142評論 0 1
  • 他迫不及待的爬上槐花樹晤揣,把樹上的雨滴和槐花弄的紛紛落下桥爽,樹下的她落了一身的槐花雨,驚慌的抬起頭昧识,他卻樂的露出了...
    木子星雨閱讀 711評論 4 4