Android 深入理解Android中的自定义属性

上:

1、引言

对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现:

  1. 自定义一个CustomView(extends View )类
  2. 编写values/attrs.xml,在其中编写styleable和item等标签元素
  3. 在布局文件中CustomView使用自定义的属性(注意namespace)
  4. 在CustomView的构造方法中通过TypedArray获取

ps:如果你对上述几个步骤不熟悉,建议先熟悉下,再继续~

那么,我有几个问题:

  • 以上步骤是如何奏效的?
  • styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?
  • 如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?
  • 构造方法中的有个参数叫做AttributeSet 
    (eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的数组,那么我能不能通过它去获取我的自定义属性呢?
  • TypedArray是什么鬼?从哪冒出来的,就要我去使用?

恩,针对这几个问题,大家可以考虑下,如何回答呢?还是说:老子会背上述4个步骤就够了~~

2、常见的例子

接下来通过例子来回答上述问题,问题的回答顺序不定~~大家先看一个常见的例子,即上述几个步骤的代码化。

  • 自定义属性的声明文件
  •   <?xml version="1.0" encoding="utf-8"?>
    <resources>
    
        <declare-styleable name="test">
            <attr name="text" format="string" />
            <attr name="testAttr" format="integer" />
        </declare-styleable>
    
    </resources>


  • 自定义View类
  • package com.example.test;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;
    
    public class MyTextView extends View {
    
        private static final String TAG = MyTextView.class.getSimpleName();
    
        public MyTextView(Context context, AttributeSet attrs) {
            super(context, attrs);
    
            TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);
    
            String text = ta.getString(R.styleable.test_testAttr);
            int textAttr = ta.getInteger(R.styleable.test_text, -1);
    
            Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);
    
            ta.recycle();
        }
    
    }


  • 布局文件中使用
  • <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:zhy="http://schemas.android.com/apk/res/com.example.test"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >
    
        <com.example.test.MyTextView
            android:layout_width="100dp"
            android:layout_height="200dp"
            zhy:testAttr="520"
            zhy:text="helloworld" />
    
    </RelativeLayout>


  • ok,大家花3s扫一下,运行结果为:
    MyTextView: text = helloworld , textAttr = 520
  • 应该都不意外吧,注意下,我的styleable的name写的是test,所以说这里并不要求一定是自定义View的名字。

3、AttributeSet与TypedArray

下面考虑:

构造方法中的有个参数叫做AttributeSet(eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的集合,那么我能不能通过它去获取我的自定义属性呢?

首先AttributeSet中的确保存的是该View声明的所有的属性,并且外面的确可以通过它去获取(自定义的)属性,怎么做呢? 
其实看下AttributeSet的方法就明白了,下面看代码。

public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        int count = attrs.getAttributeCount();
        for (int i = 0; i < count; i++) {
            String attrName = attrs.getAttributeName(i);
            String attrVal = attrs.getAttributeValue(i);
            Log.e(TAG, "attrName = " + attrName + " , attrVal = " + attrVal);
        }

        // ==>use typedarray ...

    }


输出:

MyTextView(4136): attrName = layout_width , attrVal = 100.0dip
MyTextView(4136): attrName = layout_height , attrVal = 200.0dip
MyTextView(4136): attrName = text , attrVal = helloworld
MyTextView(4136): attrName = testAttr , attrVal = 520

结合上面的布局文件,你发现了什么? 

我擦,果然很神奇,真的获得所有的属性,恩,没错,通过AttributeSet可以获得布局文件中定义的所有属性的key和value(还有一些方法,自己去尝试),那么是不是说TypedArray这个鬼可以抛弃了呢?答案是:NO!

现在关注下一个问题:

TypedArray是什么鬼?从哪冒出来的,就要我去使用?

我们简单修改下,布局文件中的MyTextView的属性。

<com.example.test.MyTextView
        android:layout_width="@dimen/dp100"
        android:layout_height="@dimen/dp200"
        zhy:testAttr="520"
        zhy:text="@string/hello_world" />


现在再次运行的结果是:

