從零開始--系統深入學習android(實踐-讓我們開始寫代碼-Android框架學習-1.用戶界面和布局)


第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)。

  FAQ群:213821767


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM