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を持つ2つのTextViewコントロールが定義されています。

私の目標は、コントロールを自動的に取得し、ID aのコントロールにonClickListenerを追加し、IDbのコントロールに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;
}

ここでは4つのメソッドが定義されていますが、実際に使用されるアノテーションが使用されます。デフォルトが記述されているため、4つのアノテーションを同時に使用することはできません。viewIdはコントロールの取得に使用され、layoutIdはアクティビティのレイアウトを設定し、onClickはクリックイベントリスナーを設定し、onLongClickは長押しイベントリスナーを設定します。

次に、アクティビティでこれら4つの注釈の使用を開始します

@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()など)などをサポートします。
Xiaobaiとして、私はそんなに知っているだけです

おすすめ

転載: blog.csdn.net/new_Aiden/article/details/53681267