MyTextView(4692): attrName = layout_width , attrVal = @2131165234
MyTextView(4692): attrName = layout_height , attrVal = @2131165235
MyTextView(4692): attrName = text , attrVal = @2131361809
MyTextView(4692): attrName = testAttr , attrVal = 520
>>use typedarray
MyTextView(4692): text = Hello world! , textAttr = 520

发现了什么?通过 AttributeSet获取的值,如果是引用都变成了@+数字的字符串。你说,这玩意你能看懂么?那么你看看最后一行使用TypedArray获取的值,是不是瞬间明白了什么。

TypedArray其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。

贴一下:如果通过AttributeSet获取最终的像素值的过程:

<span style="color: rgb(85, 85, 85); font-family: 'microsoft yahei'; line-height: 35px; background-color: rgb(255, 255, 255);"></span><pre name="code" class="java">int widthDimensionId =  attrs.getAttributeResourceValue(0, -1);
        Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));

ok,现在别人问你TypedArray存在的意义,你就可以告诉他了。
 

4、declare-styleable

我们已经解决了两个问题,接下来,我们看看布局文件,我们有一个属性叫做:zhy:text。 
总所周知,系统提供了一个属性叫做:android:text,那么我觉得直接使用android:text更nice,这样的话,考虑问题:

如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?

答案是可以的,怎么做呢? 
直接在attrs.xml中使用android:text属性。

  <declare-styleable name="test">
        <attr name="android:text" />
        <attr name="testAttr" format="integer" />
    </declare-styleable>

注意,这里我们是使用已经定义好的属性,不需要去添加 format属性(注意声明和使用的区别,差别就是有没有format)。 

然后在类中这么获取:ta.getString(R.styleable.test_android_text);布局文件中直接android:text="@string/hello_world"即可。

这里提一下,系统中定义的属性,其实和我们自定义属性的方式类似,你可以在sdk/platforms/android-xx/data/res/values该目录下看到系统中定义的属性。然后你可以在系统提供的View(eg:TextView)的构造方法中发现TypedArray获取属性的代码(自己去看一下)。

ok,接下来,我在想,既然declare-styleable这个标签的name都能随便写,这么随意的话,那么考虑问题:

styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?

其实的确是可以不写的,怎么做呢?

  • 首先删除declare-styleable的标签

那么现在的attrs.xml为:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="testAttr" format="integer" />
</resources>

哟西,so清爽~ 

* MyTextView实现

package com.example.test;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyTextView extends View {

    private static final String TAG = MyTextView.class.getSimpleName();

    private static final int[] mAttr = { android.R.attr.text, R.attr.testAttr };
    private static final int ATTR_ANDROID_TEXT = 0;
    private static final int ATTR_TESTATTR = 1;

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        // ==>use typedarray
        TypedArray ta = context.obtainStyledAttributes(attrs, mAttr);

        String text = ta.getString(ATTR_ANDROID_TEXT);
        int textAttr = ta.getInteger(ATTR_TESTATTR, -1);
        //输出 text = Hello world! , textAttr = 520
        Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);

        ta.recycle();
    }

}

貌似多了些代码,可以看到我们声明了一个int数组,数组中的元素就是我们想要获取的attr的id。并且我们根据元素的在数组中的位置,定义了一些整形的常量代表其下标,然后通过TypedArray进行获取。 

可以看到,我们原本的:

R.styleable.test => mAttr
R.styleable.test_text => ATTR_ANDROID_TEXT(0)
R.styleable.test_testAttr => ATTR_TESTATTR(1)

那么其实呢?android在其内部也会这么做,按照传统的写法,它会在R.java生成如下代码:

public static final class attr {
    public static final int testAttr=0x7f0100a9;
    }
public static final class styleable {
     public static final int test_android_text = 0;
     public static final int test_testAttr = 1;
      public static final int[] test = {
            0x0101014f, 0x7f0100a9
        };
    }

ok,根据上述你应该发现了什么。styleale的出现系统可以为我们完成很多常量(int[]数组,下标常量)等的编写,简化我们的开发工作(想想如果一堆属性,自己编写常量,你得写成什么样的代码)。那么大家肯定还知道 declare-styleable的name属性,一般情况下写的都是我们自定义View的类名。主要为了直观的表达,该 declare-styleable的属性,都是改View所用的。

