- Android經(jīng)典應(yīng)用程序開發(fā)
- 韓超編著
- 8字
- 2019-01-09 15:18:52
第3章 控件和布局
3.1 控件
在大多數(shù)GUI系統(tǒng)中,控件是其提供的主要功能,使用各種控件也是構(gòu)建界面的最主要的工作之一。了解系統(tǒng)提供的控件及其擴(kuò)展性是界面構(gòu)建的核心。
3.1.1 Android中的控件
1.控件類的繼承結(jié)構(gòu)
android.view.View類(視圖類)呈現(xiàn)了最基本的UI構(gòu)造塊。一個(gè)視圖占據(jù)屏幕上的一個(gè)方形區(qū)域,并且負(fù)責(zé)繪制和事件處理。
Android中控件類的擴(kuò)展結(jié)構(gòu)如圖3-1所示。

圖3-1 Android中控件類的擴(kuò)展結(jié)構(gòu)
View有眾多的擴(kuò)展者,它們大部分是在android.widget包中,這些繼承者實(shí)際上就是Android系統(tǒng)中的“控件”。View實(shí)際上就是各個(gè)控件的基類,創(chuàng)建交互式的圖形用戶界面的基礎(chǔ)。
View的直接繼承者包括文本視圖(TextView)、圖像視圖(ImageView)、進(jìn)度條(ProgressBar)等。它們各自又有眾多的繼承者。每個(gè)控件除了繼承父類功能之外,一般還具有自己的公有方法、保護(hù)方法、XML屬性等。
在Android中使用各種控件的一般情況是在布局文件中可以實(shí)現(xiàn)UI的外觀,然后在Java文件中實(shí)現(xiàn)對(duì)各種控件的控制動(dòng)作。控件類的名稱也是它們?cè)诓季治募ML中使用的標(biāo)簽名稱。
2.控件通用行為和屬性
View是Android中所有控件類的基類,因此View中一些內(nèi)容是所有控件類都具有的通用行為和屬性。
提示:由于Java語(yǔ)言不支持多重繼承,因此Android控件不可能以基本功能的“排列組合”的方式實(shí)現(xiàn)。在這種情況下,為了實(shí)現(xiàn)功能的復(fù)用,基類的功能往往做得較強(qiáng),作為控件的祖先類,View所實(shí)現(xiàn)的功能也是最多的。
控件類經(jīng)常在布局文件中使用,因此其可以使用XML屬性(XMLAttributes),和Java代碼經(jīng)常具有對(duì)應(yīng)關(guān)系。
View作為各種控件的基類,其XML屬性所有控件通用,幾個(gè)重要的XML屬性如表3-1所示。
表3-1 View中幾個(gè)重要XML屬性及其對(duì)應(yīng)的方法

其中,android:id表示控件的標(biāo)識(shí),通常需要在布局文件中指定這個(gè)屬性。View中與控件標(biāo)識(shí)相關(guān)的幾個(gè)方法如下所示:
public int getId() // 獲得控件的id(int類型) public void setId(int id) // 設(shè)置控件的id(int類型) public Object getTag() // 獲得控件的tag(Object類型) public void setTag(Object tag) // 設(shè)置控件的tag(Object類型)
對(duì)于一個(gè)控件,也就是View的繼承者,整數(shù)類型id是其主要的標(biāo)識(shí)。其中,getId()可以獲得控件的id,而setId()可以將一個(gè)整數(shù)設(shè)置為控件的id,但是這個(gè)方法并不常用。View的id通常可以在布局文件中獲得。
Object類型的標(biāo)識(shí)tag是控件的一個(gè)擴(kuò)展標(biāo)識(shí),由于使用了Object類型,它可以接受大部分的Java類型。
在一個(gè)View中根據(jù)id或者tag查找其孩子的方法如下所示:
public final View findViewById(int id) public final View findViewWithTag(Object tag)
findViewById()和findViewWithTag()的目的是返回這個(gè)View樹中id和tag為某個(gè)數(shù)值的View的句柄。View樹的含義是View及其所有的孩子。
值得注意的是,id不是控件的唯一標(biāo)識(shí),例如布局文件中id是可以重復(fù)的,在這種重復(fù)的情況下,findViewById()的結(jié)果不能確保找到唯一的控件。
提示:作為控件的標(biāo)識(shí)的id和tag可以配合使用:當(dāng)id有重復(fù)的時(shí)候,可以通過(guò)給控件設(shè)置不同的tag,對(duì)其進(jìn)行區(qū)分。
可見性的問(wèn)題,android:visibility在布局文件中有三個(gè)數(shù)值:visibl(e可見,默認(rèn)),invisible(不可見),gone(去除)。在Java代碼中,setVisibility()能使用的枚舉值與其對(duì)應(yīng),它們是:View.VISIBLE(0x0),View.INVISIBLE(0x4),View.GONE(0x8)。
參考示例程序:Visibility(ApiDemo=>Views)
源代碼:com/example/android/apis/view/visibility_1.java
布局文件:visibility_1.xml
Visibility程序的運(yùn)行效果如圖3-2所示。

圖3-2 Visibility程序(左:可見;中:不可見;右:去除)
對(duì)于文字為View B的文本框,分別使用了visible、invisible和gone設(shè)置。invisible和gone的區(qū)別在于invisible只是不可見,但是依然占位,gone表示將控件去除,顯示的效果就像沒(méi)有這個(gè)控件存在。
和Vi e w形態(tài)相關(guān)的幾個(gè)方法如下所示:
public void invalidate () // 使無(wú)效(重新繪制) public void requestLayout () // 申請(qǐng)重新布局 public final boolean requestFocus () // 申請(qǐng)聚焦
這幾個(gè)方法都和View的顯示形態(tài)有關(guān):invalidate()方法的功能是使得無(wú)效,用于重新繪制當(dāng)前的View;requestLayout()用于更新View樹,也就是由當(dāng)前View的大小位置變化更新與其相關(guān)的View;requestFocus()用于申請(qǐng)當(dāng)前的聚焦。
查找聚焦的Vi e w的方法如下所示:
public View findFocus () // 找到聚焦的View
在布局文件中,如果在一個(gè)控件的標(biāo)簽中使用<requestFocus />標(biāo)簽,表示指定它在默認(rèn)情況下被聚焦。當(dāng)使用上、下、左、右按鍵的時(shí)候,各個(gè)控件有著默認(rèn)的聚焦順序。其他聚焦的問(wèn)題可以在布局文件中進(jìn)一步處理,一個(gè)處理的方法如下所示:
<LinearLayout android:orientation="vertical"> <Button android:id="@+id/top" android:nextFocusUp="@+id/bottom" /> <Button android:id="@+id/bottom" android:nextFocusDown="@+id/top" /> </LinearLayout>
這里android:nextFocusUp和android:nextFocusDown分別是上下按鍵的時(shí)候,下一個(gè)聚焦的控件的id。
3.1.2 文本類控件
TextView(文本視圖)及其繼承者構(gòu)成了Android中的“文本類控件”,這是最常用的一類控件。
1.文本視圖TextView
TextView是一個(gè)View的直接繼承者,這個(gè)類本身作為一個(gè)文本框來(lái)使用,TextView對(duì)View的主要擴(kuò)展就是在其中實(shí)現(xiàn)了顯示文本的功能。TextView中的幾個(gè)主要的屬性如表3-2所示。
表3-2 TextView中幾個(gè)重要xml屬性及其對(duì)應(yīng)的方法

