Android自定义Style

1 背景介绍

最近接触了一款APP,从Android5.1.1升级到Android8.1,整个APP使用的主题为”@android:style/Theme.DeviceDefault.Light”。测试在Bugzilla上提了好几个关于UI方面的Bug。研发一看就知道这些不是Bug,但是测试他们毕竟不是开发,只会看表面现象,同一个APP在不同版本Android平台上UI不一致都是Bug。要给测试、PM说清楚这些不是Bug,就必须了解Android系统的Style.Style可以简单理解为一组属性的集合,方便对APP的样式做一个统一处理。下面从属性开始了解Style.

2 属性

属性(attr)可以简单理解为特性。比如名字、皮肤颜色等等都是属性。

2.1申明属性

假如要实现人类皮肤和名字这样一组属性,那在Android中要如何实现呢?

首先在“res/values”目录下创建一个文件名为attrs.xml。具体内容如下:

<?xml version="1.0" encoding="utf-8"?>

<resources>

<!-- 自定义属性skinColor -->

<attr name="customSkinColor" format="color" />

<!-- 自定义属性name -->

<attr name="customName" format="string" />

<!-- 申明一个人类的属性的集合 -->

<declare-styleable name="customPerson">

<attr name="customSkinColor" />

<attr name="customName" />

</declare-styleable>

</resources>

 

这样申明之后发生了什么?

2.2 属性实现

当申明了属性之后,在R.java中会有如下定义存在:

public final class R {

public static final class attr {

public static final int customSkinColor=0x7f03006f;

public static final int customName=0x7f03006d;

}

public static final class styleable {

public static final int[] customPerson={

0x7f03006d, 0x7f03006f

};

public static final int customPerson_customName=0;

public static final int customPerson_customSkinColor=1;

}

}

 

Android中的每个资源,都有它唯一的编号。编号是一个32位数字,用十六进制表示就是0xPPTTEEEE。PP为package id,TT 为type id,EEEE 为entry id.比如上面这个例子,执行下面的命令:

aapt d resources app-debug.apk | grep 0x7f03006d

spec resource 0x7f03006d com.crab.test:attr/customName: flags=0x00000000

resource 0x7f03006d com.crab.test:attr/customName: <bag>

这就表示customName这个资源的编号是 0x7f03006d 。它的package id是0x7f,资源类型的id为0x03(代表attr),它的entry id为0x006d。

备注:package id的值为0x01是系统资源,也就是包名为”android”的资源。type id代表的是drawable,string,layout等等类型的资源,默认是从值0x01开始。entry id代表这个资源在type id中的位置,默认是从0x0000开始。

2.3 属性的使用

2.3.1 属性组使用

属性组(styleable)是属性的集合。前面定义了一个属性组(customPerson),它有两个属性(customName、customSkinColor)。下面来使用属性组:

public class PersonView extends TextView {

public PersonView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

//customPerson是一个数组,在刚才R.java中定义过

final TypedArray a = context.obtainStyledAttributes(

attrs, R.styleable.customPerson, 0, 0);

//customPerson_customSkinColor的值为1,数组第二个元素

int skinColor = a.getColor(R.styleable.customPerson_customSkinColor, Color.WHITE);

//customPerson_customName的值为0,数组第一个元素

String name = a.getString(R.styleable.customPerson_customName);

setText(name);

setTextColor(skinColor);

a.recycle();

}

}

 

属性组的使用是不是很简单,下面设置具体属性值。

2.3.2 属性值的设定

现在来给具体的属性设置值:

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout

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

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

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:layout_behavior="@string/appbar_scrolling_view_behavior"

tools:context="com.crab.test.MainActivity"

tools:showIn="@layout/activity_main">


<com.crab.test.PersonView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

app:customName="xiechaojun" //自己定义的属性

app:customSkinColor="#FFFF0000" //自己定义的属性

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent"

/>


</android.support.constraint.ConstraintLayout>

 

属性的使用也很简单。现在考虑一个问题,如果APP里面有10个PersonView,想让所有PersonView的皮肤颜色都是黄色,要怎么办?下面介绍Style.

3 Android Style

3.1 定义集合属性

10个PersonView?咋办?首先在attrs.xml中创建一个属性名字以及使用这个属性的集合

<?xml version="1.0" encoding="utf-8"?>

<resources>

<!-- 自定义属性skinColor -->

<attr name="customSkinColor" format="color" />

<!-- 自定义属性name -->

<attr name="customName" format="string" />

<!-- 申明一个人类的属性的集合 -->

<declare-styleable name="customPerson">

<attr name="customSkinColor" />

<attr name="customName" />

</declare-styleable>


<!-- 自定义属性personViewStyle -->

<attr name="personViewStyle" format="reference" />

<!-- 申明一整个应用人类要使用的属性的集合 -->

<declare-styleable name="customPersonStyle">

<attr name="personViewStyle" />

</declare-styleable>

</resources>

 

3.2 自定义Style

在“res/values/styles.xml”文件中添加自定义的Style。

<resources>

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">

<!-- Customize your theme here. -->

<item name="colorPrimary">@color/colorPrimary</item>

<item name="colorPrimaryDark">@color/colorPrimaryDark</item>

<item name="colorAccent">@color/colorAccent</item>

<!-- 与上边属性名字对应 -->

<item name="personViewStyle">@style/PersonStyle</item>

</style>

<!-- 定义所有PersonView的皮肤颜色为黄色 -->

<style name="PersonStyle">

<item name="customSkinColor">#FFFFFF00</item>

</style>

</resources>

 

3.3 使用自定义的Style

3.3.1 PersonView的使用

请注意与2.3.1小节实现不同的地方:

public class PersonView extends TextView {

public PersonView(Context context, @Nullable AttributeSet attrs) {

super(context, attrs);

//系统主题

final Resources.Theme theme = context.getTheme();

//获取自定义的person style属性集合

final TypedArray a = theme.obtainStyledAttributes(

attrs, R.styleable.customPersonStyle, 0, 0);

//获取到具体某个person style ID.

int personStyleId =

a.getResourceId(R.styleable.customPersonStyle_personViewStyle,0);

if(personStyleId!=-1) {

TypedArray aa =

theme.obtainStyledAttributes(personStyleId, R.styleable.customPerson);

//获取到主题中设定的皮肤值

int skinColor = aa.getColor(R.styleable.customPerson_customSkinColor, Color.BLUE);

setTextColor(skinColor);

a.recycle();

}

a.recycle();

}

}

 

3.3.2 布局文件中使用

在布局文件中定义2个PersonView,不设置它皮肤颜色的属性,但是最后效果是两个PersonView的字体颜色都是黄色。

<?xml version="1.0" encoding="utf-8"?>

<android.support.constraint.ConstraintLayout

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

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

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

app:layout_behavior="@string/appbar_scrolling_view_behavior"

tools:context="com.crab.firstjniapplication.MainActivity"

tools:showIn="@layout/activity_main">

<LinearLayout

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:orientation="vertical"

app:layout_constraintBottom_toBottomOf="parent"

app:layout_constraintLeft_toLeftOf="parent"

app:layout_constraintRight_toRightOf="parent"

app:layout_constraintTop_toTopOf="parent"

>

<!-- 这里没有设置皮肤颜色,但是系统主题中有设置为黄色-->

<com.crab.firstjniapplication.PersonView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="xiechaojun"

/>

<com.crab.firstjniapplication.PersonView

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:text="xiechaojun1"

/>

</LinearLayout>

</android.support.constraint.ConstraintLayout>

 

4 解Bug

比如想让APP UI在不同Android版本上表现一样,可以自定Style,让APP的样式统一。比如想让所有的EditText的样式保持统一不随平台改变,可以下面这样实现:

<style name="Theme.Base" parent="@android:style/Theme.DeviceDefault.Light">

<item name="android:buttonStyle">@style/Button.Base</item>

<!-- 使用自定义样式 -->

<item name="android:editTextStyle">@style/CustomEditTextStyle</item>