其实了解该原理是有用的,详见:Android 自定义控件 优雅实现元素间的分割线

ok,现在5个问题,回答了4个,第一个问题:

自定义属性的几个步骤是如何奏效的?

恩,上述以及基本涵盖了这个问题的答案,大家自己总结,所以:略。

总结下今天的博客。

  • attrs.xml里面的declare-styleable以及item,android会根据其在R.java中生成一些常量方便我们使用(aapt干的),本质上,我们可以不声明declare-styleable仅仅声明所需的属性即可。
  • 我们在View的构造方法中,可以通过AttributeSet去获得自定义属性的值,但是比较麻烦,而TypedArray可以很方便的便于我们去获取。
  • 我们在自定义View的时候,可以使用系统已经定义的属性。

下:

相信每一位从事Android开发的猿都遇到过需要自己去自定义View的需求,如果想通过xml指定一些我们自己需要的参数,就需要自己声明一个styleable,并在里面自己定义一些attr属性,这个过程相信大家都比较了解。当然,属性其实也不一定需要和View配合使用,比如我想通过一个Theme中的style对一个库进行一些简单参数的配置,这应该怎么做呢?我今天在封装一个库时在这个地方浪费了较多时间,最后没办法,到处搜搜资料,记录在这里吧,相信对大家都有帮助。

attr和styleable的关系

首先要明确一点,attr不依赖于styleable,styleable只是为了方便attr的使用
我们自己定义属性完全可以不放到styleable里面,比如直接在resources文件中定义一些属性:

<attr name="custom_attr1" format="string" />
<attr name="custom_attr2" format="string" />

定义一个attr就会在R文件里面生成一个Id,那么我们去获取这个属性时,必须调用如下代码:

int[] custom_attrs = {R.attr.custom_attr1,R.custom_attr2};
TypedArray typedArray = context.obtainStyledAttributes(set,custom_attrs);

而通过定义一个styleable,我们可以在R文件里自动生成一个int[],数组里面的int就是定义在styleable里面的attr的id。所以我们在获取属性的时候就可以直接使用styleable数组来获取一系列的属性。

  <declare-styleable name="custom_attrs">   
          <attr name="custom_attr1" format="string" />
          <attr name="custom_attr2" format="string" />
  </declare-styleable>

获取:

  TypedArray typedArray = context.obtainStyledAttributes(set,R.styleable.custom_attrs);

由上面的例子可以知道,定义一个declare-styleable,在获取属性的时候为我们自动提供了一个属性数组。此外,我觉得使用declare-styleable的方式有利于我们我们把相关的属性组织起来,有一个分组的概念,属性的使用范围更加明确。

obtainStyledAttributes函数获取属性

其实我们在前面已经使用了obtainStyledAttributes来获取属性了,现在来看看这个函数的声明吧:

  • obtainAttributes(AttributeSet set, int[] attrs) //从layout设置的属性集中获取attrs中的属性
  • obtainStyledAttributes(int[] attrs) //从系统主题中获取attrs中的属性
  • obtainStyledAttributes(int resId,int[] attrs) //从资源文件定义的style中读取属性
  • obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)
    //这是最复杂的一种情况,后面细说。

这么多重载的方法是不是已经看懵了?其实你只需要理解其中的参数就能掌握各个方法的使用方法。所谓获取属性,无非就是需要两个参数:第一,我需要获取那些属性;第二:我从哪里去获取这些属性(数据源)。

attrs

attrs:int[],每个方法中都有的参数,就是告诉系统需要获取那些属性的值。

set