這些XML屬性均可以在TextView及其繼承者中使用。TextView有以下幾個(gè)直接繼承者:EditText(可編輯文本),Button(按鈕),Chronometer(計(jì)數(shù)器),CheckedTextView(可選擇文本區(qū)域)和DigitalClock(數(shù)字時(shí)鐘)。
2.可編輯文本EditText
可編輯文本(EditText)是TextView類的一個(gè)繼承者,其中最主要的一個(gè)特性就是其中文本可以編輯。
EditText在本質(zhì)上還是一個(gè)TextView,主要是其中的屬性設(shè)置有所不同。EditText繼承自View的屬性android:focusable,android:focusableInTouchMode和android:clickable默認(rèn)為true,繼承自TextView的android:editable屬性默認(rèn)為true。在外觀上,EditText具有特殊的背景,默認(rèn)的文本屬性也有所不同。除此之外,EditText中的文本有一個(gè)選擇的問(wèn)題,主要由selectAll()和setSelection()幾個(gè)方法完成。
EditText包含有以下幾個(gè)繼承者。
ExtractEditText:可抽取的可編輯文本;
AutoCompleteTextView:自動(dòng)補(bǔ)全文本視圖;
MultiAutoCompleteTextView:多行自動(dòng)補(bǔ)全文本視圖。
3.按鈕Button
按鈕(Button)作為TextView類的繼承者,主要的區(qū)別表現(xiàn)在外觀和使用的方式上。Button通常要設(shè)置處理單擊動(dòng)作的處理器(View.OnClickListener)。
Button對(duì)TextView的“擴(kuò)展”其實(shí)很少,這個(gè)類實(shí)際上只是有一個(gè)不同的背景(包括聚焦時(shí)、單擊時(shí))。
Button類具有一個(gè)名為CompoundButton的繼承者。CompoundButtond是具有選擇(checked)和未選擇(unchecked)兩種狀態(tài)的按鈕,當(dāng)單擊的時(shí)候自動(dòng)實(shí)現(xiàn)兩種狀態(tài)的切換,CompoundButton中具有如下兩個(gè)方法:
public boolean isChecked () // 是否選擇 public void setChecked (boolean checked) // 設(shè)置選擇狀態(tài)
CompoundButton是一個(gè)抽象類,不能在程序中直接使用。CompoundButton又有RadioButton、CheckBox和ToggleButton這三個(gè)繼承者。
CheckBox是具有一個(gè)選項(xiàng)框的多選按鈕,主要特點(diǎn)是形態(tài)的差異。
RadioButton是圓形的單選按鈕,除了形態(tài)上特別之外,它經(jīng)常以RadioGroup(單選按鈕組)作為容器,以實(shí)現(xiàn)同一組按鈕同時(shí)只能選定一個(gè)的功能。
ToggleButton為開關(guān)按鈕,包含開和關(guān)兩個(gè)狀態(tài),可以顯示不同的文本textOn(開)和textOff(關(guān)),ToggleButton的特定的XML屬性包括了以下的內(nèi)容。
android:disabledAlpha:禁止的時(shí)候的Alpha值,使用浮點(diǎn)數(shù);
android:textOn和android:textOff:定義開狀態(tài)和關(guān)狀態(tài)下顯示的文本。
參考示例程序:Buttons1(ApiDemo=>Views)
源代碼:com/example/android/apis/view/Buttons1.java
布局文件:buttons_1.xml
按鈕程序的運(yùn)行效果如圖3-3所示。
界面比較簡(jiǎn)單,前兩個(gè)按鈕是Button類,表示普通的按鈕;第三個(gè)按鈕是ToggleButton類,表示可以進(jìn)行開關(guān)操作的按鈕。
本例的布局文件buttons_1.xml的內(nèi)容如下所示:
<LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="vertical"> <Button android:id="@+id/button_normal" android:text="@string/buttons_1_normal"

圖3-3 按鈕程序(左:ToggleButton關(guān);右:ToggleButton開)
android:layout_width="wrap_content" android:layout_height="wrap_content" /> <Button android:id="@+id/button_small" style="?android:attr/buttonStyleSmall" android:text="@string/buttons_1_small" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ToggleButton android:id="@+id/button_toggle" android:text="@string/buttons_1_toggle" android:layout_width="wrap_content" android:layout_height="wrap_content" /> </LinearLayout>
這里主要引用了三個(gè)控件:第1個(gè)是一個(gè)普通的Button,沒(méi)有設(shè)置特殊屬性;第2個(gè)也是一個(gè)Button,但是其style被設(shè)置為android:attr/buttonStyleSmall,主要就是Button和其中文字的邊緣比較窄,因此Button形狀較小;第3個(gè)按鈕是ToggleButton,顯示的OFF和ON是其默認(rèn)的文本,選擇ON狀態(tài)的時(shí)候,將顯示亮色的標(biāo)志桿。
提示:Android中的很多控件的大小并不能隨意控制,例如ToggleButton,如果任意設(shè)置大小,將出現(xiàn)標(biāo)志桿和控件大小不匹配的情況。因此,經(jīng)常使用R.attr類中的buttonStyle和buttonStyleSmall等屬性指定其Style。
4.計(jì)時(shí)器Chronometer
Chronometer也是TextView的一個(gè)直接繼承者。Chronometer呈現(xiàn)的外觀就是一個(gè)簡(jiǎn)單的文本區(qū)域,顯示為可以自動(dòng)計(jì)數(shù)的格式。
Chronometer具有一些特殊的方法用于設(shè)置計(jì)數(shù)的基數(shù)、格式,如下所示:
public void setBase(long base) // 設(shè)置基數(shù) public long getBase() // 獲得基數(shù) public void setFormat(String format) // 設(shè)置格式 public String getFormat() // 獲得格式
Chronometer具有一個(gè)名稱為android:format的XML屬性,它和代碼中的setFormat()和getFormat()指定的格式代表的含義相同。其中可以為使用“%s”表示格式化的字符串,在顯示的時(shí)候被替換成“MM:SS”和“H:MM:SS”的形式。
Chronometer.OnChronometerTickListener是一個(gè)接口,設(shè)置到Chronometer當(dāng)中后,其中的onChronometerTick()方法在計(jì)數(shù)器的每一個(gè)節(jié)拍時(shí)刻被調(diào)用。
控制計(jì)數(shù)器開始和停止的方法如下所示:
public void start() // 開始計(jì)數(shù)器 public void stop() // 停止計(jì)數(shù)器
參考示例程序:Chronometer(ApiDemo=>Views)
源代碼:com/example/android/apis/view/ChronometerDemo.java
布局文件:chronometer.xml
計(jì)數(shù)器程序的運(yùn)行結(jié)果如圖3-4所示。

圖3-4 計(jì)數(shù)器程序的運(yùn)行結(jié)果(左:初始程序;中:設(shè)置成某個(gè)字符串;右:清除格式)
計(jì)數(shù)器程序的布局文件chronometer.xml的核心片段如下所示:
<Chronometer android:id="@+id/chronometer" android:format="@string/chronometer_initial_format" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="0" android:paddingBottom="30dip" android:paddingTop="30dip" />
其中,android:format使用的字符串如下所示:
<string name="chronometer_initial_format"> Initial format: <xliff:g id="initial-format">%s</xliff:g></string>
因此,此處格式化字符串當(dāng)中的“%s”在初始化顯示的時(shí)候被替換成了計(jì)數(shù)器數(shù)據(jù)的格式。通過(guò)Set format string和Clear format string兩個(gè)按鈕,可以將顯示的格式設(shè)置為其他,或者清除成無(wú)格式。
3.1.3 圖像類控件
在Android提供了功能強(qiáng)大的ImageView控件,表示圖像類控件,用于在界面上顯示圖片。ImageView具有一些繼承者。
1.圖像區(qū)域ImageView
ImageView又被稱為圖像視圖,是Android中可以直接顯示圖形的控件。幾個(gè)重要的XML屬性及其對(duì)應(yīng)的方法如表3-3所示。
表3-3 ImageView中幾個(gè)重要XML屬性及其對(duì)應(yīng)的方法

圖像源是ImageView控件的核心,實(shí)際上有多種不同的設(shè)置圖像源的方法:
public void setImageResource (int resId) // 設(shè)置圖像源的資源ID public void setImageURI(Uri uri) // 設(shè)置圖像源的URI public void setImageBitmap(Bitmap bm) // 設(shè)置一個(gè)Bitmap位圖為圖像源
使用ID的方式表示設(shè)置包中預(yù)置的圖像資源,使用URI可以設(shè)置文件系統(tǒng)中存儲(chǔ)在各種地方的圖像等,使用Bitmap的方式可以設(shè)置一個(gè)已經(jīng)表示為Bitmap類型的圖像。
android:scaleType表示縮放的類型,由ImageView.ScaleType枚舉類型來(lái)表示,包括居中,保持縱橫比例的居中縮放,適應(yīng)居中等類型。
ImageView還支持縮放、剪裁等功能:最大的寬(android:maxWidth)和最大的高(android:maxHeight)可以實(shí)現(xiàn)讓圖像縮小的功能。android:adjustViewBounds表示是否調(diào)整ImageView的邊界。
參考示例程序:ImageView(ApiDemo=>Views)
源代碼:com/example/android/apis/view/ImageView1.java
布局文件:image_view_1.xml
ImageView程序的運(yùn)行效果如圖3-5所示。
程序中的圖像都是通過(guò)ImageView類來(lái)實(shí)現(xiàn)顯示的,ImageView是View的直接擴(kuò)展者。布局文件image_view_1.xml的一個(gè)片段如下所示:
<!--1. 沒(méi)有縮放的圖像:自動(dòng)調(diào)整邊界 --> <ImageView android:src="@drawable/sample_1" android:adjustViewBounds="true" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <!--2. 自動(dòng)調(diào)整邊界限制最大的寬高 --> <ImageView android:src="@drawable/sample_1" android:adjustViewBounds="true"

圖3-5 ImageView程序的運(yùn)行效果
android:maxWidth="50dip" android:maxHeight="50dip" android:layout_width="wrap_content" android:layout_height="wrap_content" />
以上布局文件的片段,表示的是前兩個(gè)ImageView的顯示情況,這里需要注意的是ImageView涉及兩個(gè)尺寸,一個(gè)是ImageView控件的尺寸,一個(gè)是其中內(nèi)容的圖片的尺寸。第一個(gè)示例使用的是圖片的原始大小,第二個(gè)示例使用ImageView的android:maxWidth和android:maxHeight屬性限制了其中圖片的大小。
最后兩個(gè)圖像的布局文件如下所示:
<!--3. 限制最大為70x70, 留有10的邊界,圖像為“笑臉”--> <ImageView android:src="@drawable/stat_happy" android:background="#FFFFFFFF" android:adjustViewBounds="true" android:maxWidth="70dip" android:maxHeight="70dip" android:padding="10dip" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <!--4. 控件的尺寸為70x70, 留有10的邊界,圖像為“笑臉”--> <ImageView android:src="@drawable/stat_happy" android:background="#FFFFFFFF" android:scaleType="centerInside" android:padding="10dip" android:layout_width="70dip" android:layout_height="70dip" />
倒數(shù)第二個(gè)ImageView限制了其中的內(nèi)容圖像的大小最大為70x70,由于內(nèi)容圖像的大小小于70x70,此設(shè)置實(shí)際無(wú)效;最后將控件的大小設(shè)置為一個(gè)70x70,并且留有10dip的邊界,將android:scaleType設(shè)置為“centerInside”表示居中,由于沒(méi)有設(shè)置android:adjustViewBounds為true,因此控件不會(huì)調(diào)整到其中內(nèi)容的大小。
2.圖像按鈕ImageButton
圖像按鈕ImageButton是一個(gè)帶有圖片的按鈕,從邏輯上可以實(shí)現(xiàn)普通按鈕功能。圖像按鈕實(shí)際上結(jié)合了圖像和按鈕的雙重特性。圖像按鈕ImageButton繼承了ImageView,它結(jié)合了圖像和按鈕的功能。ImageButton除了可以當(dāng)做按鈕來(lái)使用,其他方面和ImageView基本一致。
ImageButton與圖像相關(guān)的XML屬性、方法與ImageView是一樣的,它與ImageView的區(qū)別也僅在于外觀和使用方式上,ImageButton具有按鈕樣式的背景。
事實(shí)上,ImageButton除了在外觀上表現(xiàn)成一個(gè)按鈕的狀態(tài),其他方面和ImageView基本一樣。由于是按鈕的功能,在Java源程序中,ImageButton通常被設(shè)定OnClickListener來(lái)獲得點(diǎn)擊時(shí)候的響應(yīng)函數(shù)。
提示:圖像按鈕ImageButton只繼承了ImageView,和普通按鈕Button并沒(méi)有繼承關(guān)系,不能作為Button類使用。
ZoomButton是ImageButton的一個(gè)繼承者,它是一個(gè)帶有動(dòng)態(tài)縮放功能的圖像按鈕。這個(gè)類與基類的主要區(qū)別也是外觀上,它有方法用于設(shè)置縮放的速度,如下所示:
public void setZoomSpeed (long speed)
參考示例程序:ImageButton(ApiDemo=>Views)
源代碼:com/example/android/apis/view/ImageButton.java
布局文件:image_button_1.xml
ImageButton程序的運(yùn)行效果如圖3-6所示。

圖3-6 ImageButton程序的運(yùn)行效果
ImageButton程序的布局文件image_button_1.xml的主要內(nèi)容如下所示:
<ImageButton android:layout_width="100dip" android:layout_height="50dip" android:src="@android:drawable/sym_action_call" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content"
android:src="@android:drawable/sym_action_chat" /> <ImageButton android:layout_width="wrap_content" android:layout_height="wrap_content" android:src="@android:drawable/sym_action_email" />
這里使用的各個(gè)XML屬性實(shí)際上都是ImageView的屬性。圖像按鈕中經(jīng)常使用類似上述透明的圖片與背景中的按鈕形態(tài)做出配合。
3.1.4 進(jìn)度條類控件
ProgressBar是View的直接繼承者,表示Android中的進(jìn)度條。除了可以作為控件使用之外,進(jìn)度條還可以在對(duì)話框和標(biāo)題欄中使用。
1.進(jìn)度條ProgressBar
ProgressBar類表示進(jìn)度條控件,它有兩種基本形態(tài),圓型和水平型。圓型表示沒(méi)有確切進(jìn)度的過(guò)程,水平型表示一個(gè)百分比的過(guò)程。
ProgressBar預(yù)定義了以下幾種樣式。
android:progressBarStyle:默認(rèn)樣式(圓型)
android:progressBarStyleHorizontal:水平樣式(水平)
android:progressBarStyleLarge:大的樣式(圓型)
android:progressBarStyleSmall:小的樣式(圓型)
ProgressBar的主要方法如下所示:
public int synchronized getMax() // 獲得進(jìn)度條的最大值 public synchronized int getProgress () // 獲得進(jìn)度值 public synchronized int getSecondaryProgress () // 獲得第二個(gè)進(jìn)度條的進(jìn)度 // 設(shè)置主進(jìn)度條的進(jìn)度和第二個(gè)進(jìn)度條的進(jìn)度 public void synchronized setProgress(int progress) public void synchronized setSecondaryProgress(int secondaryProgress) // “增量”方 式設(shè)置主進(jìn)度條的進(jìn)度和第二個(gè)進(jìn)度條的進(jìn)度 public final synchronized void incrementProgressBy (int diff) public final synchronized void incrementSecondaryProgressBy (int diff)
以上的幾個(gè)方法,只對(duì)水平ProgressBar有效,顯示效果實(shí)際是當(dāng)前值和最大值的一個(gè)比例。ProgressBar比較特殊的地方是這個(gè)類還支持第二個(gè)進(jìn)度條。
參考示例程序:1.Incremetal和2.Smoth(ApiDemo=>Views=>ProgressBar)
源代碼:
com/example/android/apis/view/ProgressBar1.java com/example/android/apis/view/ProgressBar2.java
布局文件:progressbar_1.xml和progressbar_2.xml
1.Incremetal和2.Smoth的運(yùn)行效果如圖3-7所示。
ProgressBar1的布局文件progressbar_1.xml的主要內(nèi)容如下所示:
<ProgressBar android:id="@+id/progress_horizontal" style="?android:attr/progressBarStyleHorizontal" android:layout_width="200dip" android:layout_height="wrap_content" android:max="100" android:progress="50" android:secondaryProgress="75" />

圖3-7 ProgressBar程序的運(yùn)行效果(左:ProgressBar1;右:ProgressBar2)
進(jìn)度條的形態(tài)使用XML屬性表示,progressBarStyleHorizontal表示使用水平模式的進(jìn)度條。android:max表示最大數(shù)值,android:progress和android:secondaryProgress分別表示主進(jìn)度條和第二個(gè)進(jìn)度條的當(dāng)前數(shù)值。
進(jìn)度條的進(jìn)度可以根據(jù)按鈕控制,其中一個(gè)按鈕的處理如下所示:
Button button = (Button) findViewById(R.id.increase); button.setOnClickListener(new Button.OnClickListener() { public void onClick(View v) { progressHorizontal.incrementProgressBy(1); // 設(shè)置進(jìn)度條的進(jìn)度 setProgress(100 * progressHorizontal.getProgress()); } });
這里調(diào)用的progressHorizontal是ProgressBar類的一個(gè)實(shí)例,此處調(diào)用incrementProgressBy()根據(jù)相對(duì)值調(diào)整進(jìn)度,與之類似,進(jìn)度條中的第二個(gè)進(jìn)度使用incrementSecondaryProgressBy()方法進(jìn)行調(diào)整。
布局文件progressbar_2.xml的主要內(nèi)容如下所示:
<ProgressBar android:id="@+android:id/progress_large" style="?android:attr/progressBarStyleLarge" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ProgressBar android:id="@+android:id/progress" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ProgressBar android:id="@+android:id/progress_small" style="?android:attr/progressBarStyleSmall" android:layout_width="wrap_content" android:layout_height="wrap_content" /> <ProgressBar android:id="@+android:id/progress_small_title" style="?android:attr/progressBarStyleSmallTitle" android:layout_width="wrap_content" android:layout_height="wrap_content" />
此處通過(guò)style設(shè)置了進(jìn)度條的不同樣式,第一個(gè)的樣式為大,第二個(gè)為默認(rèn)樣式,第三個(gè)、第四個(gè)的樣式為小。這些內(nèi)容使用的都是圓型進(jìn)度條。
2.AbsSeekBar及其繼承者
AbsSeekBar是ProgressBar的一個(gè)主要繼承者,這是一個(gè)抽象類,這也是其名稱中Abs的含義。
AbsSeekBar的主要擴(kuò)展是可以設(shè)置其中的內(nèi)容,幾個(gè)相關(guān)的方法如下所示:
public void setThumb(Drawable thumb) // 設(shè)置其中的內(nèi)容 public void setThumbOffset(int thumbOffset) // 設(shè)置內(nèi)容的偏移量 public int getThumbOffset() // 內(nèi)容的偏移量
由于AbsSeekBar是一個(gè)抽象類,因此這個(gè)類并不能直接使用。一般使用的是其兩個(gè)繼承者:RatingBar和SeekBar。
SeekBar具有一個(gè)XML屬性android:thumb,對(duì)應(yīng)于setThumb()的設(shè)置,在SeekBar中thumb的含義是其中的滑桿(可拖曳的小圖標(biāo))。
SeekBar具有一個(gè)設(shè)置監(jiān)聽者的方法:
public void setOnSeekBarChangeListener (SeekBar.OnSeekBarChangeListener l)
SeekBar.OnSeekBarChangeListener是一個(gè)用于監(jiān)聽的接口,包含如下幾個(gè)方法:
Public abstract void onProgressChanged(SeekBar seekBar, // 獲得進(jìn)度 int progress, boolean fromUser) Public abstract void onStartTrackingTouch(SeekBar seekBar) // 開始跟蹤觸摸 Public abstract void onStopTrackingTouch(SeekBar seekBar) // 停止跟蹤觸摸
基類ProgressBar通常用來(lái)向用戶顯示一個(gè)進(jìn)度,而SeekBar通常可以讓用戶交互式控制進(jìn)度。
RatingBar可以直接用星星的方式來(lái)表示進(jìn)度,主要有以下幾個(gè)XML屬性:android:isIndicator(是否可以被用戶控制),android:numStars(星星的數(shù)目),android:rating(初始化比例),android:stepSize(每次改變的大小)。
RatingBar具有一個(gè)設(shè)置監(jiān)聽者的方法:
public void setOnRatingBarChangeListener (RatingBar.OnRatingBarChangeListener l)
RatingBar.OnRatingBarChangeListener是一個(gè)用于監(jiān)聽的接口,包含如下方法:
public abstract void onRatingChanged (RatingBar ratingBar, float rating, boolean fromUser)
參考示例程序:Seek Bar和Ratting Bar(ApiDemo=>Views=>)
源代碼:
com/example/android/apis/view/SeekBar1.java com/example/android/apis/view/RatingBar1.java
布局文件:seekbar_1.xml和ratingbar_1.xml
Seek Bar和Ratting Bar的運(yùn)行效果如圖3-8所示。
Seek Bar的布局文件seekbar_1.xml的內(nèi)容如下所示:
<SeekBar android:id="@+id/seek"
android:layout_width="match_parent" android:layout_height="wrap_content" android:max="100" android:progress="50" android:secondaryProgress="75" />

圖3-8 Seek Bar和Ratting Bar的運(yùn)行效果
這里設(shè)置的屬性實(shí)際上都是基類ProgressBar的屬性。在實(shí)際的應(yīng)用中,SeekBar相當(dāng)于一個(gè)可以由用戶自己進(jìn)行控制的水平型的ProgressBar,并且可以通過(guò)設(shè)置監(jiān)聽者得到用戶控制的情況。
從SeekBar的運(yùn)行效果中可以看到,SeekBar的外觀和水平進(jìn)度條類似,但是進(jìn)度條中間具有一個(gè)滑桿的圖標(biāo)。這個(gè)滑桿可以被用戶拖曳,拖曳后SeekBar將自動(dòng)產(chǎn)生相應(yīng)的效果。在程序中,可以設(shè)置圖標(biāo)的外觀,也可以通過(guò)SeekBar.OnSeekBarChangeListener接口在程序中得到拖曳的進(jìn)度。
Ratting Bar的布局文件ratingbar_1.xml的主要內(nèi)容如下所示:
<RatingBar android:id="@+id/ratingbar1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:numStars="3" android:rating="2.5" /> <RatingBar android:id="@+id/ratingbar2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:numStars="5" android:rating="2.25" />
這里一共定義了4個(gè)RatingBar標(biāo)簽,前兩個(gè)分別定義了星星的數(shù)目為3和5,后兩個(gè)則將style設(shè)置為不同的樣式,因此顯示了不同的效果。
在實(shí)際的應(yīng)用中,RatingBar一般用于打分操作。
3.在對(duì)話框和標(biāo)題欄中使用進(jìn)度條
在對(duì)話框和標(biāo)題欄中使用進(jìn)度條,不需要直接使用ProgressBar類。對(duì)話框和標(biāo)題欄中的進(jìn)度條也有水平型(Horizontal)和圓型(Spinner)兩種模式,其中圓型模式又稱為不確定(Indeterminate)模式。
android.app包中的ProgressDialog是AlartDialog的繼承者,可以構(gòu)建一個(gè)直接帶有進(jìn)度條的對(duì)話框。使用ProgressDialog類,不需要AlartDialog.Builder,而是通過(guò)直接建立這個(gè)類來(lái)完成的。
ProgressDialog類具有如下方法,用于設(shè)置其樣式:
public static final int STYLE_SPINNER // 0x0,表示圓型 public static final int STYLE_HORIZONTAL // 0x1,表示水平型 public void setProgressStyle (int style) // 設(shè)置進(jìn)度條樣式 public void setIndeterminate (boolean indeterminate) //設(shè)置進(jìn)度條是否為“不確定” public boolean isIndeterminate ()
這里的“Indeterminate”含義為不確定,實(shí)際上也就是圓型的進(jìn)度條,也是STYLE_SPINNER樣式的含義;而STYLE_HORIZONTAL樣式則表示水平進(jìn)度條。除此之外,ProgressDialog類中也具有setProgress (),incrementProgressBy()等方法,含義和ProgressBar中的相同。
Activity中的幾個(gè)方法用于設(shè)置其標(biāo)題欄中的進(jìn)度條:
public final void setProgressBarIndeterminate(boolean indeterminate) public final void setProgressBarIndeterminateVisibility(boolean visible) public final void setProgressBarVisibility(boolean visible) // 設(shè)置可見性 public final void setProgress(int progress) // 設(shè)置進(jìn)度 public final void setSecondaryProgress(int secondaryProgress) // 設(shè)置第二個(gè)進(jìn)度
如果希望Activity標(biāo)題欄中具有進(jìn)度條,首先需要使用requestWindowFeature()函數(shù)申請(qǐng)相關(guān)特性,如下所示。
requestWindowFeature(Window.FEATURE_PROGRESS); // 水平型 requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); // 圓型
FEATURE_PROGRESS和FEATURE_INDETERMINATE_PROGRESS是Windows類的屬性,分別代表為水平型和圓型。
前面的ProgressBar=>1.Incremetal展示的是一個(gè)標(biāo)題欄中的水平進(jìn)度條,另外幾個(gè)對(duì)話框和標(biāo)題欄中的進(jìn)度條如圖3-9所示。

圖3-9 對(duì)話框和標(biāo)題欄中的進(jìn)度條
圖中的三個(gè)程序分別為ApiDemo中的App=>Dialog中的Progress Dialog示例和ProgressBar=>中的3.Dialog和4.In Title Bar。分別表示:對(duì)話框中的水平型進(jìn)度條,對(duì)話框中的圓型進(jìn)度條,標(biāo)題欄中的圓型進(jìn)度條。
Progress Dialog的核心內(nèi)容如下所示:
private static final int DIALOG_PROGRESS = 4; private static final int MAX_PROGRESS = 100; private ProgressDialog mProgressDialog; private int mProgress; @Override protected Dialog onCreateDialog(int id) { // ...... 省略其他內(nèi)容 case DIALOG_PROGRESS: mProgressDialog = new ProgressDialog(AlertDialogSamples.this); mProgressDialog.setIcon(R.drawable.alert_dialog_icon); mProgressDialog.setTitle(R.string.select_dialog); mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); mProgressDialog.setMax(MAX_PROGRESS); // 設(shè)置最大值 mProgressDialog.setButton(DialogInterface.BUTTON_POSITIVE, getText(R.string.alert_dialog_hide), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, getText(R.string.alert_dialog_cancel), new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int whichButton) { } }); return mProgressDialog; // 將ProgressDialog返回為Dialog類型 // ...... 省略其他內(nèi)容 }
除了按鈕、標(biāo)題等默認(rèn)內(nèi)容之外,主要設(shè)置的內(nèi)容是進(jìn)度條的樣式(使用setProgressStyle())和最大數(shù)值(使用setMax())。
在ProgressBar=>1.Incremetal示例程序中與標(biāo)題欄進(jìn)度條相關(guān)的內(nèi)容如下所示:
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_PROGRESS); setContentView(R.layout.progressbar_1); setProgressBarVisibility(true); // 設(shè)置進(jìn)度條出現(xiàn) final ProgressBar progressHorizontal = (ProgressBar) findViewById(R.id.progress_horizontal); setProgress(progressHorizontal.getProgress() * 100); setSecondaryProgress(progressHorizontal.getSecondaryProgress() * 100); }
這里使用requestWindowFeature(Window.FEATURE_PROGRESS)來(lái)實(shí)現(xiàn)了將進(jìn)度條設(shè)置到標(biāo)題欄當(dāng)中。
3.1.5 繼承View實(shí)現(xiàn)自定義控件
除了Android系統(tǒng)的預(yù)定義控件之外,在程序中還可以實(shí)現(xiàn)自定義控件。自定義控件一般可以基于所有控件類的基類Vi e w來(lái)實(shí)現(xiàn),它也可以具有公開或者保護(hù)的屬性、方法,還可以具有自己的XML屬性。在布局文件中,自定義控件的使用方法和預(yù)定義的控件也是類似的。
自定義控件可以在本應(yīng)用程序包中實(shí)現(xiàn)功能的復(fù)用,但是不能跨應(yīng)用程序包使用。也就是說(shuō)A包(Apk)中的自定義控件,不能在B包中使用。
實(shí)現(xiàn)自定義的View,繼承View并重新實(shí)現(xiàn)其中的方法是關(guān)鍵。
View具有以下三個(gè)構(gòu)造函數(shù),分別用于在不同場(chǎng)合產(chǎn)生View:
public View(Context context) // 簡(jiǎn)單構(gòu)造 public View(Context context, AttributeSet attrs) // 帶有屬性 public View(Context context, AttributeSet attrs, int defStyle) // 帶屬性和默認(rèn)值
View類中的一系列方法(protected類型的居多),用于View的運(yùn)行周期內(nèi)的特定時(shí)刻調(diào)用。通過(guò)定制實(shí)現(xiàn)這些方法,可以定制Vi e w行為。
Vi e w在創(chuàng)建結(jié)束的時(shí)候方法如下所示:
protected void onFinishInflate()
onFinishInflate()方法將在布局XML文件Inflate完成的時(shí)刻被調(diào)用。Vi e w中與布局相關(guān)的幾個(gè)方法如下所示:
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) protected void onLayout (boolean changed, int left, int top, int right, int bottom) protected void onSizeChanged (int w, int h, int oldw, int oldh)
onMeasure()用于確認(rèn)View及其孩子要求的尺寸,onLayout()用于在View完成給它的孩子們分配好尺寸和位置的時(shí)刻調(diào)用,onSizeChanged()用于在View的尺寸發(fā)生變化的時(shí)刻調(diào)用。
Vi e w在繪制時(shí)的方法如下所示:
protected void onDraw (Canvas canvas)
onDraw()是View類的核心方法,在繪制的時(shí)候被調(diào)用,這是一個(gè)用于確定View外觀的關(guān)鍵方法。
一般情況下,在一個(gè)控件的構(gòu)建過(guò)程中,依次將調(diào)用onFinishInflate()、onMeasure()、onLayout()和onDraw()方法。
Vi e w中幾個(gè)設(shè)備相關(guān)事件處理方法如下所示:
public boolean onKeyDown (int keyCode, KeyEvent event) // 按鍵按下事件 public boolean onKeyUp (int keyCode, KeyEvent event) // 按鍵抬起事件 public boolean onTouchEvent (MotionEvent event) // 觸摸屏事件 public boolean onTrackballEvent (MotionEvent event) // 軌跡球事件
Vi e w中與聚焦相關(guān)的方法如下所示:
public void onWindowFocusChanged (boolean hasWindowFocus) protected void onFocusChanged (boolean gainFocus, int direction, Rect previouslyFocusedRect)
onWindowFocusChanged()將在包含當(dāng)前控件的窗口聚焦改變的情況下被調(diào)用,onFocusChanged()將在當(dāng)前控件的聚焦發(fā)生變化的時(shí)候被調(diào)用。
與聯(lián)系到窗口相關(guān)的方法如下所示:
protected void onAttachedToWindow() protected void onDetachedFromWindow() protected void onWindowVisibilityChanged(int visibility)
onAttachedToWindow()和onDetachedFromWindow()只有在View包含特定繪制功能的時(shí)候才被調(diào)用(前者將在onDraw()之前被調(diào)用);onWindowVisibilityChanged()將在包含View的窗口的可見性發(fā)生變化的時(shí)候被調(diào)用。
通過(guò)繼承Vi e w實(shí)現(xiàn)一個(gè)自定義控件的時(shí)候,各個(gè)方法只有在需要的時(shí)候才需要被重新實(shí)現(xiàn),基本的實(shí)現(xiàn)是實(shí)現(xiàn)onDraw()方法完成繪制。
基于Vi e w自定義控件的實(shí)現(xiàn)可以分成如下三個(gè)層次。
第一層次:自定義控件可在程序中復(fù)用。
通過(guò)實(shí)現(xiàn)View的onDraw()方法,在Java代碼中可以使用new構(gòu)建View。
第二層次:在布局文件中使用控件。
進(jìn)一步實(shí)現(xiàn)View帶有屬性的構(gòu)造方法,可以在布局文件中定義View。
第三層次:自定義控件的屬性。
使用XML文件描述自定義屬性,在代碼中解析自定義屬性,在布局文件中增加名稱空間以使用自定義屬性。
參考示例程序?yàn)椋篊ustomView(ApiDemo=>Views)
源代碼:
com/example/android/apis/view/CustomView1.java com/example/android/apis/view/LabelView.java
布局文件:custom_view_1.xml
CustomView程序的運(yùn)行效果如圖3-10所示。

圖3-10 CustomView程序
3行的內(nèi)容均為L(zhǎng)abelView
第1行的內(nèi)容:文本為"Red"
第2行的內(nèi)容:文本為"Blue",文本大小為20dp
第3行的內(nèi)容:文本為"Green",文本顏色為白色
這個(gè)程序的三個(gè)“文字標(biāo)簽”實(shí)際上是一個(gè)自定義控件的三個(gè)實(shí)例,這個(gè)自定義控件就是由LabelView.java實(shí)現(xiàn)的LabelView,CustomView1.java是簡(jiǎn)單使用這個(gè)控件的Activity。
LabView.java實(shí)現(xiàn)的基本結(jié)構(gòu)如下所示:
import android.view.View; public class LabelView extends View { // 與View類繼承關(guān)系 public LabelView(Context context) { // View的構(gòu)造函數(shù) super(context); // 調(diào)用基類的方法 initLabelView(); } // ...... 省略部分內(nèi)容 protected void onDraw(Canvas canvas) { // View繪制方法 super.onDraw(canvas); canvas.drawText(mText, getPaddingLeft(), getPaddingTop() - mAscent, mTextPaint); } }
從最基本的角度,以上程序?qū)崿F(xiàn)View的構(gòu)造函數(shù)和onDraw()方法也就是實(shí)現(xiàn)了自定義控件的第一個(gè)層次。在這種情況下實(shí)現(xiàn)的控件只能在Java代碼中,使用new新建出來(lái)使用,但是還不能在布局文件中使用。
CustomView1中使用的布局文件custom_view_1.xml如下所示:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="wrap_content"> <com.example.android.apis.view.LabelView android:background="@drawable/red" android:layout_width="match_parent" android:layout_height="wrap_content" app:text="Red"/> <com.example.android.apis.view.LabelView android:background="@drawable/blue" android:layout_width="match_parent" android:layout_height="wrap_content" app:text="Blue" app:textSize="20dp"/> <com.example.android.apis.view.LabelView android:background="@drawable/green" android:layout_width="match_parent" android:layout_height="wrap_content" app:text="Green" app:textColor="#ffffffff" /> </LinearLayout>
這里使用的標(biāo)簽com.example.android.apis.view.LabelView也就是在LabelView代碼中自己實(shí)現(xiàn)的控件。在布局文件使用這種自定義控件,需要使用類的全路徑。
之所以能在布局文件中使用這個(gè)自定義控件,必須實(shí)現(xiàn)Vi e w的第二個(gè)構(gòu)造函數(shù),也就是含有Context和AttributeSet類型參數(shù)的構(gòu)造函數(shù)。
LabelView實(shí)現(xiàn)的方法如下所示:
public LabelView(Context context, AttributeSet attrs) {
super(context, attrs); initLabelView(); // 以上的內(nèi)容和第一個(gè)構(gòu)造函數(shù)相同,以下的內(nèi)容為屬性(Attributes)解析 TypedArray a = context.obtainStyledAttributes(attrs, // 獲得屬性組 R.styleable.LabelView); CharSequence s = a.getString(R.styleable.LabelView_text); if (s != null) { setText(s.toString()); // 設(shè)置文本 } setTextColor(a.getColor(R.styleable.LabelView_textColor, 0xFF000000)); int textSize = a.getDimensionPixelOffset(R.styleable.LabelView_textSize, 0); if (textSize > 0) { setTextSize(textSize); // 設(shè)置文本大小 } a.recycle(); }
Android從布局文件(XML)中解析建立一個(gè)View的時(shí)候,需要調(diào)用如上類型的構(gòu)造函數(shù),因此只要在布局文件中使用自定義控件,這個(gè)構(gòu)造函數(shù)就必須實(shí)現(xiàn)。到這個(gè)階段相當(dāng)于實(shí)現(xiàn)了自定義控件的第二個(gè)層次。
提示:只要在布局文件中使用控件,View(Context, AttributeSet)類型的構(gòu)造函數(shù)就必須實(shí)現(xiàn),無(wú)論有沒(méi)有自定義屬性。當(dāng)然,在沒(méi)有自定義屬性的情況下,不需要對(duì)AttributeSet類型參數(shù)進(jìn)行解析。
以上構(gòu)造函數(shù)中的AttributeSet類型的參數(shù),實(shí)際上是布局文件在被解析之后,被解析器傳入的參數(shù)。處理自定義屬性就是實(shí)現(xiàn)自定義控件的第三個(gè)層次。
在上述的布局文件中,有app:text,app:textSize和app:textColor三個(gè)自定義屬性,因此可以看到第二個(gè)文本標(biāo)簽的大小不同,第三個(gè)文本標(biāo)簽的顏色不同。
在LabelView的第二個(gè)構(gòu)造函數(shù)中,通過(guò)context.obtainStyledAttributes()對(duì)這幾個(gè)屬性進(jìn)行解析。R.styleable中的LabelView表示的是一個(gè)整數(shù)類型的數(shù)組,LabelView_text,LabelView_textSize和LabelView_textColor分別代表三個(gè)整數(shù),它們對(duì)應(yīng)于布局文件中的定義。
之所以能在代碼中找到這樣幾個(gè)屬性,實(shí)際上需要在res/values/的attrs.xml文件中進(jìn)行定義,內(nèi)容如下所示:
<declare-styleable name="LabelView"> <attr name="text" format="string" /> <attr name="textColor" format="color" /> <attr name="textSize" format="dimension" /> </declare-styleable>
根據(jù)以上的實(shí)現(xiàn),將生成R.styleable中的整型數(shù)組和整型值,這樣的幾個(gè)數(shù)值由此可以在布局文件中被引用。
另一方面,在布局文件中使用這些自定義屬性,還需要增加命名空間(Name Space),custom_view_1.xml的開頭位置有如下描述。
xmlns:app="http://schemas.android.com/apk/res/com.example.android.apis"
此處表示所指定的XML命名空間(xmlns)中的app為當(dāng)前類的路徑,因此在這個(gè)布局文件中可以使用attrs.xml文件中定義的屬性。
至此,LabelView實(shí)現(xiàn)了自定義控件的第三個(gè)層次,這個(gè)類實(shí)際上就是一個(gè)簡(jiǎn)化版的TextView。從繪制的角度,實(shí)現(xiàn)的內(nèi)容較少,更復(fù)雜的實(shí)現(xiàn)需要在onDraw()中完成。
作為公共的屬性,LabelView在實(shí)現(xiàn)上也具有公共的函數(shù)來(lái)設(shè)置這幾個(gè)屬性(文本、文本顏色、文本大小)。方法如下所示:
public void setText(String text) { mText = text; requestLayout(); // 需要重新刷新布局 invalidate(); // 迫使控件重新繪制 } public void setTextSize(int size) { mTextPaint.setTextSize(size); requestLayout(); // 需要重新刷新布局 invalidate(); // 迫使控件重新繪制 } public void setTextColor(int color) { mTextPaint.setColor(color); invalidate(); // 迫使控件重新繪制 }
以上的幾個(gè)函數(shù)和幾個(gè)XML中的屬性是對(duì)應(yīng)的,如果沒(méi)有它們,這些屬性在指定后,就不能再通過(guò)Java源代碼調(diào)用改變。以上的幾個(gè)方法其實(shí)更新的是幾個(gè)類的變量,通過(guò)調(diào)用invalidate()促使onDraw()函數(shù)被調(diào)用,進(jìn)行重新繪制。
提示:setText() 和setTextSize() 調(diào)用requestLayout()表示需要更新View樹,setTextColor() 不需要這個(gè)調(diào)用,這是因?yàn)槲谋竞臀谋镜淖兓绊懗叽纾穷伾淖兓粫?huì)影響到尺寸。
3.1.6 繼承控件實(shí)現(xiàn)自定義控件
在自定義控件的實(shí)現(xiàn)過(guò)程中,大多數(shù)情況是繼承控件類的基類Vi e w。在某些情況下,也可以實(shí)現(xiàn)一個(gè)預(yù)定義的控件來(lái)實(shí)現(xiàn)。這樣做的主要目的是復(fù)用預(yù)定義控件中的功能,對(duì)其進(jìn)行配置和定制。
通過(guò)繼承一個(gè)控件來(lái)實(shí)現(xiàn)自定義控件的方法,需要根據(jù)具體控件的情況來(lái)確定哪些內(nèi)容需要重新實(shí)現(xiàn)。
一般情況下,繼承控件的自定義控件有幾個(gè)方面的內(nèi)容:
繪制,同View中的onDraw()方法;
XML屬性的處理,可能包括XML屬性的重新解釋;
調(diào)用父類的方法。如果某個(gè)控件適合被繼承,會(huì)提供相應(yīng)的方法幫助繼承者復(fù)用這個(gè)類的功能。
AttributeSet類中的以下幾個(gè)方法用于遍歷其中的各個(gè)屬性,如下所示:
public abstract int getAttributeCount () // 屬性的數(shù)目 public abstract String getAttributeName (int index) // 屬性的名字
public abstract String getAttributeValue (int index) // 屬性的數(shù)值
根據(jù)以上的幾個(gè)方法,不需要得到各個(gè)屬性常量,而是可以根據(jù)在布局文件中書寫的表示屬性和屬性值的字符串來(lái)完成解析。相對(duì)來(lái)說(shuō),這種方法比根據(jù)整數(shù)值得到屬性的方法更為直接。
以下的實(shí)例程序是一個(gè)通過(guò)繼承TextView所實(shí)現(xiàn)的自定義控件,其中復(fù)用了TextView的主要功能,并且定制了其中的一些內(nèi)容。
程序的運(yùn)行效果如圖3-11所示。

圖3-11 繼承TextView所實(shí)現(xiàn)的自定義控件
第1行的內(nèi)容:文本內(nèi)容為“Text 1”,文本顏色為紅
第2行的內(nèi)容:文本內(nèi)容為“Text 2”,文本顏色為綠
第3行的內(nèi)容:文本內(nèi)容為“Text 3”,文本顏色為藍(lán)
第4行的內(nèi)容:文本內(nèi)容無(wú)設(shè)置,文本顏色為白(被截獲更改)
第5行的內(nèi)容:文本內(nèi)容無(wú)設(shè)置,文本顏色為默認(rèn)
程序中自上而下的5個(gè)內(nèi)容實(shí)際上就是由繼承TextView所實(shí)現(xiàn)的自定義控件的5個(gè)實(shí)例。程序所使用的布局文件如下所示:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/screen" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical"> <com.android.screenui.CustomTextView android:id="@+id/text1" android:layout_width="240sp" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#ffff0000" android:text="@string/text1"/> <com.android.screenui.CustomTextView android:id="@+id/text2" android:layout_width="240sp" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#ff00ff00" android:text="@string/text2"/> <com.android.screenui.CustomTextView android:id="@+id/text3" android:layout_width="240sp" android:layout_height="wrap_content" android:layout_gravity="center" android:textColor="#ff0000ff" android:text="@string/text3"/> <com.android.screenui.CustomTextView android:id="@+id/text4" android:layout_width="240sp" android:layout_height="wrap_content" android:textColor="#ffffffff" android:layout_gravity="center" /> <com.android.screenui.CustomTextView android:id="@+id/text5" android:layout_width="240sp" android:layout_height="wrap_content" android:layout_gravity="center" /> </LinearLayout>
由于這里的自定義控件CustomTextView繼承自TextView,因此在布局文件中使用的android:text和android:textColor等內(nèi)容屬性都是來(lái)自于TextView的XML屬性。屬性的使用方面和TextView是一樣的,主要的差別是在Java代碼中對(duì)屬性的解析不同。
本例的Java文件實(shí)現(xiàn)了自定義控件CustomTextView,內(nèi)容如下所示:
import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.util.AttributeSet; import android.view.View; import android.widget.TextView; import android.R.styleable; import android.util.Log; public class CustomTextView extends TextView { // 繼承TextView private static final String TAG = "CustomTextView"; private static final String DEFAULT_TEXT = "Default Text"; private Paint mPaint; private String mText; public CustomTextView(Context context) { // 構(gòu)造函數(shù) super(context); initView(); // 調(diào)用進(jìn)行View的初始化 } public CustomTextView(Context context, AttributeSet attrs){ // 帶屬性的構(gòu)造函數(shù) super(context, attrs); Log.v(TAG,"CustomTextView"); for(int i = 0; i< attrs.getAttributeCount();i++){ // 遍歷控件的XML屬性 int res = attrs.getAttributeNameResource(i); String attrname = attrs.getAttributeName(i); // 獲得屬性的名稱 String attrvalue = attrs.getAttributeValue(i); // 獲得屬性的值 Log.v(TAG,"ATTR "+res + " - " + attrname + " - "+ attrvalue); if(attrname.equals("textColor") && attrvalue.equals("#ffffffff")){ setTextColor(0xff000000); } } initView(); // 調(diào)用進(jìn)行View的初始化 } private final void initView() { String text = getText().toString(); if(text.length() == 0){ // 設(shè)置默認(rèn)的文本 setText(DEFAULT_TEXT); } setTextSize(24); // 設(shè)置文本尺寸 Drawable dr = getContext().getResources() // 獲得背景圖片 .getDrawable(R.drawable.background); setBackgroundDrawable(dr); // 設(shè)置背景 mPaint = new Paint(); // 設(shè)置自定義繪制的內(nèi)容 mPaint.setColor(getCurrentTextColor()); // 設(shè)置附加內(nèi)容的繪制信息 mPaint.setTextSize(30); mPaint.setTextAlign(Paint.Align.RIGHT); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 默認(rèn)的繪制 canvas.drawText("@",canvas.getClipBounds().right, // 自定義的附加繪制
canvas.getClipBounds().bottom,mPaint); } }
這是繼承實(shí)現(xiàn)的CustomTextView,主要自定義了以下幾個(gè)方面的內(nèi)容:
指定控件的背景;
指定文本的大小;
在沒(méi)有文本的情況下,指定默認(rèn)的文本;
自定義繪制一個(gè)附加的內(nèi)容。
在程序中,調(diào)用了setBackgroundDrawable()方法,這實(shí)際上是View中的方法,強(qiáng)行指定了這個(gè)控件的背景,因此其程序的背景和基類TextView不同。
程序調(diào)用了基類TextView的setTextSize()方法,設(shè)置了默認(rèn)的文本尺寸。
當(dāng)文本的顏色(android:textColor)為白色(#ffffffff)時(shí),調(diào)用TextView的setTextColor()將文本設(shè)置為黑色。
調(diào)用TextView的getText()方法獲得程序中設(shè)置的文本,當(dāng)文本為空的時(shí)候,TextView的setText()將其設(shè)置為默認(rèn)的文本。
由于以上的處理,前三行內(nèi)容使用了設(shè)置的文本內(nèi)容,顏色分別為紅、綠和藍(lán);第四行和第五行內(nèi)容由于沒(méi)有設(shè)置文本,使用了默認(rèn)文本("Default Text");第四行文本的顏色由于設(shè)置文本顏色為白色,在程序中被轉(zhuǎn)換成了黑色;第五行沒(méi)有設(shè)置文本顏色,實(shí)際上使用了TextView的默認(rèn)顏色。
以上設(shè)置的過(guò)程,大都使用了基類的方法,這些設(shè)置的內(nèi)容在onDraw()情況下對(duì)設(shè)置的內(nèi)容進(jìn)行了繪制。以上的程序?qū)嶋H上是截獲并處理了部分XML屬性。
提示:由于TextView是一個(gè)適合繼承的控件類,因此其很多屬性具有g(shù)et和set方法用于獲得和重新設(shè)置數(shù)值,設(shè)置的內(nèi)容可以在onDraw()的繪制中得到體現(xiàn)。
如果自定義控件僅僅處理XML屬性的處理,onDraw()方法都是不需要實(shí)現(xiàn)的。上述程序的onDraw()實(shí)現(xiàn)的主要目標(biāo)是為了繪制一個(gè)附加的字符(@),并且這個(gè)字符的顏色和文本的顏色相同。
- 圖像目標(biāo)跟蹤技術(shù)
- 現(xiàn)代數(shù)據(jù)通信技術(shù)與應(yīng)用
- cdma2000 1x/EV-DO通信網(wǎng)絡(luò)規(guī)劃與設(shè)計(jì)
- Android 10 Kotlin編程通俗演義
- 教你檢修液晶彩色電視機(jī)
- LTE無(wú)線網(wǎng)絡(luò)優(yōu)化實(shí)踐
- 數(shù)字視聽產(chǎn)品原理與維修
- 交換機(jī)/路由器及其配置
- 5G系統(tǒng)觀:從R15到R18的演進(jìn)之路
- 信息論與編碼原理
- 人工智能超密集移動(dòng)通信系統(tǒng)
- 被動(dòng)雷達(dá)寬帶數(shù)字接收機(jī)技術(shù)
- 聲紋技術(shù):從核心算法到工程實(shí)踐
- 數(shù)字信號(hào)處理
- 移動(dòng)終端安全關(guān)鍵技術(shù)與應(yīng)用分析