使用注解和反射干掉繁复的findViewById

在实际开发中,会经常使用到findViewById获取控件,但是业务一旦复杂起来,难免满屏都是findViewById代码,功能类似,繁复。那有没有办法避免写一大堆的findViewById代码呢?答案肯定是有的。开源框架butterknife、ViewBinding、DataBin ding等都可以彻底解决这个问题。如果仅仅只是干掉findViewById,显得有点大材小用了。现在我们基于butterknife原理实现替代findViewById的工具类。

首先,我们需要定义一个注解。由于是作用于成员变量的,所以@Target值应该为ElementType.FIELD。要实现在运行时获取到控件ID并且赋值的目的,则@Retention必须为RetentionPolicy.RUNTIME。由于value()只接受控件ID,所以应该给value()加上@IdRes声明。具体代码如下:

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

定义好了注解之后,我们还需要定义工具类。这里我们仅仅以Activity为例子:

public class ViewFindUtil {

    public static void find(Activity activity) {
        Class<? extends Activity> cls = activity.getClass();
        //获得此类的所有成员变量
        Field[] fields = cls.getDeclaredFields();
        for (Field field : fields) {
            //需要判断当前属性是否被ViewFinds注解声明
            if (field.isAnnotationPresent(ViewFinds.class)) {
                //获取到注解信息
                ViewFinds viewFinds = field.getAnnotation(ViewFinds.class);
                //获取到控件ID
                int viewId = viewFinds.value();
                //获取到控件
                View view = activity.findViewById(viewId);
                //反射设置属性的值
                //设置访问权限,允许操作private的属性
                field.setAccessible(true);
                try {
                    //赋值
                    field.set(activity, view);
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}

现在注解和工具类都定义好了,我们来试试效果。先定义一个页面activity_main和一个MainActivity:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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">

    <Button
        android:id="@+id/btn_ok"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="测试"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>
public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    @ViewFinds(R.id.btn_ok)
    private Button btn_ok;

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

        ViewFindUtil.find(this);

        btn_ok.setOnClickListener(this);

    }

    @Override
    public void onClick(View v) {
        Toast.makeText(this, "Success!", Toast.LENGTH_SHORT).show();
    }
}

最终测试成功,图片就不贴出来了。

发表回复

后才能评论