Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> github項目解析(六)--)自定義實現ButterKnife框架

github項目解析(六)--)自定義實現ButterKnife框架

編輯:關於Android編程

(一)使用方式

1)在activity中如何使用

@InjectView(R.id.feedback_content_edit)
    EditText feedContent; // 意見反饋功能

    @InjectView(R.id.feedback_contact_edit)
    EditText feedContact; // 聯系方式

    @InjectView(R.id.b3_button)
    Button feedOk; // 提交按鈕

    @OnClick(R.id.open_car_door)
    public void openCarDorClick() {
        dosomething;
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_feedback);
        ButterKnife.inject(this);

        initView();
    }

是不是很簡單?其實這就是butterKnife的基本用法了

通過注解(@InjectView)的方式將xml布局文件中定義的組件與Activity中定義的組件對象對應起來

通過注解(@OnClick)實現對布局文件的點擊事件

在onCreate方法中通過靜態方法:ButterKnife.inject(this);真正的將xml布局組件與activity中的組件對象映射起來;

2)在Fragment中如何使用

public class SimpleFragment extends Fragment {

    @InjectView(R.id.fragment_text_view)
    TextView mTextView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_simple, container, false);
        ButterKnife.inject(this, view);
        mTextView.setText("TextView in Fragment are found!");
        return view;
    }
}

注意這裡需要調用ButterKnife的重載方法:

public static void inject(Object target, View source) {
        inject(target, source, ButterKnife.Finder.VIEW);
    }

3)在Adapter的ViewHolder是如何使用的

static class ViewHolder {
        @InjectView(R.id.person_name)
        TextView name;
        @InjectView(R.id.person_age)
        TextView age;
        @InjectView(R.id.person_location)
        TextView location;
        @InjectView(R.id.person_work)
        TextView work;

        public ViewHolder(View view) {
            ButterKnife.inject(this, view);
        }
    }

可以發現ButterKnife的主要使用作用是簡化我們加載xml布局文件的寫法,通過注解的方式將內存對象與布局文件綁定,這對於懶程序員真是一個偷懶的利器啊。

那麼ButterKnife的實現原理是怎樣的呢?我們能否自己實現一個簡單的ButterKnife框架呢?帶著這兩個問題,我們開始今天的自定義ButterKnife之旅。

預備知識點:

java注解相關

下面是一段關於java中注解的說明:

注解相當於一種標記,在程序中加了注解就等於為程序打上了某種標記,沒加,則等於沒有某種標記,以後,javac編譯器,開發工具和其他程序可以用反射來了解你的類及各種元素上有無何種標記,看你有什麼標記,就去干相應的事。標記可以加在包,類,字段,方法,方法的參數以及局部變量上。

java反射相關

下面一段是百度百科中關於java反射的說明:

JAVA反射機制是在運行狀態中,對於任意一個類,都能夠知道這個類的所有屬性和方法;對於任意一個對象,都能夠調用它的任意一個方法和屬性;這種動態獲取的信息以及動態調用對象的方法的功能稱為java語言的反射機制。

在了解了java的注解和反射機制之後我們可以開始我們的自定義實現ButterKnife之旅了。

(二)綁定View組件;

自定義注解
@Target(ElementType.FIELD) // 標識改注解應用在成員變量上
@Retention(RetentionPolicy.RUNTIME) // 標識生命周期是運行時
public @interface ViewBinder {
    int id() default -1;
}

@interface是用於自定義注解的,它裡面定義的方法的聲明不能有參數,也不能拋出異常,並且方法的返回值被限制為簡單類型、String、Class、emnus、@interface,和這些類型的數組。
關於注解方面的內容,可自行查詢學習;

創建View解析類
/**
 * View解析類,主要用於解析含有注解的View成員變量
 */
public class ViewBinderParser {

