Android:注解型框架原理浅析

在DataBinding框架出来之前,Android有很多很多注解型框架,但是随着DataBinding的强势崛起,大多注解型框架都走投无路了。但是为了更好的了解技术,还是写了一个小小小的注解模型

作为一名初学者,已经厌烦了findViewById了,有没有更简单的办法去获取xml文件中的控件实例呢?使用注解就可以了。

注解:我们可以把它理解为是一种标记。通过反射机制+标记就能实现注解型框架了。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <TextView
        android:id="@+id/username"
        android:layout_width="match_parent"
        android:layout_height="50dp" />

    <TextView
        android:id="@+id/password"
        android:layout_width="match_parent"
        android:layout_height="50dp" />
</LinearLayout>

代码着实太简单了,就是定义了id分别为username和password的两个TextView控件。

我的目标就是:自动获取控件,并且为id为a的控件增加onClickListener监听,id为b的控件增加onLongClickListener监听。

我们先定义注解吧

@Target({ElementType.FIELD, ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface ViewAnnotation {
    int viewId() default 0;

    int layoutId() default 0;

    int onClick() default 0;

    int onLongClick() default 0;
}

这里定义了四个方法,其实要使用的注解。因为写了default,所以可以不同时使用者四个注解,viewId是用来获取控件的,layoutId是为activity设置布局的,onClick是为了设置点击事件监听的,onLongClick是为了设置长按事件监听的。

那我们开始在activity中使用这四个注解

@ViewAnnotation(layoutId = R.layout.activity_main)
public class MainActivity extends Activity {

    @ViewAnnotation(viewId = R.id.username)
    private TextView username;

    @ViewAnnotation(viewId = R.id.password)
    private TextView password;

    @ViewAnnotation(onClick = R.id.username)
    public void clickUsername(View view) {
        Toast.makeText(this, "onClickListener", Toast.LENGTH_LONG).show();
    }

    @ViewAnnotation(onLongClick = R.id.password)
    public void onLongClick(View view) {
        Toast.makeText(this, "onLongClickListener", Toast.LENGTH_LONG).show();
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ViewUtils.injectActivity(this);

        username.setText("Aiden");
        password.setText("123456");
    }
}

可以看出,代码变得非常简单。在onCreate()函数中,写了ViewUtils.injectActivity(this)。这是我自定义的ViewUtils类,然后不同findViewById了,因为我们用注解获取了那个实例了。

public class ViewUtils {

	// 注入activity
    public static void injectActivity(Activity activity) {
        Class<?> activityClass = activity.getClass();
        injectContentView(activity, activityClass);
        injectView(activity, activityClass);
        injectListener(activity, activityClass);
    }

	// 设置contentView的
    private static void injectContentView(Activity activity, Class<?> activityClass) {
        if (activityClass.isAnnotationPresent(ViewAnnotation.class)) {
            ViewAnnotation inject = activityClass.getAnnotation(ViewAnnotation.class);
            int layoutId = inject.layoutId();
            if (layoutId > 0) {
                activity.setContentView(layoutId);
            }
        }
    }

	// 获取控件的
    private static void injectView(Activity activity, Class<?> activityClass) {
        Field[] fields = activityClass.getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(ViewAnnotation.class)) {
                field.setAccessible(true);
                ViewAnnotation inject = field.getAnnotation(ViewAnnotation.class);
                int id = inject.viewId();
                if (id > 0) {
                    try {
                        field.set(activity, activity.findViewById(id));
                    } catch (IllegalAccessException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    }

	// 分别实现了点击事件和长按事件的监听
    private static void injectListener(final Activity activity, Class<?> activityClass) {
        Method[] methods = activityClass.getDeclaredMethods();
        for (final Method method : methods) {
            if (method.isAnnotationPresent(ViewAnnotation.class)) {
                ViewAnnotation ViewAnnotation = method.getAnnotation(ViewAnnotation.class);
                int onClick = ViewAnnotation.onClick();
                if (onClick > 0) {
                    activity.findViewById(onClick).setOnClickListener(
                            new View.OnClickListener() {
                                @Override
                                public void onClick(View v) {
                                    try {
								        // 回调activity中的方法
							            method.invoke(activity, v);
                                    } catch (IllegalAccessException | InvocationTargetException e) {
                                        e.printStackTrace();
                                    }
                                }
                            }
                    );
                }

                int onLongClick = ViewAnnotation.onLongClick();
                if (onLongClick > 0) {
                    activity.findViewById(onLongClick).setOnLongClickListener(new View.OnLongClickListener() {
                        @Override
                        public boolean onLongClick(View v) {
                            try {
	                            // 回调activity中的方法
                                method.invoke(activity, v);
                            } catch (IllegalAccessException | InvocationTargetException e) {
                                e.printStackTrace();
                            }
                            return false;
                        }
                    });
                }
            }
        }
    }
}

至此,一个简单的注解型框架就形成了。当然了还有更多更多的细节需要处理的。比如说支持Fragment、反射控件的更多方法(如setAdapter() )等等。。。
作为小白,只知道这么多了

猜你喜欢

转载自blog.csdn.net/new_Aiden/article/details/53681267