第1章 用戶界面和佈局
應用程序的用戶界面就是用戶能看到並可以與它交互的任何東西。Android提供多種預置的UI組件,如結構化佈局對象和允許你爲應用程序創建圖形用戶界面的UI控件。Android也會爲特殊的接口提供其他UI模塊,如對話框,通知和菜單。在一個Android應用中,所有用戶界面元素都是由View和ViewGroup對象創建的。View 是一種可以在屏幕上繪製某種畫面並且可以與用戶互動的對象。ViewGroup對象則是爲了定義佈局的接口而保存其他View(和ViewGroup)對象。Android提供一個View和ViewGroup子類的集合,這個集合能爲你提供相同的輸入控制(例如按鈕和文本框)和各種各樣的佈局模式(例如一個線性或者相對佈局)
1.1 用戶界面佈局
對應用程序的每個組件來說,用戶界面都是由View對象和ViewGroup對象的層次結構來定義的,如圖1-1所示。每一個view group都是用來組織子view的一個不可見容器,然而子 views可能是輸入控制UI或者繪製UI某些部分的其他widgets。這個樹形結構可以根據你的需要簡單化或複雜化。(但是對於性能來說簡單最好)
圖1-1 定義UI佈局的view層次結構圖
爲了聲明你的佈局,你可以在代碼中實例化View對象然後啓動構建樹,但定義佈局最容易、最有效的方法是利用XML文件。XML文件可以爲佈局提供一個可讀結構,這與HTML文件相似。一個View的XML節點名稱與它代表的Android類相對應。所以UI裏的一個<TextView>節點會創建一個TextView widget,一個<LinearLayout>節點會創建一個LinearLayout view group。例如,包含一個文本視圖和一個按鈕的簡單縱向佈局,正如代碼清單1-1所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am a TextView" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="I am a Button" />
</LinearLayout>
代碼清單1-1
當你在應用程序中載入一個佈局資源時,Android會初始化每個進入到正運行對象的佈局節點,這時你可以用它來定義附加行爲,查詢對象狀態,或修改佈局。
1.2 用戶界面組件
你不需要用View和ViewGroup對象來構建你所有的UI。Android系統提供了幾個標準UI佈局的應用程序組件,因此你只需要定義內容。這些應用程序組件都有一組唯一的API,如Action Bar,Dialogs,和Status Notifications,這些都會在他們各自的文檔中被一一介紹。
1.3 佈局(Layout)
佈局爲用戶界面定義了一個可視化結構。可以用兩種方式聲明一個佈局:
◆在XML中聲明UI元素
Android提供了簡單的XML元素,它的元素名字與View類以及子類對應,就像佈局和widgets一樣。
◆在運行時動態實例化佈局元素
使用代碼創建佈局元素(並且操作他們的屬性)
Android提供了非常靈活的方法來聲明和管理應用UI。例如,可以先在XML中聲明默認佈局,屏幕元素會根據它們的屬性顯示。接下來可以在應用中添加代碼來修改屏幕對象的狀態,也可以在運行時修改在XML中聲明的對象。 在XML中聲明UI的好處是,可以更好地區分顯示和控制這些行爲的代碼。UI描述與應用代碼無關,也就是說可以修改和調整XML中的UI佈局但是不用修改源java代碼。例如,能夠爲不同的屏幕目標、不用的設備屏幕大小、不同的語言創建不同的XML佈局文件。另外,在XML中聲明佈局使得UI更容易可視化,這樣更容易調試界面。本章主要用於教會你如何在xml中聲明佈局。如果你對運行時動態創建佈局感興趣,那麼請參考viewgroup以及view類說明。 一般來說,xml聲明UI元素的詞彙和類的命名以及方法名密切相關,元素根據類名、屬性名根據方法名來命名。實際上,能猜到什麼XML屬性對應一個類的方法, 或者能夠猜到哪個類對應給定的XML元素,這往往是直接的對應。但是,注意並不是所有的詞彙都是等同的。在某些情況下,有的命名有些許不同。例如,EditText元素有個text屬性對應EditText.setText()方法。
1.4 寫XML
使用android的XML詞彙,可以快速的設計UI佈局和它們包含的屏幕元素。跟創建web頁面使用html類似(一系列的嵌套)。 每一個佈局文件必須包含一個根節點。這個根節點必須是一個View 或者ViewGroup對象。一旦你定義了根節點,可以添加任意的佈局對象或者widgets作爲子元素,逐步構建一個View層次佈局。例如,這是一個XML佈局文件使用了縱向的線性佈局(LinearLayout)來排列一個TextView和Button,如代碼清單1-2所示:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a TextView" />
<Button android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello, I am a Button" />
</LinearLayout>
代碼清單1-2
這個文件應該是在當前android工程/res/layout/目錄下.xml的擴展名來保存這個文件,這樣纔會正確編譯。我們接下來會討論這裏顯示的每個屬性。
1.5 加載XML資源
當編譯應用程序的時候,每一個XML佈局文件都被編譯到view資源中。應該在Activity.onCreate()回調方法中實現加載佈局資源。通過調用setContentView()來設置佈局資源(按照R.layout.layout_file_name的格式)。例如,如果你的XML佈局文件被保存爲main_layout.xml,那麼可以在activity中這樣加載,如代碼清單1-3所示:
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main_layout); }
代碼清單1-3
1.6 屬性
每一個View和ViewGroup對象支持他們各自的XML屬性。有些屬性是特定的View對象(例如,TextView支持textSize屬性)但是隻要繼承這個類,這些屬性也可以在其他地方使用。有些屬性是對所有的的View對象都適用的,因爲它們是從最原始抽象的View類繼承下來的(像id屬性)。其它的屬性被認爲是佈局參數,比較容易的理解是這樣的,ViewGroup代表佈局,View代表佈局中的元素(比如Button之類),但是ViewGroup是繼承自View的,所以你可以理解爲,一切皆View。
1.6.1 ID
每一個View對象都可能有一個int型的ID和它相關,這是在樹中View對象的唯一標識。當程序編譯完,這個ID就是一個引用,但是ID屬性在XML佈局文件裏面是通過string類型賦值的。下面讓我們看一下XML中如何定義一個View對象的ID:
android:id="@+id/my_button"
這個@符號在字符串開頭表明xml解析器會解析和擴展剩餘的ID字符串,並把它定義爲ID資源。“+”表示這是一個新的資源名字,要創建並且增加的我們的資源中(在R.java文件裏)。Android framework層也提供了一部分ID資源。如果直接使用android 資源ID的話,你不需要”+”,但是要加上android包名命名空間,如下所示:
android:id="@android:id/empty"
Android的包命名空間中,我們現在引用android.R資源類的ID,而不是本地的資源類中引用。 爲了創建views,並在應用中使用,常用的流程如下:
1.在佈局文件中創建view/widget,併爲他們分配一個唯一的ID
<Button android:id="@+id/my_button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="@string/my_button_text"/>
2.接下來在代碼中創建view對象實例,從佈局中找到它(一般在OnCreate()方法中)
Button myButton = (Button) findViewById(R.id.my_button);
如果是在相對佈局(RelativeLayout)中的話定義好ID是非常重要的。因爲它們的佈局定義就是需要依賴ID。一個ID在整個樹中不一定要求唯一,但是你搜索的部分樹應該是唯一的(經常是整個樹,所以最好的辦法是在整個樹中是唯一的)儘量讓你的ID全局唯一。
1.6.1佈局參數
XML佈局屬性命名爲layout_something 的文件,在其所在的視圖組中定義了合適的view的佈局參數。
每一個ViewGroup類都是繼承了Viewgroup.LayoutParams。一般子類使用父類的佈局參數,剛開始也許有些難理解,讓我們看下圖1-2::
圖1-2 帶佈局參數的View層級
每一個佈局參數子類擁有自己的語法賦值。每一個子元素必須根據父元素來定義恰當的佈局參數,儘管父元素也爲它的子元素定義了不同的佈局參數。所有的ViewGroup包含寬度和高度,每一個View都要定義這兩個屬性。許多佈局參數也包含可選邊距。寬度和高度可以使用精確的測量工具,但一般不這麼做。往往,會使用這些常量來設置寬度和高度。
◆wrap_content會根據內容自動調整視圖到合適的大小(就是內容多大,寬高就多大)。
◆fill_parent會告訴你的View變成和父ViewGroup的範圍一樣大。
一般說來,不推薦使用用絕對單位像是像素來指定佈局的寬度和高度。而使用相對的測量工具像是與密度無關的單位(dp),wrap_content,或者fill_parent是一種較好的方法,因爲它能夠保證您的應用程序在不同屏幕大小的設備上都能夠正常顯示。
1.7 佈局位置
一個View的幾何形狀是一個矩形。一個View有一個座標、用left和top參數,和兩個表示寬高尺寸的參數表示,你可以理解爲一個左上角的點,和根據這個點的寬高就是一個矩形了。座標和尺寸的單位是像素。調用getLeft()和getTop()方法能夠得到視圖的座標。前者返回left、或者X軸的座標。後者返回top,或者Y軸座標。這些方法都返回的View座標是相對於父視圖的座標。比如,如果getLeft()返回20,這表示當前視圖在父視圖左側邊緣向右20個像素的地方。另外,提供了許多便利的方法避免了不必要的計算,像是getRight()和getBottom()這些方法返回視圖矩形的右側和底側邊緣。例如,調用getRight()和進行這個計算是一樣的:getLeft()+getWidth()。
1.8 大小,填充(padding)和邊距(Margins)
View大小是使用寬度和高度表示的。一個View實際上有兩對寬高值。第一對是測量得到的寬度和高度。這些尺寸定義了一個View想在父類中佔多大。測量尺寸能夠通過調用getMeasuredWidth()和getMeasuredHeight()得到。第二對被簡單的成爲寬度和高度,有時成爲繪製的寬度和高度。這些尺寸定義了在繪製時和佈局後視圖在屏幕上的實際大小。它或許和測量得到的寬度和高度不同。這些參數能夠通過調用getWidth()和getHeight()得到。
爲了測量尺寸,需要考慮padding。padding 表示View的左側、上側、右側和下側部分。padding能夠用來偏移視圖中的內容、通過指定一定數量的像素。比如,左側padding是2,會讓視圖內容偏移視圖左邊緣2個像素。padding能夠通過使用i setPadding(int,int,int,int)方法進行設置,調用getPaddingLeft(),getPaddingTop(),getPaddingRight()和getPaddingBottom()進行查詢。儘管View能夠定義padding,它不提供任何margins的支持。只有view group提供了這樣的支持。
1.9 一般佈局
ViewGroup的每個子類提供了唯一的方法來顯示View,接下來是在Android 平臺常用一些的佈局類型。 注意:雖然爲了UI設計,可以在一個佈局裏面放置一個或者多個佈局,但是應該力求讓佈局層次儘可能的少。這樣性能更高如果視圖層次很少會繪製的很快(一個廣度視圖層次比深度層次視圖好很多)
線性佈局:這個佈局是讓其孩子組織成一個單一的水平或垂直行。如果窗口長度超出了屏幕,它會自動創建滾動條。
相對佈局:讓你能夠指定子對象之間的相對位置(孩子A在孩子B的左側)或者和父對象之間的相對位置(和父對象頂端對齊)
頁面視圖:顯示web頁面
1.10 使用適配器構建佈局
如果佈局是動態的或者非預定義的,可以在運行時使用一個佈局子類AdapterView來填充佈局。AdapterView類的子類使用一個適配器將數據綁定到它的佈局。適配器表現爲數據源和AdapterView佈局之間的中間人-適配器檢索數據(從像是數組或者數據庫查詢這樣的數據源)並將它轉換成可以添加到AdapterView佈局視圖中的條目。通用的適配器佈局包括:
List View:顯示滾動的列的列表
Grid View:顯示滾動的網格的行和列
1.10.1用數據填充適配器佈局
可以填充一個AdapterView,如ListView和GridView,通過將AdapterView實例綁定到一個適配器上,這個適配器從外部數據源檢索數據併爲每個數據條目創建一個佈局。 Android提供了許多適配器子類用來檢索各種各樣的數據併爲AdapterView構建佈局。最常用的通用適配器是:
1. ArrayAdapter
當數據源是數組的時候,就可以使用這個適配器。默認情況下,ArrayAdapter爲每個數組元素創建一個佈局,通過在每個元素調用toString()後,將數據存放在TextView裏面。
例如,如果您想將一個字符串數組顯示在ListView中,使用構造器爲每個字符串和字符串數組指定佈局初始化一個ArrayAdapter,如代碼清單1-4所示:
ArrayAdapter adapter = new ArrayAdapter<String>(this, android.R.layout.simple_list_item_1, myStringArray);
代碼清單1-4
構造器參數:
◆應用程序Context
◆一個針對字符串數組中的字符串都有一個TextView的佈局
◆字符串數組
然後只需要在ListView上調用SetAdapter(),如代碼清單2-5所示:
ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);
代碼清單2-5
爲了調整元素外觀,你可以爲了數組中的對象重寫toString()函數。或者,爲每個元素創建非TextView的視圖(比如,想獲取每個數組元素的ImageView)擴展AdapterArray,重寫getView()方法返回想要的View類型。
2. SimpleCursorAdapter
當數據來源是Cursor時,則使用這個適配器。在使用SimpleCursorAdapter時,必須爲Cursor的每一行數據指定使用的佈局,也必須爲Cursor的每一欄指定要使用佈局中的哪個控件。比如,創建一個包含用戶名稱和電話號碼的列表。首先執行Cursor數據庫的查詢,返回的是Cursor一行用戶的信息,其中有用戶的名稱和電話號碼等信息;然後創建一個字符串數組用於指定將顯示Cursor的哪一列,創建一個integer數組爲每一列指定相應的視圖來放置數據,如代碼清單1-6所示:
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME, ContactsContract.CommonDataKinds.Phone.NUMBER}; int[] toViews = {R.id.display_name, R.id.phone_number};
代碼清單1-6
初始化SimpleCursorAdapter時,傳入的佈局參數和兩個數組對Cursor每個結果都是適用的,代碼清單1-7所示:
SimpleCursorAdapter adapter = new SimpleCursorAdapter(this, R.layout.person_name_and_number, cursor, fromColumns, toViews, 0); ListView listView = getListView(); listView.setAdapter(adapter);
代碼清單1-7
SimpleCursorAdapter接下來爲Cursor每一行使用提供的佈局來創建View,通過將每個fromColumns元素插入到指定的View。
在應用程序的生命週期中,如果適配器對應數據被改變了,應該調用notifyDataSetChanged()。這會通知相應的View數據變化了,它會自我刷新。
1.10.2處理點擊(click)事件
您可以通過實現AdapterView.OnItemClickListener接口,來處理AdapterView每個item的click事件。如代碼清單1-8所示:
private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() { public void onItemClick(AdapterView parent, View v, int position, long id) { // Do something } }; listView.setOnItemClickListener(mMessageClickedHandler);
代碼清單1-8
1.11 線性佈局
線性佈局(Linear Layout)是一個ViewGroup,它所有的子視圖都在一個方向對齊,水平或者垂直。你可以通過android:orientation 屬性來指定佈局的方向。線性佈局的所有子視圖排列都是一個靠着另一個,因此垂直列表每行僅僅有一個子視圖,不管有多寬。水平列表只能有一行的高度(最高子視圖的高度加上邊距距離)。線性佈局對於每一個子視圖涉及到邊緣在子視圖和權重(左邊或者右邊以及中間對齊)之間。
1.11.1佈局權重(Weight)
線性佈局支持給個別的子視圖設定權重,通過android:layout_weight屬性。就一個視圖在屏幕上佔多大的空間而言,這個屬性給其設定了一個重要的值。一個大的權重值,允許它擴大到填充父視圖中的任何剩餘空間。子視圖可以指定一個權重值,然後視圖組剩餘的其他的空間將會分配給其聲明權重的子視圖。默認的權重是0。例如,如果有三個文本框,其中兩個聲明的權重爲1,另外一個沒有權重,沒有權重第三個文本字段不會增加,只會佔用其內容所需的面積。其他兩個同樣的會擴大以填補剩餘的空間,在三個文本域被測量後。如果第三個字段,然後給定的權重爲2(而不是0),那麼它現在的聲明比其他的更重要,所以它得到一半2/(2+1+1)的總的剩餘空間,而前兩個平均分配剩餘的。
若在線性佈局中創建佔有相同空間的子視圖,設置每個子視圖的android:layout_height屬性值爲"0dp"(對於垂直線性佈局來說),或者設置每個子視圖的android:layout_width屬性值爲"0dp"(對於水平線性佈局來說)。然後在設置每個子視圖的android:layout_weight屬性值爲"1"。如果想更詳細的瞭解線性佈局的每個子視圖的可用屬性,可以參考LinearLayout.LayoutParams
下面讓我們來看下代碼清單1-9的實例:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:orientation="vertical" >
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/to" />
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/subject" />
<EditText
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
android:hint="@string/message" />
<Button
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_gravity="right"
android:text="@string/send" />
</LinearLayout>
代碼清單1-9
例子運行的結果如下圖1-3所示:
圖1-3 運行後的效果圖
1.12 相對佈局
RelativeLayout顧名思義,相對佈局,在這個容器內部的子元素們可以使用彼此之間的相對位置(例如,在某個視圖左邊left-of)或者和容器間的相對位置(例如,與父視圖左對齊,底部對齊或者居中等)來進行定位。
RelativeLayout(相對佈局)是一個爲用戶界面設計,非常強大的工具.因爲它可以消除嵌套視圖組,並保持你的佈局層次更簡潔,從而提高性能。如果你發現自己使用多個嵌套的LinearLayout組,您可能能夠取代單一RelativeLayout。
1.12.1定位View
相對佈局可以讓它的子View指定自己的相對於父View的位置或者視圖元素之間的相對位置(通過指定的ID)。你可以使兩個元素右邊界對齊,或者使一個View在另一個View下方, 或者使View在屏幕居中偏左等等。默認情況下,所有的子View在佈局的左上角。所以你必須通過使用佈局屬性RelativeLayout.LayoutParams中各種不同的可用屬性值來定義每個View的位置。
相對佈局View的一些可用屬性包括:
◆android:layout_alignParentTop
如果設置爲“true”,使這一View的頂部邊緣匹配父類的頂部邊緣
◆android:layout_centerVertical
如果“true”,設置此子視圖在父View中垂直居中。
◆android:layout_below
設置此視圖的上邊緣位於通過資源ID指定的View的下方。
◆android:layout_toRightOf
設置此視圖的左邊緣位於通過資源ID指定的View的右方。
這僅僅是幾個例子,所有的佈局屬性我們可以在RelativeLayout.LayoutParams中找到。
每個佈局屬性的值既可以是boolean類型的值來確定佈局相對於父佈局的位置,也可以是某個子View的ID,來指定佈局相對於這個子View的位置。
在你的xml佈局文件中,依賴於其他視圖的佈局可以在聲明的時候沒有順序。例如:
你可以聲明“View1”在“VIew2”的下方,即使View2是在視圖層次結構中最後一個被聲明的。下面的例子演示了這種情況。(這是官方的說法,但筆者遇到過的實際情況是順序是有影響的),下面讓我們看下代碼清單1-10:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:paddingLeft="16dp"
android:paddingRight="16dp" >
<EditText
android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:hint="@string/reminder" />
<Spinner
android:id="@+id/dates"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:layout_alignParentLeft="true"
android:layout_toLeftOf="@+id/times" />
<Spinner
android:id="@id/times"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:layout_alignParentRight="true" />
<Button
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_below="@id/times"
android:layout_alignParentRight="true"
android:text="@string/done" />
</RelativeLayout>
代碼清單1-10
例子運行的結果如下圖1-4所示:
圖1-4 運行後的效果圖
1.13 List View
列表視圖是一個縱向顯示滾動項的View組合。每一列的項都通過Adapter被自動插入到列表中,其中,Adapter適配器可以從數組或者數據庫查詢中提取出內容並轉化成列表View中的item。
1.13.1使用一個裝載器
使用一個CursorLoade是避免一個異步任務查詢光標Curso時阻塞程序主線程的標準途徑。當CursorLoader收到一個Cursor結果,LoaderCallbacks會收到一個對onLoadFinished()的回調,這時可以利用新的Cursor和列表視圖更新Adapter並顯示結果。
雖然CursorLoader函數在Android3.0(API級別 11)中才第一次引入,程序可以通過引入Support Library使用它們來支持運行Android 1.6及以上的設備。
要查看更多關於利用Loader異步加載數據的信息,請參看Loaders
下面的例子是把ListView作爲唯一默認佈局元素的活動ListActivity。它完成向Contacts Provider查詢姓名和電話號碼清單的功能。
爲了使用CursorLoader向列表動態加載數據,這個活動實現了LoaderCallbacks接口。如代碼清單1-11所示:
public class ListViewLoader extends ListActivity
implements LoaderManager.LoaderCallbacks<Cursor> {
SimpleCursorAdapter mAdapter;
static final String[] PROJECTION = new String[] {ContactsContract.Data._ID,
ContactsContract.Data.DISPLAY_NAME};
static final String SELECTION = "((" +
ContactsContract.Data.DISPLAY_NAME + " NOTNULL) AND (" +
ContactsContract.Data.DISPLAY_NAME + " != '' ))";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ProgressBar progressBar = new ProgressBar(this);
progressBar.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT, Gravity.CENTER));
progressBar.setIndeterminate(true);
getListView().setEmptyView(progressBar);
ViewGroup root = (ViewGroup) findViewById(android.R.id.content);
root.addView(progressBar);
String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME};
int[] toViews = {android.R.id.text1};
mAdapter = new SimpleCursorAdapter(this,
android.R.layout.simple_list_item_1, null,
fromColumns, toViews, 0);
setListAdapter(mAdapter);
getLoaderManager().initLoader(0, null, this);
}
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
return new CursorLoader(this, ContactsContract.Data.CONTENT_URI,
PROJECTION, SELECTION, null, null);
}
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
public void onLoaderReset(Loader<Cursor> loader) {
mAdapter.swapCursor(null);
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
}
}
代碼清單1-11
注意:因爲這個例子要向Contacts Provider請求查詢數據,程序需要在製作清單文件中請求READ_CONTACTS權限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
1.14 Grid View
GridView是一個在可滾動的二維網格空間中顯示item的ViewGroup。元件會使用ListAdapter自動插入網格佈局中。
在本教程中,將創建一個縮略圖網格。當一個item被選中,會彈出顯示該圖像的位置的消息框。
1. 首先創建一個名爲HelloGridView的工程
2. 找出一些將要使用的的圖像。將準備好的圖像放在工程的res/drawable/目錄下。
3. 打開 res/layout/main.xml文件,並插入以下代碼清單1-12:
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/gridview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="90dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>
代碼清單1-12
創建的GridView將填滿整個屏幕。這些屬性的含義都很明顯。更多關於屬性的信息,請參閱的GridView的參考。
4、打開 HelloGridView.java 並在其中的onCreate()函數中插入以下代碼清單1-13:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
GridView gridview = (GridView) findViewById(R.id.gridview);
gridview.setAdapter(new ImageAdapter(this));
gridview.setOnItemClickListener(new OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
Toast.makeText(HelloGridView.this, "" + position, Toast.LENGTH_SHORT).show();
}
});
}
代碼清單1-13
當main.xml文件完成內容佈局以後,GridView會被findViewById(int)方法從佈局中捕捉到。setAdapter()方法設置一個自定義的適配器(ImageAdapter)作爲被顯示在網格中的元件的源。ImageAdapter會在下一步中創建。
setOnItemClickListener()會傳遞一個新的 AdapterView.OnItemClickListener消息以便於響應網格元件被選中的事件。匿名實例定義了由onItemClick()回調函數彈出一個Toast消息框顯示所選中的網格元件的位置索引號(索引號從零開始計算。在實際程序中可以通過位置索引號獲取其全尺寸圖像以備其他用途)。
5. 創建一個擴展BaseAdapter並調用ImageAdapter的新類,如代碼清代1-14:
public class ImageAdapter extends BaseAdapter {
private Context mContext;
public ImageAdapter(Context c) {
mContext = c;
}
public int getCount() {
return mThumbIds.length;
}
public Object getItem(int position) {
return null;
}
public long getItemId(int position) {
return 0;
}
public View getView(int position, View convertView, ViewGroup parent) {
ImageView imageView;
if (convertView == null) {
imageView = new ImageView(mContext);
imageView.setLayoutParams(new GridView.LayoutParams(85, 85));
imageView.setScaleType(ImageView.ScaleType.CENTER_CROP);
imageView.setPadding(8, 8, 8, 8);
} else {
imageView = (ImageView) convertView;
}
imageView.setImageResource(mThumbIds[position]);
return imageView;
}
private Integer[] mThumbIds = {
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7,
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7,
R.drawable.sample_0, R.drawable.sample_1,
R.drawable.sample_2, R.drawable.sample_3,
R.drawable.sample_4, R.drawable.sample_5,
R.drawable.sample_6, R.drawable.sample_7
};
}
代碼清單1-14
首先,實例化一些繼承自BaseAdapter的必要函數。構造函數和getCount()不用多解釋。通常情況下,getItem(int)應該返回一個適配器中指定位置的真實對象,但是在本例中這點被忽略了。同樣的,getItemId(int)應該返回元件的真實編號,但是本例中不需要這樣。
第一個必須的方法是getView()。這個方法爲每一個加入到ImageAdapter的圖像創建一個新的View視圖。當調用它時,一個View視圖對象會被傳入並且是可重複使用的(在被調用至少一次以後),所以需要確認對象是否爲空。如果爲空,就要實例化一個ImageView並根據要呈現的圖像設置屬性參數。
setLayoutParams(ViewGroup.LayoutParams)設置視圖的高度和寬度,這樣可以確保不論原圖像的大小如何都能適當的調整大小和裁減。
setScaleType(ImageView.ScaleType)聲明瞭圖像將依照中心進行裁減(如果需要的話)。
int, int, int) setPadding(int, int, int, int)定義了各邊如何進行填充。(需要注意的是,如果圖像有不同的縱橫比,那麼當圖像不匹配ImageView給定的尺寸時,較少的填充就會導致圖像更多的裁減)。
如果傳給android.view.View, android.view.ViewGroup) getView()的View視圖不爲空,則本地的ImageView會由可重複使用的View初始化。
在getView()方法的最後,被傳入的position參數會用於從被作爲ImageView資源的mThumbIds數組中選擇圖像。
剩下的就是定義繪畫資源的mThumbIds數組。
6. 運行程序。
可以通過調整GridVie和ImageView的元素體驗其使用方法。例如,使用setAdjustViewBounds(boolean)而不使用setLayoutParams(ViewGroup.LayoutParams)。