A summary of the priority of Android View property settings

table of Contents

A custom attribute

1. Declare attributes in attrs.xml

2. Get the attribute value in the View's constructor

3. Use in layout files

Priority of second attribute assignment

1. Add the attribute name in the custom control GridView

2. In the initialization constructor of GridView, get the value of name through the following code

3. Set attribute values ​​in different places to verify priority

Three summary


The previous blog post mainly introduced several ways for Android to customize View. Then the defined control must have some attributes that can be directly configured in the layout file. For example, when using Android system controls, some attributes start with android:. You can simply configure some attribute values ​​in the layout file. Maintain an attrs.xml in the values ​​folder of the system in the Android source code. The file defines these attributes, such as layout_width that is often used:

 <declare-styleable name="ViewGroup_Layout">
        <!-- Specifies the basic width of the view.  This is a required attribute
             for any view inside of a containing layout manager.  Its value may
             be a dimension (such as "12dip") for a constant width or one of
             the special constants. -->
        <attr name="layout_width" format="dimension">
            <!-- The view should be as big as its parent (minus padding).
                 This constant is deprecated starting from API Level 8 and
                 is replaced by {@code match_parent}. -->
            <enum name="fill_parent" value="-1" />
            <!-- The view should be as big as its parent (minus padding).
                 Introduced in API Level 8. -->
            <enum name="match_parent" value="-1" />
            <!-- The view should be only big enough to enclose its content (plus padding). -->
            <enum name="wrap_content" value="-2" />
        </attr>
........
    </declare-styleable>

So when customizing the View, you also need custom attributes, which can be configured in the layout file or other places.

A custom attribute

1. Declare attributes in attrs.xml

From the layout_width source code mentioned above, you can see that when defining an attribute, you need to declare the attribute in attrs.xml in the form of a tag like <attr name="xxx" format="xxx" />. There are usually two ways:

One is to declare through the <declare-styleable> tag, then for the attributes declared in this way, the system will generate an array of attributes R.styleable.GridView, the index of each attribute is R.styleable.GridView_name, which can be found by the index Corresponding attributes.

<resources>   
    <declare-styleable name="GridView">
        <attr name="verticalSpacing" format="dimension" />
        <attr name="horizontalSpacing" format="dimension" />
        <attr name="numColumns" format="integer" />
        <!-- 初始化的个数-->
        <attr name="initNum" format="integer" />
        <!--测试不同的构造函数调用周期-->
        <attr name="name" format="string" />

    </declare-styleable>
</resources>

The other is to declare directly through <attr>, then the attribute declared in this way is an element, and the corresponding attribute value can be found directly through R.attr.GridViewStyle

<resources>   
      <!--通过单独的属性来设置属性值-->
    <attr name="GridViewStyle" format="reference" />
</resources>

Parameter Description:

parameter Remarks For example
name It's simply the name of this property  
format reference This attribute can set the ID of the resource file <attr name="textAppearance" format="reference" />
color The attribute is the color value <attr name="titleTextColor" format="color" />
boolean This attribute can be set to the value of the corresponding data type <attr name="hint" format="string" />

float

integer
string
dimension

This attribute can be set as a size value, it can be a specific size value with a unit, or it can be a reference to R.dimen

<attr name="layout_margin" format="dimension" />

enum

 

This attribute can be set as an enumeration value, and only one enumeration value can be used when using it

<attr name="visibility">
     <enum name="visible" value="0" />
     <enum name="invisible" value="1" />
     <enum name="gone" value="2" />
</attr>

flag This attribute can be set to bitwise OR operation

<attr name="textStyle">

    <flag name="normal" value="0" />

    <flag name="bold" value="1" />

     <flag name="italic" value="2" />

</attr>

fraction This attribute can be set as a percentage value <attr name="centerX" format="float|fraction" />
mixing At that time, when the attribute is specified, multiple types of values ​​can also be used, which can be specified by | <attr name="gradientRadius" format="float|fraction|dimension" />

2. Get the attribute value in the View's constructor

The attributes set in the layout file are usually obtained through context.obtainStyledAttribute in the constructor of the custom View.

  TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GridView, defStyleAttr, defStyleRes)

Mainly read out the value of the attribute defined in R.styleable.GridView defined in the layout file. The priority of the attribute value assignment in different places will be explained in detail later. Then the attribute value defined in attrs.xml can be read out and assigned in a way similar to the following.

     mVerticalSpacing = array.getDimensionPixelSize(R.styleable.GridView_verticalSpacing, 0);
     mHorizontalSpacing = array.getDimensionPixelOffset(R.styleable.GridView_horizontalSpacing, 0);
     maxNumber = array.getInt(R.styleable.GridView_maxNumber, 0);
     mNumColumns = array.getInt(R.styleable.GridView_numColumns, 4);
     initNum = array.getInt(R.styleable.GridView_initNum, 1);
     name = array.getString(R.styleable.GridView_name);

3. Use in layout files

     <com.android.attrsetting.grid.GridView
            android:id="@+id/gv_test"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="50px"
            psv:horizontalSpacing="50px"
            psv:initNum="1"
            psv:name="直接定义在布局文件中 设置的名字 优先级为1"
            psv:verticalSpacing="50px" />

In addition, new names must be added to the layout file to use these custom attributes.

  xmlns:psv="http://schemas.android.com/apk/res-auto"

Priority of second attribute assignment

The attributes defined in the first part can be assigned in multiple places: like "layout xml assignment", "reference style in layout xml", "style specified by defStyleAttr", "style specified by defStyleRes", "direct in theme" Assignment". When you use context.obtainStyledAttributes(attrs, R.styleable.GridView, defStyleAttr, defStyleRes) in the custom View to obtain values, then which part of the assignment shall prevail?

A few examples are given to illustrate the priority of different settings.

1. Add the attribute name in the custom control GridView

 <!--测试不同的构造函数调用周期-->
<attr name="name" format="string" />

2. In the initialization constructor of GridView, get the value of name through the following code

 private void initAttributes(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        if (attrs == null) {
            return;
        }
         TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GridView, defStyleAttr, defStyleRes);
         if (array == null) {
             return;
         }  
         name = array.getString(R.styleable.GridView_name);
    ........
  } 

The public final TypedArray obtainStyledAttributes(@Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes) method can obtain various attribute sets defined in R.styleable.GridView.

set: It is the various attributes of the control defined in the layout file, including system attributes and custom attributes

attrs: An array of attributes that need to be obtained. That is, which attributes we need to obtain in the layout file. Usually for our custom R.styleable.xxx (if xxx is declared with <declare-styleable> when it is defined, it will return an array set, please refer to the introduction of "1. Declaring attributes in attrs.xml") .

The remaining two parameters can be used with the last two parameters of the four parameters of the constructor mentioned in the four ways of customizing the Android View .

3. Set attribute values ​​in different places to verify priority

The first example: GridViewAttrTheme1SettingActivity

  • (1) The theme for registering GridViewAttrTheme1SettingActivity in AndroidManifest is:
    <activity
            android:name="com.android.attrsetting.GridViewAttrTheme1SettingActivity"
            android:theme="@style/GridViewTheme" />

The @style/GridViewTheme code is as follows:

    <!--在Activity定义Theme时使用defStyleAttr-->
    <style name="GridViewTheme">
        <item name="initNum">5</item>
<!--Theme中直接赋值-->
        <item name="name">通过在Theme的添加name属性 设置的名字 优先级5</item>
        <item name="GridViewStyle">@style/GridViewStyleInTheme</item>
    </style>

    <style name="GridViewStyleInTheme">
        <item name="initNum">3</item>
<!--在Theme中指定defStyleAttr-->
        <item name="name">通过Theme的defStyleAttr属性 设置的名字 优先级3</item>
    </style>

That is, directly assign a value to the name attribute in Themem, and also specify defStyleAttr in Theme. The code of the defStyleAttr defined in the GridView is as follows:

    public GridView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, R.attr.GridViewStyle, 0);
    }
  • (2) Add the following GridView controls in the layout file

1) The first GridView control : set the attribute value of name in the layout file

        <!--优先级为1的情况:在布局文件中定义name属性-->
        <com.android.attrsetting.grid.GridView
            android:id="@+id/gv_test"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="50px"
            psv:horizontalSpacing="50px"
            psv:initNum="1"
            psv:name="直接定义在布局文件中 设置的名字 优先级为1"
            psv:verticalSpacing="50px" />

2) The second GridView control : reference style in the layout file to point to the name attribute

     <!--优先级为2的情况:在布局文件的style属性中定义name属性-->
        <com.android.attrsetting.grid.GridView
            android:id="@+id/gv_test111"
            style="@style/GridViewInLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="50px"
            psv:horizontalSpacing="50px"
            psv:initNum="2"
            psv:verticalSpacing="50px" />

The corresponding @style/GridViewInLayout code is as follows:


    <style name="GridViewInLayout">
        <item name="initNum">2</item>
        <item name="name">通过布局文件引入style,在该style中 设置的名字 优先级为2</item>
    </style>

 3) The third GridView control : do not set the name attribute and style reference in the layout file to point to the name attribute, only specify defStyleAttr in the Theme of the Activity.

       <!--优先级为3的情况:在Theme中通过defStyleAttr属性定义name属性,在控件中使用android:theme不起作用-->
        <com.android.attrsetting.grid.GridView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="50px"
            android:theme="@style/GridViewThemeAttr0"
            psv:horizontalSpacing="50px"
            psv:verticalSpacing="50px" />

        <!--优先级为3的情况:在Theme中通过defStyleAttr属性定义name属性-->
        <com.android.attrsetting.grid.GridView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:padding="50px"
            psv:horizontalSpacing="50px"
            psv:verticalSpacing="50px" />

The difference between the two GridView controls is that the verification that setting android:theme in the control will not work. 

  •  (3) The style specified for defStyleRes in the GridView control
       if (defStyleRes <= 0) {
            defStyleRes = R.style.DefaultGridViewStyleRes;
        }
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.GridView, defStyleAttr, defStyleRes);
        if (array == null) {
            return;
        }
        name = array.getString(R.styleable.GridView_name);

The code of R.style.DefaultGridViewStyleRes is as follows:

    <style name="DefaultGridViewStyleRes">
        <item name="name">自定义UI的默认的styleRes样式中 设置的名字 优先级4</item>
        <item name="initNum">4</item>
    </style>

So for the first GridView control, the above five ways of setting properties all exist; for the second GridView control, except for the direct assignment of name in the layout file, the other four assignment methods all exist; For the third and fourth GridView controls, there are only three assignment methods: "style specified by defStyleAttr", "style specified by defStyleRes", and "direct assignment in theme". Then look at the initialization of GridViewAttrTheme1SettingActivity. The name attribute value of each GridView is:


2020-11-30 14:45:07.874 25978-25978/com.android.widgetplaceholder V/GridView.initAttributes(L:129):  name = 直接定义在布局文件中 设置的名字 优先级为1
2020-11-30 14:45:07.876 25978-25978/com.android.widgetplaceholder V/GridView.initAttributes(L:129):  name = 通过布局文件引入style,在该style中 设置的名字 优先级为2
2020-11-30 14:45:07.878 25978-25978/com.android.widgetplaceholder V/GridView.initAttributes(L:129):  name = 通过Theme的defStyleAttr属性 设置的名字 优先级3
2020-11-30 14:45:07.880 25978-25978/com.android.widgetplaceholder V/GridView.initAttributes(L:129):  name = 通过Theme的defStyleAttr属性 设置的名字 优先级3

You can see from the log: "layout xml assignment"> "reference style in layout xml"> "style specified by defStyleAttr". Because only by sequentially removing the methods of setting the name attribute with the higher priority, can you get the value of the method of setting the name attribute with the lower priority. Look at the priority of another alternative.

The second instance: GridViewAttrTheme2SettingActivity

  • (1) The theme for registering GridViewAttrTheme2SettingActivity in AndroidManifest is:
 <activity
            android:name="com.android.attrsetting.GridViewAttrTheme2SettingActivity"
            android:theme="@style/GridViewThemeAttr0" />

The code of @style/GridViewThemeAttr0 is as follows:

    <!--在Activity定义Theme时无默认的style应用-->
    <style name="GridViewThemeAttr0">
        <item name="initNum">5</item>
        <item name="name">通过在Theme的添加name属性并且没有defStyleAttr 设置的名字 优先级为5</item>
    </style>

In this theme, only the name attribute is directly assigned in the Theme.

  • (2) Add a GridView control in the layout file
    <!--优先级为4/5的情况:在Theme中不定义defStyleAttr属性定义name属性,默认的会使用在控件中定义的defStyleRes中属性-->
    <com.android.attrsetting.grid.GridView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="50px"
        psv:horizontalSpacing="50px"
        psv:verticalSpacing="50px" />

For this control, there are only two ways to set the name attribute: "style specified by defStyleRes" and "direct assignment in theme". Initialize GridViewAttrTheme2SettingActivity and look at the printed log:

2020-11-30 15:03:05.950 25978-25978/com.android.widgetplaceholder V/GridView.initAttributes(L:129):  name = 自定义UI的默认的styleRes样式中 设置的名字 优先级4

From the log, you can see "style specified by defStyleRes"> "direct assignment in theme", and only when the three methods of setting the name attribute in the first instance are not set, "style specified by defStyleRes" is only Will work.

The third instance: GridViewAttrTheme3SettingActivity

(1) The theme for registering GridViewAttrTheme3SettingActivity in AndroidManifest is:

        <activity
            android:name="com.android.attrsetting.GridViewAttrTheme3SettingActivity"
            android:theme="@style/GridViewThemeAttr1" />

The code of @style/GridViewThemeAttr1 is as follows:

    <!--验证优先级5-->
    <style name="GridViewThemeAttr1">
        <item name="initNum">5</item>
        <item name="name">通过在Theme的添加name属性并且没有defStyleAttr 设置的名字 优先级为5</item>
        <item name="GridViewStyle">@style/GridViewStyleNoInTheme</item>
    </style>

    <style name="GridViewStyleNoInTheme">
        <item name="initNum">5</item>
    </style>

 It can be seen that the name is directly assigned in the Theme, and the style specified by defStyleAttr is also set, but there is no name attribute in the style.

(2) Add a GridView control in the layout file

    <!--优先级为4/5的情况:在Theme中不定义defStyleAttr属性定义name属性,默认的会使用在控件中定义的defStyleRes中属性-->
    <com.android.attrsetting.grid.GridView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="50px"
        psv:horizontalSpacing="50px"
        psv:verticalSpacing="50px" />

Initialize GridViewAttrTheme2SettingActivity and look at the printed log:

2020-11-30 15:03:32.184 25978-25978/com.android.widgetplaceholder V/GridView.initAttributes(L:129):  name = 通过在Theme的添加name属性并且没有defStyleAttr 设置的名字 优先级为5

It can be seen from the printed log that if defStyleRes is set when customizing the control, then to make the name attribute set in the Theme work, the style specified by defStyleAttr must be set in the Theme, but the style does not have the name attribute. Assignment.

Three summary

This way of custom attributes can conveniently assign the attributes of the control through methods such as layout files, styles, etc., and can set different values ​​with different themes. Then the priority of setting the attribute is "layout xml assignment"> "reference style in layout xml"> "style specified by defStyleAttr"> "style specified by defStyleRes"> "direct assignment in theme". In addition, if defStyleRes is set, then only the attribute of name is set in Theme, and defStyleAttr must be set in Theme to point to style, and no attribute is assigned to the style.

In addition, the code involved in this article has been uploaded to github . Related codes are mainly com/android/attrsetting related codes.

Guess you like

Origin blog.csdn.net/nihaomabmt/article/details/109800839