set:表示从layout文件中直接为这个View添加的属性的集合,如:android:layout_width="match_parent"。注意,这里面的属性必然是通过xml配置添加的,也就是由LayoutInflater加载进来的布局或者View`才有这个属性集。

现在你知道为啥我们在自己定义View的时候至少要重写(Context context, AttributeSet set)构造器了吧?因为不重写时,我们将无法获取到layout中配置的属性!!当然,也因为这样,LayoutInflater在inflater布局时会通过反射去调用View的(Context context, AttributeSet attrs)构造器。
set 中实际上又有两种数据来源,当然最后都会包含在set中。一种是直接使用android:layout_width="wrap_content"这种直接指定的,还有一种是通过style="@style/somestyle"这样指定的。

defStyleAttr

这个参数是本文的关键所在,也是自定义一个可以在Theme中配置的样式的关键,先看个栗子吧:
如果我想通过在系统主题里面设置一个样式,修改所有textview的样式,你一般会这么做:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    //在主题中设置textviewstyle
    <item name="android:textViewStyle">@style/textviewstyle</item>
</style>

<style name="textviewstyle" parent="android:style/Widget.TextView">
    <!--指定一些属性-->
</style>

首先android:textViewStyle其实就是一个普通的在资源文件中定义的属性attr,它的format="reference"。那问题来了,TextView是怎么得知我们自己定义的textviewstyle的呢?这其实就是defStyleAttr的应用场景:定义Theme可配置样式。

  public TextView(Context context, AttributeSet attrs) {
    //指定属性textViewStyle为defStyleAttr,然后系统会去搜索Theme中你为这个
    //属性配置的style
    this(context, attrs, com.android.internal.R.attr.textViewStyle);
  }

  public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
    this(context, attrs, defStyleAttr, 0);
    //最终调用到View的第四个构造器时,调用了obtainStyledAttributes
    TypedArray a = theme.obtainStyledAttributes(attrs,
            com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
  }

resId=defStyleRes

resId or defStyleRes:直接从资源文件中定义的某个样式中读取。

NULL

看看第二个方法吧,里面除了指定了attrs属性集之外没有任何属性值来源,数据从哪儿来呢?原来我们可以直接在Theme中指定属性的值,那么NULL表示直接从Theme中读取属性。

是不是看到这里你已经有点迷糊了?不要紧,耐心看下去,后面有一个例子,看完例子你再回头看看这里的说明就ok了。

四个参数的obtainStyledAttributes

看看这个方法,返回的结果还是我们所关心的attrs(int[])中包含的属性集。那么数据来源呢?一共有4个,set,defStyleAttr,NULL,defStyleRes,如果一个属性在多个地方都被定义了,那么以哪个为准?

优先级如下:
set>defStyleAttr(主题可配置样式)>defStyleRes(默认样式)>NULL(主题中直接指定)

栗子终于来了!!下载地址-GitHub

attr资源文件中如下定义:

  //定义属性
<declare-styleable name="custom_attrs">
    <attr name="custom_color1" format="color"></attr>
    <attr name="custom_color2" format="color"></attr>
    <attr name="custom_color3" format="color"></attr>
    <attr name="custom_color4" format="color"></attr>
    <attr name="custom_color5" format="color"></attr>
</declare-styleable>
//定义theme可配置style
<attr name="custom_style" format="reference"></attr>
 //定义默认style
<style name="default_style">
    <item name="custom_color1">#ff333333</item>
    <item name="custom_color2">#ff333333</item>
    <item name="custom_color3">#ff333333</item>
    <item name="custom_color4">#ff333333</item>
</style>

styles资源文件中如下定义:

  <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
      //配置style
    <item name="custom_style">@style/custom_theme</item>
      //直接在主题中指定
    <item name="custom_color1">#ff444444</item>
    <item name="custom_color2">#ff444444</item>
    <item name="custom_color3">#ff444444</item>
    <item name="custom_color4">#ff444444</item>
    <item name="custom_color5">#ff444444</item>
  </style>
    //主题中配置的style
  <style name="custom_theme">
    <item name="custom_color1">#ff222222</item>
    <item name="custom_color2">#ff222222</item>
    <item name="custom_color3">#ff222222</item>
  </style>
    //直接在layout文件中引用的style,最后会被放到set中
  <style name="myStyle">
    <item name="custom_color1">#ff111111</item>
    <item name="custom_color2">#ff111111</item>
  </style>

layout中如下定义:

  <com.exmp.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:style="@style/myStyle"
    app:custom_color1="#ff000000"
    >
  </com.exmp.MyCustomView>

在MyCustomView的构造器中:

public MyCustomView(Context context) {
    this(context,null);
}

public MyCustomView(Context context, AttributeSet set) {
    this(context, set, R.attr.custom_style);
}

public LinearRecyclerView(Context context, AttributeSet set, int defStyle) {
    super(context, set, defStyle);
    final TypedArray a = context.obtainStyledAttributes(
            set, R.styleable.custom_attrs, defStyle, R.style.default_style);
}

如上配置之后,TypedArray中获取的属性值分别是:
custom_color1=#ff000000 //布局文件中直接指定,优先级最高
custom_color2=#ff111111 //布局同通过style指定,也包含在set中,优先级第二
custom_color3=#ff222222 //布局通过主题中配置风格style
custom_color4=#ff444444 //由系统Theme直接指定的
custom_color5=#ff444444

这里看到我们的默认style没有效果,原因是只有当defStyle(Theme中可配置style)不为0 而且在Theme中已经配置了defStyle时,默认style不起效果。

TypedArray

我们看到在获取到属性值之后,都会返回一个TypedArray对象,它又是什么鬼?TypedArray主要有两个作用,第一是内部去转换attrid和属性值数组的关系;第二是提供了一些类型的自动转化,比如我们getString时,如果你是通过@string/hello这种方式设置的,TypedArray会自动去将ResId对应的string从资源文件中读出来。说到底,都是为了方便我们获取属性参数。

例子下载地址-GitHub

回到开始

现在我们应该知道如何为我们的自定义View添加在主题中可配置的Style,主要是通过
obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes)方法来做,需要注意的是,defStyleAttrdefStyleRes都可以设置成0表示不去搜索可配置的风格和默认风格。

问题来了,如果来实现我的第二个需求为一个普通的类添加一个可以在Theme中可以配置的样式(主要不就是为了业务方使用库时配置或者传入一些简单的值,这里不去讨论这种方式的优劣,只讨论可行性)?其实很简单:

首先定义:

  <attr name="config_style" reformat="referenc" />

  public class ClassNeedConfig {

    public ClassNeedConfig(Context context) {
         //因为这个类不是通过Inflate进来的,所以肯定没有set,直接给null就ok
         //attrs  你需要获取的属性,通常是自己定义的
         //指定在Theme中搜索的属性
          // 0表示不去搜索默认的样式
        TypedArray a = context.obtainStyledAttributes(null,attrs,R.attr.config_style,0);
    }
}

本文主要参考:
Android 深入理解Android中的自定义属性
Android中自定义样式与View的构造函数中的第三个参数defStyle的意义

2016-01-14更新
优先级如下:set>defStyleAttr(主题可配置样式)>defStyleRes(默认样式)>NULL(主题中直接指定)
这个优先级需要说明一点,defStyleRes只有在defStyleAttr为0或者主题中没有配置时,才会生效;所以上面例子中
custom_color4=#ff444444 而不是333333,因为此时的defStyleAttr我们配置了。

3

Android中自定义属性的格式详解

1. reference:参考某一资源ID。

   (1)属性定义:

            <declare-styleable name = "名称">

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

            </declare-styleable>

    (2)属性使用:

             <ImageView

                     android:layout_width = "42dip"
                     android:layout_height = "42dip"
                     android:background = "@drawable/图片ID"

                     />

2. color:颜色值。

    (1)属性定义:

            <declare-styleable name = "名称">

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

            </declare-styleable>

    (2)属性使用:

            <TextView

                     android:layout_width = "42dip"
                     android:layout_height = "42dip"
                     android:textColor = "#00FF00"

                     />

3. boolean:布尔值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "focusable" format = "boolean" />

            </declare-styleable>

    (2)属性使用:

            <Button

                    android:layout_width = "42dip"
                    android:layout_height = "42dip"

                    android:focusable = "true"

                    />

 

4. dimension:尺寸值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "layout_width" format = "dimension" />

            </declare-styleable>

    (2)属性使用:

            <Button

                    android:layout_width = "42dip"
                    android:layout_height = "42dip"

                    />

5. float:浮点值。

    (1)属性定义:

            <declare-styleable name = "AlphaAnimation">

                   <attr name = "fromAlpha" format = "float" />
                   <attr name = "toAlpha" format = "float" />

            </declare-styleable>

    (2)属性使用:

            <alpha
                   android:fromAlpha = "1.0"
                   android:toAlpha = "0.7"

                   />

6. integer:整型值。

    (1)属性定义:

            <declare-styleable name = "AnimatedRotateDrawable">

                   <attr name = "visible" />
                   <attr name = "frameDuration" format="integer" />
                   <attr name = "framesCount" format="integer" />
                   <attr name = "pivotX" />
                   <attr name = "pivotY" />
                   <attr name = "drawable" />

            </declare-styleable>

    (2)属性使用:

            <animated-rotate

                   xmlns:android = "http://schemas.android.com/apk/res/android
                   android:drawable = "@drawable/图片ID"  
                   android:pivotX = "50%"  
                   android:pivotY = "50%"  
                   android:framesCount = "12"  
                   android:frameDuration = "100"             />

7. string:字符串。

   (1)属性定义:

            <declare-styleable name = "MapView">
                   <attr name = "apiKey" format = "string" />
            </declare-styleable>

    (2)属性使用:

            <com.google.android.maps.MapView
                    android:layout_width = "fill_parent"
                    android:layout_height = "fill_parent"
                    android:apiKey = "0jOkQ80oD1JL9C6HAja99uGXCRiS2CGjKO_bc_g"

                    />

8. fraction:百分数。

    (1)属性定义:

            <declare-styleable name="RotateDrawable">
                   <attr name = "visible" />
                   <attr name = "fromDegrees" format = "float" />
                   <attr name = "toDegrees" format = "float" />
                   <attr name = "pivotX" format = "fraction" />
                   <attr name = "pivotY" format = "fraction" />
                   <attr name = "drawable" />
            </declare-styleable>

    (2)属性使用:

            <rotate

                   xmlns:android = "http://schemas.android.com/apk/res/android"
               android:interpolator = "@anim/动画ID"

                   android:fromDegrees = "0" 
               android:toDegrees = "360"

                   android:pivotX = "200%"

                   android:pivotY = "300%" 
               android:duration = "5000"

                   android:repeatMode = "restart"

                   android:repeatCount = "infinite"

                   />

9. enum:枚举值。

    (1)属性定义:

            <declare-styleable name="名称">
                   <attr name="orientation">
                          <enum name="horizontal" value="0" />
                          <enum name="vertical" value="1" />
                   </attr>           

            </declare-styleable>

    (2)属性使用:

            <LinearLayout

                    xmlns:android = "http://schemas.android.com/apk/res/android"
                    android:orientation = "vertical"
                    android:layout_width = "fill_parent"
                    android:layout_height = "fill_parent"
                    >
            </LinearLayout>

10. flag:位或运算。

     (1)属性定义:

             <declare-styleable name="名称">
                    <attr name="windowSoftInputMode">
                            <flag name = "stateUnspecified" value = "0" />
                            <flag name = "stateUnchanged" value = "1" />
                            <flag name = "stateHidden" value = "2" />
                            <flag name = "stateAlwaysHidden" value = "3" />
                            <flag name = "stateVisible" value = "4" />
                            <flag name = "stateAlwaysVisible" value = "5" />
                            <flag name = "adjustUnspecified" value = "0x00" />
                            <flag name = "adjustResize" value = "0x10" />
                            <flag name = "adjustPan" value = "0x20" />
                            <flag name = "adjustNothing" value = "0x30" />
                     </attr>        

             </declare-styleable>

     (2)属性使用:

            <activity

                   android:name = ".StyleAndThemeActivity"
                   android:label = "@string/app_name"
                   android:windowSoftInputMode = "stateUnspecified | stateUnchanged | stateHidden">
                   <intent-filter>
                          <action android:name = "android.intent.action.MAIN" />
                          <category android:name = "android.intent.category.LAUNCHER" />
                   </intent-filter>
             </activity>

    注意:

     属性定义时可以指定多种类型值。

    (1)属性定义:

            <declare-styleable name = "名称">

                   <attr name = "background" format = "reference|color" />

            </declare-styleable>

    (2)属性使用:

             <ImageView

                     android:layout_width = "42dip"
                     android:layout_height = "42dip"
                     android:background = "@drawable/图片ID|#00FF00"

                     />



©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页