上:
1、引言
对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现:
- 自定义一个CustomView(extends View )类
- 编写values/attrs.xml,在其中编写styleable和item等标签元素
- 在布局文件中CustomView使用自定义的属性(注意namespace)
- 在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呢?
其实的确是可以不写的,怎么做呢?
那么现在的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">
//在主题中设置textview的style
<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) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
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">
<item name="custom_style">@style/custom_theme</item>
<item name="custom_color1">
<item name="custom_color2">
<item name="custom_color3">
<item name="custom_color4">
<item name="custom_color5">
</style>
<style name="custom_theme">
<item name="custom_color1">
<item name="custom_color2">
<item name="custom_color3">
</style>
<style name="myStyle">
<item name="custom_color1">
<item name="custom_color2">
</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主要有两个作用,第一是内部去转换attr id和属性值数组的关系;第二是提供了一些类型的自动转化,比如我们getString 时,如果你是通过@string/hello 这种方式设置的,TypedArray 会自动去将ResId 对应的string 从资源文件中读出来。说到底,都是为了方便我们获取属性参数。
例子下载地址-GitHub
回到开始
现在我们应该知道如何为我们的自定义View 添加在主题中可配置的Style ,主要是通过 obtainStyledAttributes (AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) 方法来做,需要注意的是,defStyleAttr 和defStyleRes 都可以设置成0表示不去搜索可配置的风格和默认风格。
问题来了,如果来实现我的第二个需求为一个普通的类添加一个可以在Theme中可以配置的样式(主要不就是为了业务方使用库时配置或者传入一些简单的值,这里不去讨论这种方式的优劣,只讨论可行性)?其实很简单:
首先定义:
<attr name="config_style" reformat="referenc" />
public class ClassNeedConfig {
public ClassNeedConfig(Context context) {
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"
/>
|