Android自定义View - Theme和Style

446 阅读3分钟

今天我们讲一下主题和样式的相关知识。每个Activity都是可以有自己特定的主题的,也可以直接在AndroidManifest.xml中定义所有Activity的默认主题。

<application
android:theme="@style/Theme.MyApplication">
</application>

View的构造方法解析

我们在继承View的时候,可以重写4个参数,一般我们就重写1个或2个,如果这个自定义View不需要在xml中使用,只需要在代码中new出来使用,那么你只需要重写1个参数的构造方法。如果你需要在xml中使用该控件,就重写1个参数的构造方法和2个以上参数的构造方法。我们一起来看一下,这几个参数的意思吧。

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

public class MyView extends View {
    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    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);
        initAttrs(context, attrs, defStyleAttr, defStyleRes);
    }

    private void initAttrs(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyView, R.attr.doraViewStyle, R.style.DoraViewStyle);
        int color = a.getColor(R.styleable.MyView_dora_background, 0);
        setBackgroundColor(color);
        a.recycle();
    }
}
  1. context:上下文,重写必须传入,应用基本环境,可以获取到很多应用的资源信息,不多解释。
  2. attrs:自定义控件的自定义属性在布局xml中被指定后解析并赋值到这个变量,如果需要在xml中使用这个控件,则需要传入这个参数。
  3. defStyleAttr:R.attr形式的style引用,没有在xml的控件节点指定属性值,且没有在xml的控件节点引用style,然后就查询该自定义View的默认样式。
  4. defStyleRes:R.style形式的style引用,没有在xml的控件节点指定属性值,且没有在xml的控件节点引用style,然后就查询该自定义View的默认样式。

我们总结下自定义控件的属性取值优先级,如果在xml的控件节点直接定义了该属性的值,比如android:layout_width="match_parent",无论是否引用了style="@style/xxxStyle",都使用直接指定的。然后就是查找该控件使用时定义的style,接下来看该控件本身有没有提供该属性的默认值,即defStyleAttr和defStyleRes。其中defStyleAttr又优先于defStyleRes,且一般只会用一个。这两个参数最终是在context.obtainStyledAttributes()方法时使用4个参数的方法时使用的。最后才会用AndroidManifest.xml中activity和application节点指定的主题。

直接指定属性 -> style -> defStyleAttr -> defStyleRes -> 主题

自己定义一系列自定义View需要定义以下几个文件主题和样式。在自定义View中要使用4个参数的context.obtainStyledAttributes()方法,第3个和第4个参数是一起使用的,因为没有3个参数的方法。我们可以在构造方法中直接指定,并传给父类,然后写一个initAttrs(context, attrs, defStyleAttr, defStyleRes);传过来。

attrs.xml

<!-- 你的控件的自定义属性 -->
<declare-styleable name="YourCustomView">
    <item name="your_custom_view_field1" format="string"/>
    <item name="your_custom_view_field2" format="color"/>
    <item name="your_custom_view_field3" format="reference"/>
</declare-styleable>

    <!-- 定义一些attr,不定义则在themes.xml中的item中找不到该属性,在View的构造函数第3个参数指定 -->
    <attr name="doraViewStyle" format="reference"/>
    <attr name="doraTextViewStyle" format="reference"/>
    <attr name="doraButtonStyle" format="reference"/>
    <attr name="doraCurveChartStyle" format="reference"/>
    <attr name="doraFlashViewStyle" format="reference"/>

控件自定义属性不做过多解释。

styles.xml

<style name="DoraTextViewStyle">
    <item name="android:textSize">40sp</item>
    <item name="android:textColor">@color/black</item>
</style>

对应View的构造方法和context.obtainStyledAttributes()方法的第4个参数。

themes.xml

<style name="DoraCustomViewTheme" parent="Theme.MaterialComponents.DayNight.DarkActionBar">
    <item name="doraViewStyle">@style/DoraViewStyle</item>
    <item name="doraTextViewStyle">@style/DoraTextViewStyle</item>
    <item name="doraCurveChartStyle">@style/DoraCurveChartStyle</item>
    <item name="doraButtonStyle">@style/DoraButtonStyle</item>
    <item name="doraFlashViewStyle">@style/DoraFlashViewStyle</item>
</style>

提供一个主题供指定给AndroidManifest.xml,parent可以换。

验证运行效果

指定值,也指定样式,显示指定值的背景色。 截屏2023-01-22 22.27.06.png 去掉指定值,只用style,显示样式的背景色。 截屏2023-01-22 22.27.47.png 截屏2023-01-22 22.28.30.png 啥都不写,还是有背景色,也不会用到获取自定义属性时的默认值0,即黑色。 截屏2023-01-22 22.29.20.png