天天看点

Android中自定义样式与View的构造函数中的第三个参数defStyle的意义零、序 一、自定义Style 二、在xml中为相应的属性声明属性值 三、在运行时获取属性值  结论

  系统自带的view可以在xml中配置属性,对于写的好的custom view同样可以在xml中配置属性,为了使自定义的view的属性可以在xml中配置,需要以下4个步骤:

通过<declare-styleable>为自定义view添加属性

在xml中为相应的属性声明属性值

在运行时(一般为构造函数)获取属性值

将获取到的属性值应用到view

  怎么将获取到的属性值应用到view就不用说了,自己定义的属性什么用处自己肯定是清楚的,所以接下来看一下前三点。

  通过<declare-styleable>元素声明custom view需要的属性即可,下面是一个例子,文件是res/values/attrs.xml

  在上述xml中,我们声明了customize与customizesyle,customize包含了attr_one、attr_two、attr_three与attr_four四个attribute,customizestyle也是一个attribute,但是却没有声明在declare-styleable中。

  定义在declare-styleable中与直接用attr定义没有实质的不同,上述xml中,无论attr_one - attr_four是否声明在declare-styleable中,系统都会为我们在r.attr中生成5个attribute

  不同的是,如果声明在declare-styleable中,系统还会为我们在r.styleable中生成相关的属性。

   如上所示,r.styleable.customize是一个int[],而里面的元素的值正好和r.attr.attr_one - r.attr.attr_four一一对应,而r.styleable.customize_attr_one等4个值就是r.attr.attr_one-r.attr.attr_four在r.styleable.customize数组中的索引。这个数组和索引在第三步运行时获得属性值时会用到,将attr分组声明在declare-styleabe中的作用就是系统会自动为我们生成这些东西,如果不声明在declare-styleable中,我们完全可以在需要的时候自己构建这个数组,由于数组是自己构建的,每个属性的下标索引也就很清楚了,只是比较麻烦。以上一家之言,不过从使用及实验难上看确实是这样的。

        我们知道,在xml中为属性赋值有几种不同的方式

直接在layout中使用属性

设置style并在style中设置属性

application和activity可以指定theme,可以在theme中指定在当前application或activity中属性的默认值

        下面就分别看一下这三种方式

        在xml layout中使用自定义属性和使用系统属性差不多,不过属性所属的namespace不同,比如像下面这样。

  像layout_width等属性属于android的namespace,自定义的属性属于当前程序的namespace,只需像声明android的namespace一样声明当前程序的namespace就好,只需要把上面红色部分的android换成当前程序的包名。应用属性的时候也需要注意属性的namespace。

  看上面xml中绿色的那一行,我们为customtextview声明了一个style:throughstyle,这个style很简单,只是指定了两个属性的值

  注意,在style中我们声明了attr_one的值,同时在xml中也直接向attr_one赋了值,最终用哪一个有个优先级的问题,后面在第三节:在运行时获取属性值中再说,接下来看下第三种方式。

   在上述xml中,我们在apptheme中为attr_one、attr_two与attr_three指定了值,但是同时也为customizestyle指定了一个reference,而在这个reference中为attr_one、attr_two与attr_three指定了值,customizestyle就是我们在attrs.xml中定义的一个属性。在theme中为属性设置值的方式有两这种,直接在theme中定义或通过另一个attribute引用一个style,这两种方式还是有区别的,在下面的获取属性值一节中可以看到。

        在styles.xml中,我们同时定义一个defaultcustomizestyle的style,它的作用等下可以看到。

  先看下customtextview的代码

   主要代码都在第三个函数中,这里也没做什么,只是通过context的obtainstyledattributes获得了前面定义的4个属性的值并打印了出来。

  如果在code中实例化一个view会调用第一个构造函数,如果在xml中定义会调用第二个构造函数,而第三个函数系统是不调用的,要由view(我们自定义的或系统预定义的view,如此处的customtextview和button)显式调用,比如在这里我们在第二个构造函数中调用了第三个构造函数,并将r.attr.customizestyle传给了第三个参数。

  第三个参数的意义就如同它的名字所说的,是默认的style,只是这里没有说清楚,这里的默认的style是指它在当前application或activity所用的theme中的默认style,以系统中的button为例说明。

   在code中实例化view会调用第一个构造函数,在xml中定义会调用第二个构造函数,在button的实现中都调用了第三个构造函数,并且defstyle的值是com.android.internal.r.attr.buttonstyle。buttonstyle是系统中定义的一个attribute,系统默认有一个theme,比如4.0中是theme.holo

  上面是系统默认的theme,为buttonstyle指定了一个style

   这个style定义了系统中button的默认属性,如background等。

  从文档中第三个构造函数的说明中也可以看到,这个构造函数的作用是view的子类提供这个类的基础样式

