安卓系统为我们提供了丰富的控件,但是在实际项目中我们仍然需要重新通过布局来实现一些效果,比如我们需要一个上面图标,下面文字的button,类似于下面这样的:

最直接的解决办法是通过将imageview和textview放在一个垂直排列的linearlayout中,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<code><linearlayout xmlns:android=</code><code>"http://schemas.android.com/apk/res/android"</code>
<code> </code><code>android:layout_width=</code><code>"fill_parent"</code>
<code> </code><code>android:layout_height=</code><code>"fill_parent"</code>
<code> </code><code>android:gravity=</code><code>"center"</code>
<code> </code><code>android:orientation=</code><code>"vertical"</code>
<code>></code>
<code> </code><code><imageview</code>
<code> </code><code>android:id=</code><code>"@+id/icon_part"</code>
<code> </code><code>android:layout_width=</code><code>"wrap_content"</code>
<code> </code><code>android:layout_height=</code><code>"wrap_content"</code>
<code> </code><code>android:layout_gravity=</code><code>"center"</code>
<code> </code><code>/></code>
<code> </code><code><textview</code>
<code> </code><code>android:id=</code><code>"@+id/text_part"</code>
<code> </code><code>android:textcolor=</code><code>"#000000"</code>
<code>/></code>
<code></linearlayout></code>
但是每一个button都需要这么长的代码,上面三个按钮的话就需要重复写三次,而且别人一看是个linearlayout,不会将它button联系起来。
如果有一种办法能将上面那个布局组合成一个控件就好了。
的确是有办法的。主要有两方面的工作。
1.新建一个继承自linearlayout的类(也可以是其他布局类,不过linearlayout好像比较合适),然后通过inflater在这个类的构造函数中将上面的布局添加进去。
2.为了能在xml中也给这个自定义控件赋予属性来获得现实效果,比如字体大小、图标资源等,我们还需要在attrs文件中申明一些自定义属性。你可以查阅declare-styleable了解这是怎么回事。
我这里有一个已经实现了这种button效果的类fleximagebutton:
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
<code>package com.jcodecraeer.client.widget;</code>
<code>import com.jcodecraeer.client.r;</code>
<code>import android.content.context;</code>
<code>import android.content.res.typedarray;</code>
<code>import android.graphics.drawable.drawable;</code>
<code>import android.util.attributeset;</code>
<code>import android.view.layoutinflater;</code>
<code>import android.view.view;</code>
<code>import android.widget.imageview;</code>
<code>import android.widget.linearlayout;</code>
<code>import android.widget.textview;</code>
<code>public class fleximagebutton extends linearlayout {</code>
<code> </code><code>private imageview imageview;</code>
<code> </code><code>private textview textview;</code>
<code> </code><code>private charsequence text;</code>
<code> </code><code>private drawable drawable;</code>
<code> </code><code>private float textsize;</code>
<code> </code><code>public fleximagebutton(context context) {</code>
<code> </code><code>super</code><code>(context);</code>
<code> </code><code>// todo auto-generated constructor stub</code>
<code> </code><code>}</code>
<code> </code><code>public fleximagebutton(context context, attributeset attrs) {</code>
<code> </code><code>super</code><code>(context, attrs);</code>
<code> </code><code>typedarray a = context.obtainstyledattributes(attrs, r.styleable.fleximagebutton);</code>
<code> </code><code>text = a.gettext(r.styleable.fleximagebutton_text);</code>
<code> </code><code>if</code><code>(text==</code><code>null</code><code>){</code>
<code> </code><code>text=</code><code>""</code><code>;</code>
<code> </code><code>}</code>
<code> </code><code>drawable d = a.getdrawable(r.styleable.fleximagebutton_src);</code>
<code> </code><code>if</code>
<code>(d !=</code><code>null</code><code>) {</code>
<code> </code><code>drawable=d;</code>
<code> </code><code>}</code><code>else</code>
<code>{</code>
<code> </code><code>throw</code>
<code>new</code> <code>runtimeexception(</code><code>"图像资源为空"</code><code>);</code>
<code> </code>
<code> </code><code>textsize = a.getdimension(r.styleable.fleximagebutton_textsize,12);</code>
<code> </code><code>string infservice = context.layout_inflater_service;</code>
<code> </code><code>layoutinflater inflater = (layoutinflater) context</code>
<code> </code><code>.getsystemservice(context.layout_inflater_service);</code>
<code> </code><code>inflater.inflate(r.layout.flex_image_button_layout,</code><code>this</code><code>);</code>
<code> </code><code>imageview = (imageview) findviewbyid(r.id.icon_part);</code>
<code> </code><code>imageview.setimagedrawable(drawable);</code>
<code> </code><code>textview = (textview) findviewbyid(r.id.text_part);</code>
<code> </code><code>textview.settextsize((float) textsize);</code>
<code> </code><code>textview.settext(text);</code>
<code> </code><code>if</code><code>(text.equals(</code><code>""</code><code>)||text==</code><code>null</code><code>){</code>
<code> </code><code>textview.setvisibility(view.gone);</code>
<code> </code><code>a.recycle();</code>
<code> </code><code>public void setimageresource(int resid) {</code>
<code> </code><code>imageview.setimageresource(resid);</code>
<code> </code><code>public void settextviewtext(string text) {</code>
<code>}</code>
在attrs.xml文件中我们声明一些自定义属性,这里我们希望我的fleximagebutton能拥有可以灵活设置的文字属性,字体大小属性、图标资源属性,因此我这样定义:
<code><resources></code>
<code> </code><code><declare-styleable name=</code><code>"fleximagebutton"</code><code>></code>
<code> </code><code><attr name=</code><code>"text"</code>
<code>format=</code><code>"reference"</code><code>/></code>
<code> </code><code><attr name=</code><code>"src"</code>
<code> </code><code><attr name=</code><code>"textsize"</code>
<code>format=</code><code>"dimension"</code><code>/></code>
<code> </code><code></declare-styleable></code>
<code></resources></code>
其中<code>format=</code><code>"reference"</code>表示这个属性的值类型是资源id,也就是说在使用<code></code>fleximagebutton的时候我只可以用资源id来为这个属性赋值。属性值类型有那些,我在文章结尾的附录里面一一列出。
上面我们已经完成了一个自定义的控件,activity的布局文件中如下使用fleximagebutton:
<code><com.jcodecraeer.client.widget.fleximagebutton</code>
<code> </code><code>android:layout_height=</code><code>"fill_parent"</code>
<code> </code><code>android:layout_width=</code><code>"50dip"</code>
<code> </code><code>cl:src=</code><code>"@drawable/toolbar_collect"</code>
<code> </code><code>cl:text=</code><code>"@string/collect"</code>
<code> </code><code>android:clickable=</code><code>"true"</code>
<code> </code><code>android:focusable=</code><code>"true"</code>
<code> </code><code>android:layout_marginleft=</code><code>"5dip"</code>
<code> </code><code>android:layout_marginright=</code><code>"5dip"</code>
<code> </code><code>android:contentdescription=</code><code>"收藏"</code>
<code> </code>
<code> </code><code>android:background=</code><code>"@drawable/back_selector"</code>
仔细的人会注意到所有这些属性中,有两种形式。其中凡是android:开头的都是系统属性,而
<code>cl:src=</code><code>"@drawable/toolbar_collect"</code>
<code>cl:text=</code><code>"@string/collect"</code>
为我自定义的属性,为什么是cl:开头?
你也可以不用cl开头,但是不管你用什么开头,如果你用到了自定义属性,你都必须在activity布局文件的最开始这样声明:
<code><?xml version=</code><code>"1.0"</code>
<code>encoding=</code><code>"utf-8"</code><code>?></code>
<code><relativelayout</code>
<code> </code><code>xmlns:android=</code><code>"http://schemas.android.com/apk/res/android"</code>
<code> </code><code>xmlns:cl=</code><code>"http://schemas.android.com/apk/res/com.jcodecraeer.client"</code>
其中<code>cl</code>就是刚刚用到的,<code>com.jcodecraeer.client</code>为我的apk包名,注意是apk包名,而不是你自定义控件的在包中的路径。
附录:自定义属性的值类型:
1. reference:参考某一资源id。
(1)属性定义:
<code><declare-styleable name=</code><code>"名称"</code><code>></code>
<code> </code><code><attr format=</code><code>"reference"</code>
<code>name=</code><code>"background"</code>
<code></declare-styleable></code>
(2)属性使用:
<code><imageview</code>
<code> </code><code>android:layout_width=</code><code>"42dip"</code>
<code> </code><code>android:layout_height=</code><code>"42dip"</code>
<code> </code><code>android:background=</code><code>"@drawable/图片id"</code>
2. color:颜色值。
<code> </code><code><attr format=</code><code>"color"</code>
<code>name=</code><code>"textcolor"</code>
<code><textview</code>
<code> </code><code>android:textcolor=</code><code>"#00ff00"</code>
3. boolean:布尔值。
<code> </code><code><attr format=</code><code>"boolean"</code>
<code>name=</code><code>"focusable"</code>
<code><button</code>
4. dimension:尺寸值。
<code> </code><code><attr format=</code><code>"dimension"</code>
<code>name=</code><code>"layout_width"</code>
5. float:浮点值。
<code><declare-styleable name=</code><code>"alphaanimation"</code><code>></code>
<code> </code><code><attr format=</code><code>"float"</code>
<code>name=</code><code>"fromalpha"</code>
<code>name=</code><code>"toalpha"</code>
<code><alpha</code>
<code> </code><code>android:fromalpha=</code><code>"1.0"</code>
<code> </code><code>android:toalpha=</code><code>"0.7"</code>
6. integer:整型值。
<code><declare-styleable name=</code><code>"animatedrotatedrawable"</code><code>></code>
<code> </code><code><attr format=</code><code>"integer"</code>
<code>name=</code><code>"frameduration"</code>
<code>name=</code><code>"framescount"</code>
<code><animated-rotate</code>
<code> </code><code>android:frameduration=</code><code>"100"</code>
<code> </code><code>android:framescount=</code><code>"12"</code>
<code> </code><code>/></code>
7. string:字符串。
<code><declare-styleable name=</code><code>"mapview"</code><code>></code>
<code> </code><code><attr format=</code><code>"string"</code>
<code>name=</code><code>"apikey"</code>
<code><com.google.android.maps.mapview</code>
<code> </code><code>android:apikey=</code><code>"0jokq80od1jl9c6haja99ugxcris2cgjko_bc_g"</code>
8. fraction:百分数。
<code><declare-styleable name=</code><code>"rotatedrawable"</code><code>></code>
<code> </code><code><attr format=</code><code>"fraction"</code>
<code>name=</code><code>"pivotx"</code>
<code>name=</code><code>"pivoty"</code>
<code><rotate</code>
<code> </code><code>android:pivotx=</code><code>"200%"</code>
<code> </code><code>android:pivoty=</code><code>"300%"</code>
<code> </code><code>/></code>
9. enum:枚举值。
<code> </code><code><attr name=</code><code>"orientation"</code><code>></code>
<code> </code><code><enum name=</code><code>"horizontal"</code>
<code>value=</code><code>"0"</code>
<code> </code><code><enum name=</code><code>"vertical"</code>
<code>value=</code><code>"1"</code>
<code> </code><code></attr></code>
<code><linearlayout</code>
10. flag:位或运算。
<code> </code><code><attr name=</code><code>"windowsoftinputmode"</code><code>></code>
<code> </code><code><flag name=</code><code>"stateunspecified"</code>
<code> </code><code><flag name=</code><code>"stateunchanged"</code>
<code> </code><code><flag name=</code><code>"statehidden"</code>
<code>value=</code><code>"2"</code>
<code> </code><code><flag name=</code><code>"statealwayshidden"</code>
<code>value=</code><code>"3"</code>
<code> </code><code><flag name=</code><code>"statevisible"</code>
<code>value=</code><code>"4"</code>
<code> </code><code><flag name=</code><code>"statealwaysvisible"</code>
<code>value=</code><code>"5"</code>
<code> </code><code><flag name=</code><code>"adjustunspecified"</code>
<code>value=</code><code>"0x00"</code>
<code> </code><code><flag name=</code><code>"adjustresize"</code>
<code>value=</code><code>"0x10"</code>
<code> </code><code><flag name=</code><code>"adjustpan"</code>
<code>value=</code><code>"0x20"</code>
<code> </code><code><flag name=</code><code>"adjustnothing"</code>
<code>value=</code><code>"0x30"</code>
<code><activity</code>
<code> </code><code>android:windowsoftinputmode=</code><code>"stateunspecified | stateunchanged | statehidden"</code> <code>></code>
<code></activity></code>
注意:属性定义时可以指定多种类型值:
<code> </code><code><attr format=</code><code>"reference|color"</code>
<code> </code><code>android:background=</code><code>"@drawable/图片id|#00ff00"</code>
<code></code>