安卓系統為我們提供了豐富的控件,但是在實際項目中我們仍然需要重新通過布局來實作一些效果,比如我們需要一個上面圖示,下面文字的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>