    /**
     * 初始化解析
     * @param object
     */
    public static void inject(Object object) {
        // 創建解析對象並開始執行解析方法
        ViewBinderParser parser = new ViewBinderParser();
        try {
            parser.parser(object);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * 開始執行解析方法
     * @param object
     * @throws Exception
     */
    public void parser(final Object object) throws Exception{
        View view = null;
        // 獲取目標對象字節碼
        final Class clazz = object.getClass();
        // 獲取目標對象定義的成員變量
        Field[] fields = clazz.getDeclaredFields();
        // 循環遍歷成員變量
        for (Field field : fields) {
            if (field.isAnnotationPresent(ViewBinder.class)) {
                ViewBinder inject = field.getAnnotation(ViewBinder.class);
                int id = inject.id();
                if (id < 0) {
                    throw new Exception("id must not be null!!!");
                }
                if (id > 0) {
                    field.setAccessible(true);
                    if (object instanceof View) {
                        view = ((View) object).findViewById(id);
                    } else if (object instanceof Activity) {
                        view = ((Activity) object).findViewById(id);
                    }
                    field.set(object, view);// 給我們要找的字段設置id
                }
            }
        }
    }
}

這裡可以看出,主要是通過創建一個解析對象,然後通過反射方法獲取定義的成員變量,判斷是否注解屬性,如果是的話,則將注解的id值通過findViewById獲取並給View對象賦值;

在activity執行解析方法
/**
 * 測試Activity,主要用於測試通過注冊實現View組件的初始化過程
 */ 
public class MainActivity extends AppCompatActivity {

    @ViewBinder(id = R.id.button1)
    public Button button1;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.button2).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                ViewBinderParser.inject(MainActivity.this);

                Log.i("tag", button1.getText() + " ####");
            }
        });

    }

}

可以發現當我們點擊Button2的時候我們執行了Log.i方法,並將button1的text打印出來了,正式我們在布局文件中初始化的時候設置的text字符串,從而說明我們通過注解的方式實現了button1組件的初始化工作,初始化過程可能有一些地方有待優化,但這個其實就是butterKnife框架實現組件初始化工作的核心流程。

既然我們已經實現了組件的初始化工作,下面我們將嘗試的綁定View組件的事件點擊事件。

(三)綁定View的OnClick事件

(一)自定義OnClick注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface OnClick {
    int id() default -1;
}

和設置View組件的初始化注解類似,這裡定義了組件點擊事件OnClick的注解。

(二)創建解析類,解析方法

/**
     * 解析成員方法
     * @param clazz
     * @throws Exception
     */
    public void parserMethod(Class clazz, final Object object) throws Exception{
        View view = null;
        // 獲取目標對象定義的成員方法
        Method[] methods = clazz.getDeclaredMethods();
        // 循環遍歷成員變量
        for (final Method method : methods) {
            if (method.isAnnotationPresent(OnClick.class)) {
                OnClick inject = method.getAnnotation(OnClick.class);
                int id = inject.id();
                if (id < 0) {
                    throw new Exception("id must not be null!!!");
                }
                if (id > 0) {
                    if (object instanceof View) {
                        view = ((View) object).findViewById(id);
                    } else if (object instanceof Activity) {
                        view = ((Activity) object).findViewById(id);
                    }
                    view.setOnClickListener(new View.OnClickListener() {
                        @Override
                        public void onClick(View v) {
                            try {
                                method.invoke(object, null);
                            } catch (IllegalAccessException e) {
                                e.printStackTrace();
                            } catch (InvocationTargetException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                }
            }
        }
    }

(三)在activity中測試解析

/**
 * 測試Activity,主要用於測試組件的點擊事件
 */ 
public class MainActivity extends AppCompatActivity {

    @ViewBinder(id = R.id.button1)
    public Button button1;

    /**
     * 執行button1的點擊事件
     */
    @OnClick(id = R.id.button1)
    public void button1OnClick() {
        Log.i("tag", "這是一個測試的例子");
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ViewBinderParser.inject(MainActivity.this);
    }

}

可以發現我們在Activity中為button1OnClick方法綁定了組件id,這樣就實現了button1組件的事件綁定定義,然後我們在Activity的onCreate方法執行了ViewBinderParser.inject方法,這樣就真正實現了對組件的事件綁定,當我們點擊button1的時候執行了Log.i方法,並打印:”這是一個測試的例子”,說明我們組件綁定操作執行成功了。O(∩_∩)O哈哈~

總結:

本文主要分析了一下ButterKnife的簡單實用與實現原理,並實際實現了一個簡單的ButterKnife框架。

已經將自己實現的ButterKnife框架上傳至我的github中感興趣的同學可以star和follow。

自己實現簡單的ButterKnife框架主要涉及到了java中的注解和反射相關知識。

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved