我是靠谱客的博主 斯文天空,最近开发中收集的这篇文章主要介绍探索了解自定义View中属性 style theme的关系,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

背景

日常开发中我们经常会遇到自定义view的情形,我们首先就会覆盖View的三个或者四个构造方法;如下:


class MyView extends View {
    public MyView(Context context) {
        super(context);
    }
    public MyView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }
}

上述四个构造方法相信因为各种各样的原因我们只关注了前两个参数,context 和 attrs并且准确的知道他们是干啥的。但是后面的两个参数很少用到,甚至根本就不知道干啥用的。我也是在观摩别人的库中使用到了后面的两个参数才研究了一下。研究一下之后有了关于app主题的很多想法,顺便会写到此文。因为后面两个参数一方面会大大节省我们的开发工作量,另一方面和主题(theme)和风格(style)配合,我们可以让我们的app切换各种样式有了高效率的可能性,顺便可以说app 的ui组件可以作为平台化的产品,快速应用在新的app中,只需要改几个样式,整个app就不一样了。
1.关于自定义View的属性
一般情况下我们自定义view的属性大概经历以下几个步骤;
1.定义
在res/values/目录下新建或者在现有的attrs.xml中定义相对应的styleable,我们都使用string类型做为例子,其他类型大家类比即可。

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView">
        <attr name="attr1" format="string"/>
        <attr name="attr2" format="string"/>
        <attr name="attr3" format="string"/>
        <attr name="attr4" format="string"/>
        <attr name="attr5" format="string"/>
        <attr name="attr6" format="string"/>
        <attr name="attr7" format="string"/>
    </declare-styleable>
</resources>

2.在java代码中获取相应的属性值

class MyView extends View {
    private final  static String TAG = "MyView";
    public MyView(Context context) {
        super(context);
    }
    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context,attrs,0);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context,attrs,0,0);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }
    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView, defStyleAttr, defStyleRes);
        Log.e(TAG,"attr1 - >" + typedArray.getString(R.styleable.MyView_attr1));
        Log.e(TAG,"attr2 - >" + typedArray.getString(R.styleable.MyView_attr2));
        Log.e(TAG,"attr3 - >" + typedArray.getString(R.styleable.MyView_attr3));
        Log.e(TAG,"attr4 - >" + typedArray.getString(R.styleable.MyView_attr4));
        Log.e(TAG,"attr5 - >" + typedArray.getString(R.styleable.MyView_attr5));
        Log.e(TAG,"attr6 - >" + typedArray.getString(R.styleable.MyView_attr6));
        Log.e(TAG,"attr7 - >" + typedArray.getString(R.styleable.MyView_attr7));
        typedArray.recycle();
    }
}

3.在布局中使用属性

  <com.example.customviewattrs.MyView
        app:attr1 = "布局中"
        app:attr2 = "布局中"
        app:attr3 = "布局中"
        app:attr4 = "布局中"
        app:attr5 = "布局中"
        app:attr6 = "布局中"
        app:attr7 = "布局中"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="@string/next"/>

经过3个步骤我们通常就已经完成了自定义view属性相关的开发工作。我们运行工程,打印log,发现所有属性都能成功获取,然而到这里完全没有完。

2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr1 - >布局中
2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr2 - >布局中
2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr3 - >布局中
2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-22 17:41:40.020 17397-17397/com.example.customviewattrs E/MyView: attr7 - >布局中

几个问题
1 attrs.xml中的attr 必须用declare-styleable包起来么
不包起来也可以。但我们使用的时候会有些麻烦,体验过麻烦过后就知道我们包起来的意义了。代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--    <declare-styleable name="MyView">-->
        <attr name="attr1" format="string"/>
        <attr name="attr2" format="string"/>
        <attr name="attr3" format="string"/>
        <attr name="attr4" format="string"/>
        <attr name="attr5" format="string"/>
        <attr name="attr6" format="string"/>
        <attr name="attr7" format="string"/>
<!--    </declare-styleable>-->
</resources>

布局中。注意我们需要自己定另外的命名空间:xmlns:zh=“http://schemas.android.com/apk/res-auto”

<com.example.customviewattrs.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/next"
        zh:attr1="布局中"
        zh:attr2="布局中"
        zh:attr3="布局中"
        zh:attr4="布局中"
        zh:attr5="布局中"
        zh:attr6="布局中"
        zh:attr7="布局中" />

重点看下获取属性的代码麻烦很多。而且会有些爆红,但能运行,说明android 不建议这么做

class MyView extends View {
    private final static String TAG = "MyView";
    //手动定义attr数组
    private int[] attrsDefine = {R.attr.attr1, R.attr.attr2, R.attr.attr3, R.attr.attr4, R.attr.attr5, R.attr.attr6, R.attr.attr7};
    public MyView(Context context) {
        super(context);
    }
    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, 0, 0);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }
    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, attrsDefine, defStyleAttr, defStyleRes);
        Log.e(TAG, "attr1 - >" + typedArray.getString(0));
        Log.e(TAG, "attr2 - >" + typedArray.getString(1));
        Log.e(TAG, "attr3 - >" + typedArray.getString(2));
        Log.e(TAG, "attr4 - >" + typedArray.getString(3));
        Log.e(TAG, "attr5 - >" + typedArray.getString(4));
        Log.e(TAG, "attr6 - >" + typedArray.getString(5));
        Log.e(TAG, "attr7 - >" + typedArray.getString(6));
        typedArray.recycle();
    }
}

log也没问题。但不建议这么做,styleable 帮我们封装了很多操作我们没必要自己搞的这么麻烦。

2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr1 - >布局中
2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr2 - >布局中
2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr3 - >布局中
2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-22 18:24:11.398 23183-23183/com.example.customviewattrs E/MyView: attr7 - >布局中

2.系统中已有的属性我能直接使用,比如android:text ,为啥我用的时候获取不成功。或者两个自定义view的属性想同崩溃。

 <com.example.customviewattrs.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/next"
        app:attr1="布局中"
        app:attr2="布局中"
        app:attr3="布局中"
        app:attr4="布局中"
        app:attr5="布局中"
        app:attr6="布局中"
        app:attr7="布局中" />
<resources>

   <declare-styleable name="MyView">
       <attr name="attr1" format="string"/>
       <attr name="attr2" format="string"/>
       <attr name="attr3" format="string"/>
       <attr name="attr4" format="string"/>
       <attr name="attr5" format="string"/>
       <attr name="attr6" format="string"/>
       <attr name="attr7" format="string"/>
       <attr name="text" format="string"/>
   </declare-styleable>
</resources>

此时我们在java代码中获取text属性的值是空的。

  Log.e(TAG,"text - >" + typedArray.getString(R.styleable.MyView_text));

log

2021-01-22 20:31:20.813 6511-6511/com.example.customviewattrs E/MyView: text - >null

其原因是我们在重新定义的text属性所属的命名空间和之前的android:不是同一个。两种解决办法:
1.布局中
android:text="@string/next"
改成命名空间
app:text="@string/next"
2.或者在attrs处定义加上android的命名空间,
//去掉formate 因为系统定义过了
并且获取该属性方式按照如下方式获取android命名空间属性。
Log.e(TAG,“text - >” + typedArray.getString(R.styleable.MyView_android_text));

浅析AttributeSet 和 TypedArray

字面上理解AttributSet 是个包含参数的集合,那么通过看他的方法就知道如何获取其内容了。

 int attributeCount = attrs.getAttributeCount();
        for (int i = 0; i < attributeCount; i++) {
            String attributeName = attrs.getAttributeName(i);
            String attributeValue = attrs.getAttributeValue(i);
            Log.e(TAG,attributeName +" - - >" + attributeValue);
        }
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: layout_width - - >-2
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: layout_height - - >-2
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: text - - >@2131755093
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr1 - - >布局中
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr2 - - >布局中
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr3 - - >布局中
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr4 - - >布局中
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr5 - - >布局中
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr6 - - >布局中
2021-01-25 11:18:06.555 24607-24607/com.example.customviewattrs E/MyView: attr7 - - >布局中

我们看到布局中所有的属性都在attrs中,并且text所对应的资源变成了@2131755093;由此我们可以得到两个结论:1、布局中所有属性都会被inflate到attrs这个集合中,所以平时开发中有些历史遗留的无用属性该删就删了吧,还是有性能和内存的占用的影响的。2、attrs集合中的资源内容不能通过直接获取value得到,此时typedArray就派上用场了。
ok,那现在typedArray的用处就是简化我们的操作的,我们要获取@2131755093资源所对应的具体值,肯定要经过很多步骤才能达到,typedArray封装了所有操作。

关于style

接下来我们探索关于style的七七八八。
最初我们了解style一定是在不居中使用,若干有相同的属性组件,把有共性属性的属性放在一起组成一个style属性集合,以达到复用的目的,比如:
style.xml中增加myViewStyle

<resources>
    <style name="myViewStyle">
        <item name="attr1">style.xml 中设置的属性</item>
        <item name="attr2">style.xml 中设置的属性</item>
        <item name="attr3">style.xml 中设置的属性</item>
        <item name="attr4">style.xml 中设置的属性</item>
        <item name="attr5">style.xml 中设置的属性</item>
        <item name="attr6">style.xml 中设置的属性</item>
        <item name="attr7">style.xml 中设置的属性</item>
        <item name="android:text">style.xml 中设置的属性</item>
    </style>
</resources>

在布局中设置,并且把attr1,attr2 attr3的属性去掉

    <com.example.customviewattrs.MyView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        style="@style/myViewStyle" 
        android:text="@string/next"
        app:attr4="布局中"
        app:attr5="布局中"
        app:attr6="布局中"
        app:attr7="布局中" />

我们在代码中保持用typedArray获取属性值:

 private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView, defStyleAttr, defStyleRes);
        Log.e(TAG,"attr1 - >" + typedArray.getString(R.styleable.MyView_attr1));
        Log.e(TAG,"attr2 - >" + typedArray.getString(R.styleable.MyView_attr2));
        Log.e(TAG,"attr3 - >" + typedArray.getString(R.styleable.MyView_attr3));
        Log.e(TAG,"attr4 - >" + typedArray.getString(R.styleable.MyView_attr4));
        Log.e(TAG,"attr5 - >" + typedArray.getString(R.styleable.MyView_attr5));
        Log.e(TAG,"attr6 - >" + typedArray.getString(R.styleable.MyView_attr6));
        Log.e(TAG,"attr7 - >" + typedArray.getString(R.styleable.MyView_attr7));
        Log.e(TAG,"text - >" + typedArray.getString(R.styleable.MyView_android_text));
        typedArray.recycle();
    }

log:

2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr1 - >style.xml 中设置的属性
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr2 - >style.xml 中设置的属性
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr3 - >style.xml 中设置的属性
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: attr7 - >布局中
2021-01-25 11:54:05.321 4828-4828/com.example.customviewattrs E/MyView: text - >Next

我们看到布局设置的属性会覆盖掉style.xml中设置的,即最近原则。我们继续再theme中设置相关属性。
themes.xml

    <style name="Theme.Customviewattrs" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
        <item name="attr1">theme 中 设置属性</item>
        <item name="attr2">theme 中 设置属性</item>
        <item name="attr3">theme 中 设置属性</item>
    </style>

同时去掉style.xml中attr2 attr3

<resources>
    <style name="myViewStyle">
        <item name="attr1">style.xml 中设置的属性</item>
        <item name="attr4">style.xml 中设置的属性</item>
        <item name="attr5">style.xml 中设置的属性</item>
        <item name="attr6">style.xml 中设置的属性</item>
        <item name="attr7">style.xml 中设置的属性</item>
        <item name="android:text">style.xml 中设置的属性</item>
    </style>
</resources>

继续打log

2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr1 - >style.xml 中设置的属性
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr2 - >theme 中 设置属性
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr3 - >theme 中 设置属性
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: attr7 - >布局中
2021-01-25 11:58:44.681 5628-5628/com.example.customviewattrs E/MyView: text - >Next

同理我们看到遵循就近原则,即theme中的同属性被style中覆盖,style中的同属性被布局中的属性覆盖。

关于自定义view的构造器中的defStyleAttr 和defStyleRes

饶了一大圈回到自定义view本身,我们先看源码中对四个参数的解释就能明白大半了。

 /**
     * Perform inflation from XML and apply a class-specific base style from a
     * theme attribute or style resource. This constructor of View allows
     * subclasses to use their own base style when they are inflating.
     * <p>
     * When determining the final value of a particular attribute, there are
     * four inputs that come into play:
     * <ol>
     * <li>Any attribute values in the given AttributeSet.
     * <li>The style resource specified in the AttributeSet (named "style").
     * <li>The default style specified by <var>defStyleAttr</var>.
     * <li>The default style specified by <var>defStyleRes</var>.
     * <li>The base values in this theme.
     * </ol>
     * <p>
     * Each of these inputs is considered in-order, with the first listed taking
     * precedence over the following ones. In other words, if in the
     * AttributeSet you have supplied <code>&lt;Button * textColor="#ff000000"&gt;</code>
     * , then the button's text will <em>always</em> be black, regardless of
     * what is specified in any of the styles.
     *
     * @param context The Context the view is running in, through which it can
     *        access the current theme, resources, etc.
     * @param attrs The attributes of the XML tag that is inflating the view.
     * @param defStyleAttr An attribute in the current theme that contains a
     *        reference to a style resource that supplies default values for
     *        the view. Can be 0 to not look for defaults.
     * @param defStyleRes A resource identifier of a style resource that
     *        supplies default values for the view, used only if
     *        defStyleAttr is 0 or can not be found in the theme. Can be 0
     *        to not look for defaults.
     * @see #View(Context, AttributeSet, int)
     */
    public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)

前两个参数不再赘述,

defStyleAttr

defStyleAttr : An attribute in the current theme that contains a reference to a style resource that supplies default values for the view. Can be 0 to not look for defaults.
大意是,在当前theme中的的一个引用的属性,指向一个style资源,style中包含该view的若干默认属性的值。此值可以是0来忽略此参数的效果。其实就是有现实的例子的,TextView中的属性:textAppearance
源码中textAppearace的定义就是这个意思

        <!-- Default appearance of text: color, typeface, size, and style. -->
        <attr name="textAppearance" format="reference" />

我们实测一下:
attrs.xml中添加属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyView">
        <attr name="attr1" format="string" />
        <attr name="attr2" format="string" />
        <attr name="attr3" format="string" />
        <attr name="attr4" format="string" />
        <attr name="attr5" format="string" />
        <attr name="attr6" format="string" />
        <attr name="attr7" format="string" />
        <attr name="android:text" />
    </declare-styleable>
    <!--   定义单独的属性  format 是reference-->
    <attr name="defStyleAttr" format="reference" />
</resources>

style.xml添加默认style :mydefStyle

<resources>
    <style name="myViewStyle">
        <item name="attr1">style.xml 中设置的属性</item>
        <item name="attr4">style.xml 中设置的属性</item>
        <item name="attr5">style.xml 中设置的属性</item>
        <item name="attr6">style.xml 中设置的属性</item>
        <item name="attr7">style.xml 中设置的属性</item>
        <item name="android:text">style.xml 中设置的属性</item>
    </style>
    <style name="mydefStyle">
        <item name="attr1">style.xml mydefStyle  中设置的属性</item>
        <item name="attr4">style.xml mydefStyle 中设置的属性</item>
        <item name="attr5">style.xml mydefStyle 中设置的属性</item>
        <item name="attr6">style.xml mydefStyle 中设置的属性</item>
        <item name="attr7">style.xml mydefStyle 中设置的属性</item>
        <item name="android:text">style.xml mydefStyle 中设置的属性</item>
    </style>
</resources>

theme中添加该属性配置

  <style name="Theme.Customviewattrs" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
        <item name="attr1">theme 中 设置属性</item>
        <item name="attr2">theme 中 设置属性</item>
        <item name="attr3">theme 中 设置属性</item>
        <item name="defStyleAttr">@style/mydefStyle</item>
    </style>

代码中把新加的属性配置加入代码中如下:


public class MyView extends View {
    private final  static String TAG = "MyView";
    public MyView(Context context) {
        super(context);
    }
    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context,attrs,R.attr.defStyleAttr);///这里传入
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context,attrs,defStyleAttr,0);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }
    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView, defStyleAttr, defStyleRes);
        Log.e(TAG,"attr1 - >" + typedArray.getString(R.styleable.MyView_attr1));
        Log.e(TAG,"attr2 - >" + typedArray.getString(R.styleable.MyView_attr2));
        Log.e(TAG,"attr3 - >" + typedArray.getString(R.styleable.MyView_attr3));
        Log.e(TAG,"attr4 - >" + typedArray.getString(R.styleable.MyView_attr4));
        Log.e(TAG,"attr5 - >" + typedArray.getString(R.styleable.MyView_attr5));
        Log.e(TAG,"attr6 - >" + typedArray.getString(R.styleable.MyView_attr6));
        Log.e(TAG,"attr7 - >" + typedArray.getString(R.styleable.MyView_attr7));
        Log.e(TAG,"text - >" + typedArray.getString(R.styleable.MyView_android_text));
        typedArray.recycle();
    }
}

运行看log,一个也没见默认配置

2021-01-25 13:09:16.871 25622-25622/com.example.customviewattrs E/MyView: attr1 - >style.xml 中设置的属性
2021-01-25 13:09:16.871 25622-25622/com.example.customviewattrs E/MyView: attr2 - >theme 中 设置属性
2021-01-25 13:09:16.871 25622-25622/com.example.customviewattrs E/MyView: attr3 - >theme 中 设置属性
2021-01-25 13:09:16.871 25622-25622/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-25 13:09:16.871 25622-25622/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-25 13:09:16.871 25622-25622/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-25 13:09:16.872 25622-25622/com.example.customviewattrs E/MyView: attr7 - >布局中
2021-01-25 13:09:16.872 25622-25622/com.example.customviewattrs E/MyView: text - >Next
所以去掉除了mydefStyle处配置的attr1之外所有attr1配置:log
2021-01-25 13:16:01.241 26237-26237/com.example.customviewattrs E/MyView: attr1 - >style.xml mydefStyle 中设置的属性
2021-01-25 13:16:01.241 26237-26237/com.example.customviewattrs E/MyView: attr2 - >theme 中 设置属性
2021-01-25 13:16:01.241 26237-26237/com.example.customviewattrs E/MyView: attr3 - >theme 中 设置属性
2021-01-25 13:16:01.241 26237-26237/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-25 13:16:01.241 26237-26237/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-25 13:16:01.241 26237-26237/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-25 13:16:01.242 26237-26237/com.example.customviewattrs E/MyView: attr7 - >布局中
2021-01-25 13:16:01.242 26237-26237/com.example.customviewattrs E/MyView: text - >Next

我们看到如果所有地方都没有配置的属性,系统就会到配置的defStyleAttr属性中去获取,至此defStyleAttr的作用很清楚了,而且他用在主题中配置,并且优先级最低,这就是我在布局中这几写个textview会有默认属性的原因了。

defStyleRes

至于这个参数其实就是在theme中未指定style属性时,可以动态指定默认的style。我们新建一个style:mydefStyleNew

<resources>
    <style name="myViewStyle">
        <item name="attr3">style.xml 中设置的属性</item>
        <item name="attr4">style.xml 中设置的属性</item>
        <item name="attr5">style.xml 中设置的属性</item>
        <item name="attr6">style.xml 中设置的属性</item>
        <item name="attr7">style.xml 中设置的属性</item>
        <item name="android:text">style.xml 中设置的属性</item>
    </style>
    <style name="mydefStyle">
        <item name="attr1">style.xml mydefStyle  中设置的属性</item>
        <item name="attr4">style.xml mydefStyle 中设置的属性</item>
        <item name="attr5">style.xml mydefStyle 中设置的属性</item>
        <item name="attr6">style.xml mydefStyle 中设置的属性</item>
        <item name="attr7">style.xml mydefStyle 中设置的属性</item>
        <item name="android:text">style.xml mydefStyle 中设置的属性</item>
    </style>
    <style name="mydefStyleNew">
        <item name="attr1">style.xml mydefStyleNew  中设置的属性</item>
        <item name="attr2">style.xml mydefStyleNew  中设置的属性</item>
        <item name="attr3">style.xml mydefStyleNew  中设置的属性</item>
        <item name="attr4">style.xml mydefStyleNew 中设置的属性</item>
        <item name="attr5">style.xml mydefStyleNew 中设置的属性</item>
        <item name="attr6">style.xml mydefStyleNew 中设置的属性</item>
        <item name="attr7">style.xml mydefStyleNew 中设置的属性</item>
        <item name="android:text">style.xml mydefStyleNew 中设置的属性</item>
    </style>
</resources>

在代码中设置:

public class MyView extends View {
    private final  static String TAG = "MyView";
    public MyView(Context context) {
        super(context);
    }
    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context,attrs,R.attr.defStyleAttr);
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context,attrs,defStyleAttr,R.style.mydefStyleNew);//这里设置
    }
    public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context, attrs, defStyleAttr, defStyleRes);
    }
    private void init(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyView, defStyleAttr, defStyleRes);
        Log.e(TAG,"attr1 - >" + typedArray.getString(R.styleable.MyView_attr1));
        Log.e(TAG,"attr2 - >" + typedArray.getString(R.styleable.MyView_attr2));
        Log.e(TAG,"attr3 - >" + typedArray.getString(R.styleable.MyView_attr3));
        Log.e(TAG,"attr4 - >" + typedArray.getString(R.styleable.MyView_attr4));
        Log.e(TAG,"attr5 - >" + typedArray.getString(R.styleable.MyView_attr5));
        Log.e(TAG,"attr6 - >" + typedArray.getString(R.styleable.MyView_attr6));
        Log.e(TAG,"attr7 - >" + typedArray.getString(R.styleable.MyView_attr7));
        Log.e(TAG,"text - >" + typedArray.getString(R.styleable.MyView_android_text));
        typedArray.recycle();
    }
}

删掉主题中所有的自定义属性:

 <!-- Base application theme. -->
    <style name="Theme.Customviewattrs" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
<!--        <item name="attr2">theme 中 设置属性</item>-->
<!--        <item name="attr3">theme 中 设置属性</item>-->
<!--        <item name="defStyleAttr">@style/mydefStyle</item>-->
    </style>

log

2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr1 - >style.xml mydefStyleNew 中设置的属性
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr2 - >style.xml mydefStyleNew 中设置的属性
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr3 - >style.xml 中设置的属性
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: attr7 - >布局中
2021-01-25 14:08:06.826 8515-8515/com.example.customviewattrs E/MyView: text - >Next

再打开主题中的刚才注释掉的代码:
log

2021-01-25 14:09:46.809 10858-10858/com.example.customviewattrs E/MyView: attr1 - >style.xml mydefStyle 中设置的属性
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: attr2 - >theme 中 设置属性
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: attr3 - >style.xml 中设置的属性
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: attr4 - >布局中
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: attr5 - >布局中
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: attr6 - >布局中
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: attr7 - >布局中
2021-01-25 14:09:46.810 10858-10858/com.example.customviewattrs E/MyView: text - >Next

发现mydefStyleNew中配置的属性值都未显示。综上我们可以得出view的属性通过配置设置的几种方法:
1.在布局xml中直接配置
2.在声明style,并在布局中配置其style属性。
3.在theme中设置其属性。
4.通过自定义style属性,在theme中配置属性对应style声明。
5.通过defStyleRes ,不需要任何额外配置自定义view自带默认的属性集合。
一般情况下其优先级:(1)> (2) > (4) > (3) >(5).

总结

通过以上探索,我们在封装ui组件库的时候可以通过主题来配置应用的视觉属性,同样谷歌官方的实践也是如此,达到通过少量配置来整体切换app风格的高效工程。所以在未来的ui换肤和平台化过程中可以考虑此特性。

本文首发于我的博客—点击跳转

最后

以上就是斯文天空为你收集整理的探索了解自定义View中属性 style theme的关系的全部内容,希望文章能够帮你解决探索了解自定义View中属性 style theme的关系所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(39)

评论列表共有 0 条评论

立即
投稿
返回
顶部