Android 自定义控件之第三讲:obtainStyledAttributes 系列函数详解

在项目中开发自定义控件时,或多或少都会用到 obtainStyledAttributes(AttributeSet, int[], int, int) 或者 obtainAttributes(AttributeSet, int[]) 函数,它们的主要作用是:根据传入的参数,返回一个对应的 TypedArray ,如果小伙伴还没有看过 LZ 的第二讲,那么请自行移步 Android 自定义控件之第二讲:TypedArray 详解,好了,就先扯到这里,下面开始今天内容讲解:

获取 TypedArray 对象 的函数一共四个:

1.public TypedArray obtainStyledAttributes (int[] attrs)

2.public TypedArray obtainStyledAttributes (int resid, int[] attrs)

3.public TypedArray obtainAttributes (AttributeSet set, int[] attrs)

4.public TypedArray obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)

讲解之前,需要说明一点:函数 1、2、4 都是 Resources.Theme 的函数,而 3 是 Resources 的函数。

接下来,我们针对这四个函数一 一进行讲解:

1. 解析前的准备

1. 在资源文件 values 下创建文件 attrs.xml,如下:

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

    <declare-styleable name="MyFirstCustomerView">
        <attr name="text" format="string" />
        <attr name="textColor" format="color"/>
        <attr name="textSize" format="dimension"/>
        </attr>
    </declare-styleable>

</resources>

2. 在资源文件 layout 下创建文件 activity_main.xml,如下:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:first_customer="http://schemas.android.com/apk/res/com.smart.customer_view_03_19"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="${packageName}.${activityClass}" >

    <com.smart.customer_view_03_19.customerview.MyFirstCustomerView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/small_padding"
        android:layout_centerInParent="true"
        first_customer:text="1234"
        first_customer:textColor="@color/green"
        first_customer:textSize="@dimen/x_large_font"
        />

</RelativeLayout>

2. 解析

1. obtainStyledAttributes (int[] attrs)

Google Developer 是这么解释这个函数的:

主要信息也就这么一句:

Return a TypedArray holding the values defined by Theme which are listed in attrs

它的大意是:返回一个与 attrs 中列举出的属性相关的数组,数组里面的值由 Theme 指定。从上面的概述中,我们可以知道:这个 Theme 就是关键。于是 LZ 各种试,皇天不负苦心人,谜底最终还是解开了:

attrs 对应的属性值必须定义在 Application 中 android:theme 对应的 style 下,也就是说:

我们在为 Application 设置主题的同时需要在对应的主题下为 attrs 设置相关的属性:

1. 清单文件

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/MyTheme" >
        <activity
            android:name="com.smart.customer_view_03_19.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

2. styles 文件

<!-- MyTheme -->
    <style name="MyTheme" parent="@android:style/Theme.Light.NoTitleBar">
        <item name="textColor">@color/red</item>
        <item name="text">1234</item>
        <item name="textSize">@dimen/x_large_font</item>
    </style>

3. 构造函数

TypedArray _TypedArray = mContext.getTheme().obtainStyledAttributes (R.styleable.MyFirstCustomerView);

应用程序运行效果如下:

what?!

这些个奇怪字符都是从哪里来的,其实 LZ 也不知道,但是打了个断点看了下,发现从 mText = _TypedArray.getString(R.styleable.MyFirstCustomerView_text); 获取数据的时候已经是这些奇怪字符了,出现问题了,当然有解决问题的办法,我们只需要在构造函数里面做如下操作:

if(!TextUtils.isEmpty(mText)){
            mText = "1234";
}

这样之后,就是我们想要的效果了:

对!就是这么个情况,搜了下貌似没有人遇到上面的问题,关键这个函数本来就很少有人用,所以等后面吧,也许未来的某一天,LZ 灵机一动,就知道这个问题的答案了,哈哈~,开玩笑~

不知道小伙伴有没有发现,我们在 Layout 布局文件中也为 attrs 对应的属性赋值了,其实简单的分析下就可以知道:无论我们有没有在 Layout 布局文件中为 attrs 对应属性赋值,这些值都不会起作用,因为这些值是从 Theme 中获取的,不相信的小伙伴可以自己试下,答案肯定和 LZ 分析的一样。

2. obtainStyledAttributes (int resid, int[] attrs)

Google Developer 是这么解释这个函数的:

细心的朋友一定会发现,其实这个函数的解释和上面的那个差不多。没错!格式几乎一样,因此我们只需要仿照上面的例子,找出这句话里面的关键字即可:

Return a TypedArray holding the values defined by the style resource resid which are listed in attrs

这句话的大意是:返回一个与 attrs 中列举出的属性相关的数组,数组里面的值由 样式属性resid 指定。其实这句话已经很明确了,因此我们就不做过多的赘述,直接上 Demo:

1. 清单文件

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.smart.customer_view_03_19.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

2. styles 文件

<style name="MyFirstCustomerView" >
        <item name="textColor">@color/blue</item>
        <item name="text">1234</item>
        <item name="textSize">@dimen/x_large_font</item>
    </style>

3. 构造函数

TypedArray _TypedArray = mContext.getTheme().obtainStyledAttributes (R.style.MyFirstCustomerView,R.styleable.MyFirstCustomerView);

应用程序运行效果如下:

对,就是这么简单!

同上个函数一样,因为 attrs 的属性都是 style 文件中获取的,因此无论是否在 Layout 布局文件中为 attrs 添加属性,这些值都不会起作用。

3. obtainAttributes (AttributeSet set, int[] attrs)

Google Developer 是这么解释这个函数的:

相信在自定义控件的时候,应该有好多小伙伴和一样喜欢用这个函数吧。

同上面两个函数一样,也是一句话:

Retrieve a set of basic attribute values from an AttributeSet, not performing styling of them using a theme and/or style resources.

这句话的大意是:从 AttributeSet 中获取 attrs 对应的属性值,不为这些属性值设置样式。

考虑到很多小伙伴经常使用这个函数,因此,直接上 Demo:

1. 清单文件

<application
        android:allowBackup="true"
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name"
        android:theme="@style/AppTheme" >
        <activity
            android:name="com.smart.customer_view_03_19.MainActivity"
            android:label="@string/app_name" >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>

2. 构造函数

TypedArray _TypedArray = mContext.getResources().obtainAttributes (attrs,R.styleable.MyFirstCustomerView)

不知道小伙伴还记不记得 LZ 在上面说过这个函数的特殊性—— Resources 里面的函数。

应用程序运行效果如下:

同上面的两个函数不同,在这个函数里面的有个特别的参数 AttributeSet,它对应着 attrs 里面的属性,AttributeSet 对象中的数据是从 XML 布局文件中读取出来的,因此此时如果我们不在布局文件中为 attrs 相关属性设置值,那么就会报错!报错!!报错!!!切记,小伙伴~

4. obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)

Google Developer 是这么解释这个函数的:

相信很多人看完这么多的注释之后都蒙圈了,LZ 也不例外,第一次看的时候,稀里糊涂的就看完了,看完之后,发现什么也没懂~就是知道怎么用,但是具体里面的细节,一问三不知。终于今天静下心好好研究了下,把之前所有的迷惑都解开了,接下来就让我将这个函数讲个一清二楚吧,哈哈~

上面的 Google 开发者文档的大意是:

返回一个与 attrs 属性相对应的数组。另外,如果在 AttributeSet 中为 attrs 指定了样式属性,那么这个样式属性就会应用在这些属性上。

attribute 最终由下面四个因素决定:

  1. 在 AttributeSet 中定义的属性(Any attribute values in the given AttributeSet);
  2. AttributeSet 指定的样式资源文件(The style resource specified in the AttributeSet (named “style”));
  3. 由 defStyleAttr 和 defStyleRes 指定的样式资源文件(The default style specified by defStyleAttr and defStyleRes);
  4. 主题中的默认值(The base values in this theme)。

上面四种元素的优先级是从上到下排序的,也就是说:如果在 AttributeSet 中定义了某个属性的值,那么无论后面的样式属性如何定义,它的值都不会改变。

接下来我们分别解释下,函数中各参数的含义:

  1. AttributeSet set :XML 中定义的属性值,可能为 null;
  2. int[] attrs :目标属性值;
  3. int defStyleAttr :在当前主题中有一个引用指向样式文件,这个样式文件将 TypedArray 设置默认值。如果此参数为 0 则表示不进行默认值设置。

4.int defStyleRes :

好了,接下来就开始这部分的讲解吧:

1. 清单文件

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

    <declare-styleable name="MyFirstCustomerView">
        <attr name="text" format="string" />
        <attr name="textColor" format="color"/>
        <attr name="textSize" format="dimension"/>
        <!-- defStyleAttr 对应-->
        <attr name="myCustomerStyle" format="reference"></attr>
    </declare-styleable>

</resources>
时间: 2024-12-25 14:23:09

Android 自定义控件之第三讲:obtainStyledAttributes 系列函数详解的相关文章

PHP输出缓存ob系列函数详解

ob,输出缓冲区,是output buffering的简称,而不是output cache.ob用对了,是能对速度有一定的帮助,但是盲目的加上ob函数,只会增加CPU额外的负担 ob的基本原则:如果ob缓存打开,则echo的数据首先放在ob缓存.如果是header信息,直接放在程序缓存.当页面执行到最后,会把ob缓存的数据放到程序缓存,然后依次返回给浏览器.下面我说说ob的基本作用:  1)防止在浏览器有输出之后再使用setcookie().header()或session_start()等发送

PHP ob系列函数详解

一. 相关函数简介:    1.Flush:刷新缓冲区的内容,输出.    函数格式:flush()    说明:这个函数经常使用,效率很高.    2.ob_start :打开输出缓冲区    函数格式:void ob_start(void)    说明:当缓冲区激活时,所有来自PHP程序的非文件头信息均不会发送,而是保存在内部缓冲区.   为了输出缓冲区的内容,可以使用ob_end_flush()或flush()输出缓冲区的内容.    3 .ob_get_contents :返回内部缓冲区

对文件操作系列函数详解

<span style="font-size:18px;"> #include <string.h> #include<stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> int main(int argc,char **argv) { FILE *fp; struct stat filestat; char buf[1

Android总结篇系列:Activity中几个主要函数详解

专注Android领域开发. 仰望星空,同时需要脚踏实地. ——好记性不如烂博客 Android总结篇系列:Activity中几个主要函数详解 Activity作为Android系统中四大基本组件之一,包含大量的与其他的各大组件.intent.widget以及系统各项服务等之间的交互的函数.在此,本文主要选取实际项目开发中常用的,但完全理解又需要有一定深入了解的几个函数进行讲解,后续本文会根据需要不断更新. 1. startActivityForResult / onActivityResult

C++ list容器系列功能函数详解

C++ list函数详解 首先说下eclipse工具下怎样debug:方法:你先要设置好断点,然后以Debug方式启动你的应用程序,不要用run的方式,当程序运行到你的断点位置时就会停住,也会提示你进入到Debug视图方式操作,F5是进入到函数或语句块的内部,F6是单步运行,一行一行的走,F7可以跳当前监听函数或语句块F8 会直接跳到下个断点. 下面进入主题: 一.构造.析构函数.= 运算符 1.功能:声明list容器.4种方式 list<int> first;                

Android Touch系统简介(二):实例详解onInterceptTouchEvent与onTouchEvent的调用过程

上一篇文章主要讲述了Android的TouchEvent的分发过程,其中有两个重要的函数:onInterceptTouchEvent和onTouchEvent,这两个函数可被重装以完成特定的逻辑.onInterceptTouchEvent的定义为于ViewGroup中,默认返回值为false,表示不拦截TouchEvent.onTouchEvent的定义位于View中,当ViewGroup要调用onTouchEvent时,会利用super.onTouchEvent.ViewGroup调用onTo

Android中内容观察者的使用---- ContentObserver类详解

  转载请注明出处:http://blog.csdn.net/qinjuning 前言: 工作中,需要开启一个线程大量的查询某个数据库值发送了变化,导致的开销很大,后来在老大的指点下,利用了 ContentObserver完美的解决了该问题,感到很兴奋,做完之后自己也对ContentObserver做下总结. ContentObserver——内容观察者,目的是观察(捕捉)特定Uri引起的数据库的变化,继而做一些相应的处理,它类似于 数据库技术中的触发器(Trigger),当ContentObs

可变参数函数详解

可变参数函数又称参数个数可变函数(本文也简称变参函数),即函数参数数目可变.原型声明格式为: type VarArgFunc(type FixedArg1, type FixedArg2, -); 其中,参数可分为两部分:数目确定的固定参数和数目可变的可选参数.函数至少需要一个固定参数,其声明与普通函数参数相同:可选参数由于数目不定(0个或以上),声明时用"-"表示("-"用作参数占位符).固定参数和可选参数共同构成可变参数函数的参数列表. 由于参数数目不定,使用可

Android 最火的快速开发框架AndroidAnnotations使用详解

Android 最火的快速开发框架androidannotations配置详解文章中有eclipse配置步骤,Android 最火快速开发框架AndroidAnnotations简介文章中的简单介绍,本篇注重讲解AndroidAnnotations中注解方法的使用. @EActivity 示例: @EActivity(R.layout.main) public class MyActivity extends Activity { } @fragment 示例: @EFragment(R.lay