view(context context, attributeset attrs, int defstyleattr) perform inflation from xml and apply a class-specific base style.

  上面说的都比较抽象,还是直接看实例代码来的清楚明白,实验用的代码上面全都贴完了,这里直接看结果,但在这之前要先看一个函数:obtainstyledatributes。

  我们要获取的属性值都是通过这个函数返回的typedarray获得的,这是官方说明:obtainstyledattributes,以下是函数原型:

  4个参数的意思分别是:

    set:属性值的集合

    attrs:我们要获取的属性的资源id的一个数组,如同contextprovider中请求数据库时的projection数组,就是从一堆属性中我们希望查询什么属性的值

    defstyleattr:这个是当前theme中的一个attribute,是指向style的一个引用,当在layout xml中和style中都没有为view指定属性时,会从theme中这个attribute指向的style中查找相应的属性值,这就是defstyle的意思,如果没有指定属性值,就用这个值,所以是默认值,但这个attribute要在theme中指定,且是指向一个style的引用,如果这个参数传入0表示不向theme中搜索默认值

    defstyleres:这个也是指向一个style的资源id,但是仅在defstyleattr为0或defstyleattr不为0但theme中没有为defstyleattr属性赋值时起作用

  链接中对这个函数说明勉强过得去,这里简要概括一下。对于一个属性可以在多个地方指定它的值,如xml直接定义,style,theme,而这些位置定义的值有一个优先级,按优先级从高到低依次是:

直接在xml中定义>style定义>由defstyleattr和defstyleres指定的默认值>直接在theme中指定的值

  看这个关系式就比较明白了,defstyleattr和defstyleres在前面的参数说明中已经说了,“直接在theme中指定的值”的意思在下面的示代码中可以看到。

  相关的代码都前面都已经贴上了,不过为了方便查看,这里再把相关的xml一起贴一遍

   在code中是这样获取属性的

  customizestyle是定义的一个attribute,defaultcustomizestyle是定义的一个style。

  从代码中可以看到,r.attr.customizestyle就是前面提到的defstyleattr,r.style.defaultcustomizestyle就是defstyleres。

  在styles.xml中我们为app定义了一个theme:apptheme,在这个theme中直接为attr_one、attr_two、attr_three定义了属性值,这个就是前面关系式中说的直接在theme中指定的值。

  在apptheme中同时定义了customizestyle,引用了一个style,在customtextview的构造函数中分别打印了attr_one、attr_two、attr_three、attr_four的值,所以下面我们就可以通过log输出验证一下前面的关系式,如果一个attribute在多个位置都定义了值,究竟哪一个起作用。

  

Android中自定义样式与View的构造函数中的第三个参数defStyle的意义零、序 一、自定义Style 二、在xml中为相应的属性声明属性值 三、在运行时获取属性值  结论

  如上图所示:

attr_one同时在xml、style、defstyleattr、defstyleres与theme中直接定义了值,但起作用的是在xml中的定义

attr_two同时在style、defstyleattr、defstyleres与theme中直接定义了值,但起用的是在style中的定义

attr_three同时在defstyleattr、defstyleres与theme中直接定义了值,但起作用的是defstyleattr

attr_four在defstyleres中定义了,但结果仍是0。

  这可以说明:

1. 直接在xml中定义>style定义>由defstyleattr定义的值>defstyleres指定的默认值、直接在theme中指定的值 2. defstyleattr(即defstyle)不为0且在当前theme中可以找到这个attribute的定义时,defstyleres不起作用,所以attr_four虽然在defstyleres(defaultcustomizestyle)中定义了,但取到的值仍为null。

  为了看一下defstyleres的作用,1. 我们在apptheme中加上attr_four的定义,2. 向obtainstyledattributes的第三个参数传入0,或者移除apptheme中customizestyle的定义,结果是一样的

   由于defstyleattr为0,或者defstyleattr不为0但是我们没有为这个属性赋值,所以defstyleres起作用,当一个属性没有在xml和style中赋值时,系统会在defstyleres(此处为defaultcustomizestyle)查找属性的值。

Android中自定义样式与View的构造函数中的第三个参数defStyle的意义零、序 一、自定义Style 二、在xml中为相应的属性声明属性值 三、在运行时获取属性值  结论

  我们看到attr_three的值来自defstyleres,而attr_four的值来自theme中的直接定义(defaultcustomizestyle定义了attr_one、atrr_two、attr_three) ,这就说明

1. defstyleatrtr即defstyle为0或theme中没有定义defstyle时defstyleres才起作用 2. defstyleres>在theme中直接定义

   从前面的说明可以得到以下结论:

要为自定义view自定义属性,可以通过declare-styleable声明需要的属性

为了在theme设置view的默认样式,可以同时定义一个format为reference的属性att_a,在定义theme时为这个attribute指定一个style,并且在view的第二个构造函数中将r.attr.attr_a 作为第三个参数调用第三个构造函数

可以定义一个style作为theme中没有定义attr_a时view属性的默认值。

可以在theme中直接为属性赋值,但优先级最低

当defstyleattr(即view的构造函数的第三个参数)不为0且在theme中有为这个attr赋值时,defstyleres(通过obtainstyledattributes的第四个参数指定)不起作用

属性值定义的优先级:xml>style>theme中的默认sytle>默认style(通过obtainstyledattributes的第四个参数指定)>在theme中直接指定属性值

  代码下载:customstyle.zip