Android知识进阶(遥远的重头开始)随笔-生活工作点滴

Android-自定义注解-控件注解

2019-07-10  本文已影响1人  MonkeyLei

<h2>Give morning to myself</h2>

之前我们说了下反射的一些基础知识,基本已经可以来做一些简单的事情了。我们就在Android里面去尝试下怎么给控件做注解,实现类似Butterknife那样@BindView(R.id.xxx)public xxxType xxx;这样的控件注解...

开始之前我们了解几个概念:

1.之前我们通过反射获取到了Field,那么这个时候就需要知道如何给这个Field赋值

2.我们都知道,使用第三方控件需要implementation引入第三方(可能还需要添加jcenter等)或者直接使用jar包的方式(然后添加依赖) - 至于如何制作自己的开源项目并放到开源依赖仓库(bintray 发布相关)里面给别人用,后面再去学习这块! 这里我们先学习下创建自己的本地Module,然后专门实现注解模块,并提供init方法供注解使用-就仿Butterknife.bind(context);

第一个问题比较简单,Field的set方法就可以了

image

<h2>从工程开始</h2>

1.创建基本的Android工程(这个就不用怎么说了)

2.创建Android Library

image image

3. app module引入模块,然后就可以使用第三方模块下面的相关api了

image

<h2>骚年Start</h2>

布局增加一个id

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
    tools:context=".MainActivity">

    <TextView
        android:id="@+id/testSB"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Hello World!"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

然后mylibrary里面新建一个注解类for控件

image

BindView.java

package com.example.mylibrary;

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

/**
 * Author: hl
 * Time:  2018/5/23 11:12
 * Des:   This is BindView for widget.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
    int viewId();
}

App添加mylibrary模块依赖

image

MainActivity里面使用注解

package com.example.lieyun_android.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.example.mylibrary.BindView;

public class MainActivity extends AppCompatActivity {

    @BindView(viewId = R.id.testSB)
    private TextView sb;

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

新建一个ButterKnife.java, 实现绑定注解,代码里面也增加了注释说明;一开始难理解可以多看看官方文档介绍,然后多记,多联系,自己多练习的时候尽量靠自己的理解去实现代码...

package com.example.mylibrary;

import android.app.Activity;
import android.content.Context;

import java.io.File;
import java.lang.reflect.Field;

/**
 * Author: hl
 * Time:  2018/5/23 11:21
 * Des:   This is not really ButterKnife
 */
public class ButterKnife {
    /**
     * 注解绑定
     * @param context
     */
    public static void bind(final Context context) {
        ///< 0\. 获取Class类
        Class classObj = context.getClass();
        ///< 1.获取Field数组
        //Field[] fields = classObj.getFields();        ///< 只能获取public声明的,ButterKnife用的应该是这种
        Field[] fields = classObj.getDeclaredFields();  ///< public和private的都可以获取
        for (Field field : fields){
            ///< 2.判断是否添加了BindView注解,如果是我们则可以获取控件id
            if (field.isAnnotationPresent(BindView.class)){
                ///< 3.获取注解实例
                BindView bindView = field.getAnnotation(BindView.class);
                ///< 4.获取上面的控件id
                int viewId = bindView.viewId();
                ///< 判断下上下文类型吧!(真正的Butterknife在不同类型界面使用方法有区别)
                if (context instanceof Activity){
                    ///< 5.我们暂时用以前常用的findviewbyid来获取控件吧
                    Object viewObj = ((Activity)context).findViewById(viewId);
                    try {
                        ///< 6.将获取的控件设置到file上面
                        ///< 注意,如果控件类型声明的是private的,这里需要添加;
                        ///<       如果是public的,可以不用添加;
                        //field.setAccessible(true);
                        field.set(context, viewObj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }
}

使用一下下

MainActivity.java

package com.example.lieyun_android.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.TextView;

import com.example.mylibrary.BindView;
import com.example.mylibrary.ButterKnife;

public class MainActivity extends AppCompatActivity {

    @BindView(viewId = R.id.testSB)
    private TextView sb;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        ///< 注解绑定
        ButterKnife.bind(this);
        ///< 使用下控件,么么哒
        sb.setText("fuck sb");
    }
}

到此搞定...

image

怎么可以像@BindView(R.id.xxx)那样,不要前面的viewId=?前面章节我们讲过,直接 int viewId() 修改为value

package com.example.mylibrary;

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

/**
 * Author: hl
 * Time:  2018/5/23 11:12
 * Des:   This is BindView for widget.
 */
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface BindView {
    int value();
}

相应注解实现的地方

//int viewId = bindView.viewId();
int viewId = bindView.value();

定义就可以和ButterKnife一样了

//@BindView(viewId = R.id.testSB)
@BindView(R.id.testSB)
private TextView sb;

<h2>骚年Start Two</h2>

之前我们获取控件是通过直接findViewBy来的(还需要把context转换为activity),怎么通过反射来搞?

我们看下Method里面有个方法getMethod(String name, Class<?>... parameterTypes),可以获取指定的某个方法,第二个是参数需要传入参数类型

image

然后通过invoke执行这个方法获取控件

///< 5.我们暂时用以前常用的findviewbyid来获取控件吧
                    //Object viewObj = ((Activity)context).findViewById(viewId);
                    try {
                        ///< 5.two 用反射获取方法,进而执行获取控件
                        Method method = classObj.getMethod("findViewById", int.class);
                        Object viewObj = method.invoke(context, viewId);
                        ///< 6.将获取的控件设置到file上面
                        ///< 注意,如果控件类型声明的是private的,这里需要添加;
                        ///<       如果是public的,可以不用添加;
                        field.setAccessible(true);
                        field.set(context, viewObj);
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    } catch (NoSuchMethodException e) {
                        e.printStackTrace();
                    } catch (InvocationTargetException e) {
                        e.printStackTrace();
                    }

就是酱紫,我们的控件注解基本就从效果上仿了仿ButterKnife...这篇暂时先到这里,后面我们开始看下方法部分如何搞(最后部分已经提到了Invoke,后面也是重点围绕invoke相关弄的)!

上一篇 下一篇

猜你喜欢

热点阅读