</style>


<style name="CustomEditTextStyle" parent="@android:style/Widget.Material.EditText">

<!-- 自定义样式 -->

</style>

5 Android support包属性遇到的问题

当整个应用使用support包的Theme时,自己定义了一个AlertDialog,继承制android.app.AlertDialog,想给这个自定义的AlertDialog设置主题,使用的下面方法:

<!-- 使用的是support包的主题 -->
 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- 自定义的AlertDialog样式 --> 
    <item name="alertDialogTheme">@style/AppAlertDialogTheme</item>
 </style>
<style name="AppAlertDialogTheme" parent="ThemeOverlay.AppCompat.Dialog.Alert">
    <!-- 给AlertDialog的List项目添加一条分割线--> 
 <item name="listDividerAlertDialog">@drawable/preference_list_divider_material</item>
</style>

这样设置之后发现弹出的对话框包含ListView时并没有给ListView的每一个Item绘制一条下划线,那是什么原因呢?

我们来看下android.app.AlertDialog的源码:

    /**
     * Creates an alert dialog that uses the default alert dialog theme.
     * <p>
     * The default alert dialog theme is defined by
     * {@link android.R.attr#alertDialogTheme} within the parent
     * {@code context}'s theme.
     *
     * @param context the parent context
     * @see android.R.styleable#Theme_alertDialogTheme
     */
    protected AlertDialog(Context context) {
        this(context, 0);
    }

从源码可以看出alertDialogTheme是定义在属性android.R.attr.alertDialogTheme下的,也就是包名为android这个应用下的。而像我们自定义的alertDialogTheme:

    <!-- 自定义的AlertDialog样式 --> 
    <item name="alertDialogTheme">@style/AppAlertDialogTheme</item>

是在自己的包名.R.attr.alertDialogTheme下面的.知道原因了都很好改,可以使用下面两种方法:

1.在自定义的AlertDialog主题前都加上android,比如:

<!-- 使用的是support包的主题 -->
 <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <!-- 自定义的AlertDialog样式 --> 
    <item name="android:alertDialogTheme">@style/AppAlertDialogTheme</item>
 </style>
<style name="AppAlertDialogTheme" parent="ThemeOverlay.AppCompat.Dialog.Alert">
    <!-- 给AlertDialog的List项目添加一条分割线--> 
 <item name="android:listDividerAlertDialog">@drawable/preference_list_divider_material</item>
</style>

2.自定义的AlertDialog继承自Support包里面的AlertDialog.

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android Style 主要用于定义 Android 应用程序的外观和样式。它是一种基于 XML 的风格定义方式,可以用来定义应用程序中的布局、颜色、字体、尺寸等。Android Style 可以应用于整个应用程序、单个 Activity 或者某个视图。 Android Style 的定义通常存储在 res/values/styles.xml 文件中。在该文件中,可以使用 `<style>` 标签来定义样式,使用 `<item>` 标签来定义样式的具体属性。 以下是一个示例的 Android Style 定义: ```xml <!-- 定义一个基础样式 --> <style name="AppTheme" parent="Theme.AppCompat.Light"> <!-- 定义颜色 --> <item name="colorPrimary">@color/colorPrimary</item> <item name="colorPrimaryDark">@color/colorPrimaryDark</item> <item name="colorAccent">@color/colorAccent</item> <!-- 定义字体 --> <item name="android:typeface">monospace</item> <!-- 定义尺寸 --> <item name="android:textSize">16sp</item> </style> <!-- 定义一个特定页面的样式 --> <style name="MainActivityStyle" parent="AppTheme"> <!-- 添加特定属性 --> <item name="android:background">@drawable/background_main</item> </style> ``` 在布局文件或者代码中,可以通过使用 `android:theme` 属性来应用样式。例如: ```xml <!-- 在布局文件中应用样式 --> <Button android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Click Me" android:theme="@style/MainActivityStyle" /> ``` 通过使用 Android Style,可以统一定义应用程序的外观,提高开发效率,并且方便后续的修改和维护。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值