1. 程式人生 > >自定義xUtils框架

自定義xUtils框架

const enum list 解釋 policy nbsp 利用 ext.get 生命

xUtils是基於Afinal開發的目前功能比較完善的一個Android開源框架,最近又發布了xUtil3.0,在增加新功能的同時又提高了框架的性能。它的功能很強大,但是有時候我們只需要其中的一些功能,如果把整個xUtils引進去沒什麽必要。

下面我們就講講如何自定義小型的xUtils,只有兩個功能:通過註解找到省去findViewById()和setContentView().

一、首先:要自定義兩個註解:

(1)找到activity視圖的註解,即用來省去setContentView()的:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface ContentView { int value(); }

(2)找到控件的註解,即用來省去findViewById()的。

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

給大家解釋一下,@Target ,@Retentio這種註解叫元註解。

Target的功能就是表明你這個註解是用在什麽地方,它值是一個枚舉類型:

 public enum ElementType {
        
/** * Class, interface or enum declaration. 用於描述類、接口(包括註解類型) */ TYPE, /** * Field declaration. 字段聲明 */ FIELD, /** * Method declaration. 方法聲明 */ METHOD, /** * Parameter declaration. 參數聲明
*/ PARAMETER, /** * Constructor declaration. 構造器聲明 */ CONSTRUCTOR, /** * Local variable declaration. 局部變量 */ LOCAL_VARIABLE, /** * Annotation type declaration. 註釋類型聲明。 */ ANNOTATION_TYPE, /** * Package declaration. 用於描述包 */ PACKAGE }

Retention大概意思是註解的生命周期什麽時候生效。

public enum RetentionPolicy {
        /**
         * Annotation is only available in the source code. 在源文件中有效(指定註解只保留在源文件當中,
編譯成類文件後就把註解去掉;)
*/ SOURCE, /** * Annotation is available in the source code and in the class file, but not * at runtime. This is the default policy. 在class文件中有效,不是在運行時有效(指定註解只保留在源文件和編譯後的class
文件中,當jvm加載類時就把註解去掉)
*/ CLASS, /** * Annotation is available in the source code, the class file and is * available at runtime. 運行時有效 */ RUNTIME }

二、我們需要自定義一個工具類,這個工具類裏面可以獲得我們的註解,通過反射來找到我們的View。

public class InjectUtils {
    public static void initContext(Object context) {
        //injectLayout必須在injectView前面,因為必須先找到contentView才能夠找到控件
        injectLayout(context); //找到contentView
        injectView(context); //找到控件
    }

    private static void injectView(Object context) {
        Class<?> clazz = context.getClass();
        Field[] fields = clazz.getDeclaredFields();

        for(Field field : fields){
            //獲取註解
            ViewInject viewInject = field.getAnnotation(ViewInject.class);
            //如果上面沒有標明註解
            if (viewInject == null)
                continue;

            //獲取註解裏面的Id
            int valueId = viewInject.value();
            try {
                //用反射調用findViewById()方法
                Method findViewById = context.getClass().getMethod("findViewById",int.class);
                View view = (View) findViewById.invoke(context,valueId);
                //反射訪問私有成員,必須加上這句
                field.setAccessible(true);
                //然後對這個屬性復制
                field.set(context,view);
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }
    }

    private static void injectLayout(Object context) {
        int layoutId = 0;

        //獲得類
        Class<?> clazz = context.getClass();

        //獲得該類聲明的註解
        ContentView contentView = clazz.getAnnotation(ContentView.class);
        //如果該類沒有聲明註解
        if (contentView == null)
        {
            return;
        }

        //獲得註解裏面設置的Id
        layoutId = contentView.value();

        try {
            //利用反射調用setContentView()方法
            Method setContentView = context.getClass().getMethod("setContentView",int.class);
            setContentView.invoke(context,layoutId);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }

    }


}

具體含義請看註釋。

三、定義一個BaseActivity

public class BaseActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        InjectUtils.initContext(this);
    }
}

四、實現我們的activity

//給該class加上我們自定義的註解,InjectUtils會通過找到
//註解裏面的R.layout.activity_main,然後通過反射調用
//setContentView()方法
@ContentView(R.layout.activity_main) public class MainActivity extends BaseActivity { //給該組件加上我們自定義的註解,InjectUtils會通過找到 //註解裏面的R.id.xx,然後通過反射調用 //findViewById()方法找到控件 @ViewInject(R.id.text1) private TextView textView1; @ViewInject(R.id.text2) private TextView textView2; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); textView1.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { textView1.setText("text1"); } }); textView2.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { textView2.setText("text2"); } }); } }

要註意一點:如果某個註解屬性使用value作為名稱如ContentView中的value,那麽賦值的時候可以直接@ContentView(類的ID),但是如果你使用的是其他名稱,比如下面這個註解:

public @interface Person
{
  //name是屬性而不是方法,t是它的默認值,在定義的時候可以不用給定默認值
  String name() default t;
}

那麽必須@Person(name=xx)這樣調用

自定義xUtils框架