Android: Analysis of the Annotated Framework Principle

Before the DataBinding framework came out, Android had many annotation-based frameworks, but with the strong rise of DataBinding, most of the annotation-based frameworks were desperate. But in order to better understand the technology, I wrote a small annotation model

As a beginner, I am tired of findViewById. Is there an easier way to get the control instance in the xml file? Just use annotations.

Note: We can understand it as a mark. An annotation framework can be realized through reflection mechanism + mark.

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>

The code is too simple, that is, two TextView controls with IDs of username and password are defined.

My goal is to automatically obtain the control, and add onClickListener to the control with id a, and add onLongClickListener to the control with id b.

Let's define the annotation first

@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;
}

Four methods are defined here, but the annotations to be used are actually used. Because the default is written, you can not use the four annotations at the same time . The viewId is used to get the control, the layoutId is to set the layout for the activity, onClick is to set the click event listener, and onLongClick is to set the long press event listener.

Then we start to use these four annotations in the 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");
    }
}

As you can see, the code has become very simple. In the onCreate() function, ViewUtils.injectActivity(this) is written. This is my custom ViewUtils class, and then findViewById is different, because we got that instance with annotations.

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;
                        }
                    });
                }
            }
        }
    }
}

At this point, a simple annotated framework is formed. Of course, there are more details that need to be dealt with. For example, support Fragment, more methods of reflection control (such as setAdapter()) and so on. . .
As Xiaobai, I only know so much

Guess you like

Origin blog.csdn.net/new_Aiden/article/details/53681267