今天我们讲一下主题和样式的相关知识。每个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();
}
}
- context:上下文,重写必须传入,应用基本环境,可以获取到很多应用的资源信息,不多解释。
- attrs:自定义控件的自定义属性在布局xml中被指定后解析并赋值到这个变量,如果需要在xml中使用这个控件,则需要传入这个参数。
- defStyleAttr:R.attr形式的style引用,没有在xml的控件节点指定属性值,且没有在xml的控件节点引用style,然后就查询该自定义View的默认样式。
- 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可以换。
验证运行效果
指定值,也指定样式,显示指定值的背景色。
去掉指定值,只用style,显示样式的背景色。
啥都不写,还是有背景色,也不会用到获取自定义属性时的默认值0,即黑色。