Android开发经验谈Android技术知识Android开发

Android | 自定义属性

2019-08-01  本文已影响126人  彭旭锐

前言

自定义属性 思维导图 属性分类 示意图

Manifest属性是可以在AndroidManifest.xml中使用的属性,定义在原生框架中(比如Android Pattrs_manifests.xml)。本文的重点是其他两种属性:控件属性与主题属性


在正文开始之前,先来看看几个小题目,这些问题你都能解释清楚吗?

稍后会为大家一一解答,有任何疑问都可以在评论区留言讨论!


1. 定义

如何定义一个属性呢?定义属性需要用到:

我们就以Android P原生框架提供的attrs.xml为例,里面定义了非常多的属性,我们选取一些熟知的属性如下:

<!-- 文本颜色 -->
<attr name="textColor" format="reference|color"/>
<!-- 高亮文本颜色 -->
<attr name="textColorHighlight" format="reference|color" />
<!-- 高亮文本颜色 -->
<attr name="textColorHint" format="reference|color" />

你也可以在SDK中找到这个文件,有两种方式:

  • 文件夹:sdk/platform/android-28/data/res/values/attrs.xml

  • Android Studio(切换到project视图):External Libraries/<Android API 28 Platform>/res/values/attrs.xml

提示:你在这里看到的版本号是在app/build.gradle中的compileSdkVersion设置的

这些都是我们再熟悉不过的属性了,比如我们用它们来定义EditText

<EditText
        ...
        android:textColor="@android:color/white"
        android:textColorHighlight="@color/colorAccent"
        android:textColorHint="@android:color/darker_gray"/>

2. 命名空间

为什么我们定义属性的时候没有指明android:前缀,而引用属性的时候却需要呢?这就引出了属性命名空间的概念,有四种命名空间:

命名空间 示意图

注意到了吗,无论是原生、AppCompat兼容库还是自定义的属性,定义的时候都不需要命名空间,只有在引用的时候需要,命名空间用于区分属性定义的位置,比如:

<resources>
    <style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
            <!-- AppCompat兼容库定义的属性 -->
        <item name="colorAccent">@color/colorAccent</item>
        <!-- 原生框架定义的属性 -->
        <item name="android:colorAccent">@android:color/white</item>
    </style>
</resources>

上面两个键值对,一个将AppCompat兼容库的colorAccent属性的值设置为@color/colorAccent,另一个将原生框架的android:colorAccent属性的值设置为@android:color/white,命名空间帮助在区分具体指示的是哪个属性。

事实上,在使用兼容库的应用中,只有第一个键值对能产生有实际效果


3. 控件属性

控件属性是最常用的属性了,后面提到的样式属性也需要以控件属性为前提,控件属性有以下特点:

我们以TextView的控件属性为例,定义在attrs.xml中:

// 定义
<attr name="textColor" format="reference|color"/>

// 引用
<declare-styleable name="TextView">
        // 同时定义和引用属性
        <attr name="text" format="string" localization="suggested" />
        <attr name="hint" format="string" />
        // 引用属性
        <attr name="textColor" />
        // ...
</declare-styleable>

属性定义和属性引用可以在一步完成,比如上面的text属性与hint属性

可以看到,name指明了对应的控件为TextView,因此我们可以在布局文件中将这些属性设置到TextView上:

<TextView
        ...
        android:textColor="@android:color/white"
        android:text="Hello World!"/>

那么问题来了,这些设置的属性值最终是怎么体现在视图上的呢?我们需要到TextView的源码中寻找答案:

public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        // ...
        final Resources.Theme theme = context.getTheme();
        TypedArray a = theme.obtainStyledAttributes(
                    attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
    for (int i = 0; i < n; i++) {
                int attr = a.getIndex(i);
                switch (attr) {      
                        // ...
                        // 读取到text属性的属性值
                        case com.android.internal.R.styleable.TextView_text:
                    textIsSetFromXml = true;
                    // 属性值是一个String类型的资源ID
                    mTextId = a.getResourceId(attr, ResourceId.ID_NULL);
                    // 从资源ID取得String
                    text = a.getText(attr);
                    break;
                        // ...
        }
    }
        // ...
}

总结一下,布局属性可以在布局文件中使用,代码中使用TypedArray读取属性值,最终作用在控件上。

控件属性 示意图

4. 样式属性

样式属性是另外一种用途广泛的属性,与控件属性结合使用可以打造主题化应用,巩固项目UI的可维护性。样式属性具有以下特点:

我们以AppCompat兼容库中定义的一个主题为例:

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

<style name="Base.V7.Theme.AppCompat" parent="Platform.AppCompat">
        // ...
        // 样式属性1
        <item name="colorAccent">@color/accent_material_dark</item>
        // ...
        // 样式属性2
        <item name="colorControlActivated">?attr/colorAccent</item>
        // ...
</style>

样式属性可以被子样式覆盖,比如:

<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
        <!-- Customize your theme here. -->
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
</style>

可以看到,这是我们熟知的主题样式定制,我们修改了colorAccent属性的值,设置为自定义的@color/colorAccent,结果是所有引用了colorAccent属性的地方都被修改。

注意,样式属性在布局文件中是不生效的,比如以下布局并不会取得预期效果:

<TextView
        ...
        android:colorAccent="@android:color/white"/>

提示:一个属性可以同时被引用为控件属性和样式属性,比如textSize;也有的属性只是作为样式属性,比如colorAccent

样式属性 示意图

5. 属性值类型

前文提到,定义属性需要指定:属性名属性值类型,属性值类型可以分为资源类与特殊类

属性值类型 描述 TypedArray
fraction 百分数 getFraction(...)
float 浮点数 getFloat(...)
boolean 布尔值 getBoolean(...)
color 颜色值 getColor(...)
string 字符串 getString(...)
dimension 尺寸值 getDimensionPixelOffset(…)
getDimensionPixelSize(...)
getDimension(...)
integer 整数值 getInt(...)
getInteger(...)
属性值类型 描述 TypedArray
flag 标志位 getInt(...)
enum 枚举值 getInt(…)等
reference 资源引用 getDrawable(...)等

6. 总结


推荐阅读


参考


感谢喜欢!你的点赞是对我最大的鼓励!欢迎关注彭旭锐的简书!

上一篇 下一篇

猜你喜欢

热点阅读