文章目錄
  1. 1. Layouts
    1. 1.1. Write the XML
    2. 1.2. Load the XML Resource
    3. 1.3. …中略…
    4. 1.4. Building Layouts with an Adapter
    5. 1.5. Filling an adapter view with data
      1. 1.5.1. ArrayAdapter
      2. 1.5.2. SimpleCursorAdapter
    6. 1.6. Handling click events
  2. 2. GridView
    1. 2.1. Example

為了看懂 GridView 所以回去看 Layouts 的 Adapter 部分
所以這篇有一部份的 Layouts + GridView

Layouts

Layout 用來定義 UI 的視覺結構,有兩種方法可以定義 Layout:

  • XML
  • 用編程的方式,讓 Layout 在執行期實現

可以兩種方式一起用,用 XML 定義 Layout 和他的特性,之後可以在執行期被修改。

Write the XML

直接寫 XML ,定義完 layout 將 XML 檔(.xml)放在 res/layout/ 路徑下,就會被正確的編譯。

Load the XML Resource

當你編譯你的 APP,每個 XML layout 檔被編譯到 View resource 中,你可以在你的 Activity.onCreate() Callback 中用程式碼讀取 layout resource。
例如你的 XML layout 儲存為 main_layout.xml,你應該要讓你的 Activity 這樣讀取:

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main_layout);
}

你的 Activity 中的 onCreate() callback 方法,會在你的 Activity 啟動時,由 Android framework 呼叫。

…中略…

目標是下面的 Adapter

Building Layouts with an Adapter

當 layout 的內容是動態的或是不能預先決定,你可以用 layout 的子類別 AdapterView 在執行期時將 View 填入 layout。
子類別 AdapterView 使用 Adapter 來結合資料到 layouts。
Adapter 是資料來源和 AdapterView layout 的中間人,Adapter 取回資料(資料來源像是 array 或是資料庫查詢)
並轉換成資料到 View 中,讓他們可以被加入到 AdapterView layout 中。

支持 Adapter 的 layout 有:
List View
顯示可以捲動的一列清單
Grid View
顯示可以捲動的行列網格

Filling an adapter view with data

你可以藉由結合 AdapterView 到一個 Adapter 來填入像是 ListView 或 GridView 的 AdapterView,他會從額外來源獲取資料並且建立一個 View。

Android 提供幾個 Adapter 子類別,來取回不同種類的資料並且結合 views 到 AdapterView。下面兩個是最常見的 Adapter:

ArrayAdapter

用這個 Adapter 可以用陣列當作資料來源。
ArrayAdapter 建立一個 view 時預設會把陣列中每一個項目呼叫 toString() 並把他們放到 TextView。

例如,如果你有一個字串陣列,你想要用 ListView來顯示他,用建構式初始化一個 ArrayAdapter:

ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);

建構式的參數是:

  • 你的 app Context
  • layout,用來容納陣列中每個字串的 TextView
  • 字串陣列

之後在你的 ListView 簡單的呼叫 setAdapter():

ListView listView = (ListView) findViewById(R.id.listview);
listView.setAdapter(adapter);

要自訂每個項目的外表,可以為你的陣列物件 override toString() 方法,
或是要為一些 TextView 以外其他種類的東西建立 view(例如想要為陣列的每個項目建立 ImageView),可以 extend ArrayAdapter 並 override getView() 來為每個項目回傳你想要的型態的 veiw。

SimpleCursorAdapter

當你的資料來自 Cursor(不是滑鼠游標,是讀取資料庫用的 android.database.Cursor)可以使用這個 Adapter。
你必須指定一個 layout 給每個 Cursor 的 Row 使用,而且每個 Cursor 的 Columns 必須加入到 layout 中的 view。
例如你要建立某些人的姓名與電話清單,你可以回傳 Cursor 做的查詢結果,包含每個人的姓名與電話,然後建立一個字串陣列指定哪個查詢結果的 column 要放到哪裡,
建立一個整數陣列指定相對每個 coulmn 要被放進去的 view:

String[] fromColumns = {ContactsContract.Data.DISPLAY_NAME,
ContactsContract.CommonDataKinds.Phone.NUMBER};
int[] toViews = {R.id.display_name, R.id.phone_number};

翻譯感覺不太順,簡單來說就是把查詢結果放到 String Array 中,然後在另一個 int Array 把這些資料要放進去哪個 view 的 id 指定好。

當你實做 SimpleCursorAdapter 時,傳入要使用的 layout,包含查詢結果的 Cursor,還有上面兩個陣列:

SimpleCursorAdapter adapter = new SimpleCursorAdapter(this,
R.layout.person_name_and_number, cursor, fromColumns, toViews, 0);
ListView listView = getListView();
listView.setAdapter(adapter);

如果 Course 在 APP 執行中,你更改 adapter 讀取的底層資料,你必須呼叫 notifyDataSetChanged()。
這個方法會通知所屬的 view 資料被更動了,必須更新。

Handling click events

你可以藉由實現 AdapterView.OnItemClickListener 介面,回應 AdapterView 每個項目的點擊事件,例如:

// Create a message handling object as an anonymous class.
private OnItemClickListener mMessageClickedHandler = new OnItemClickListener() {
public void onItemClick(AdapterView parent, View v, int position, long id) {
// Do something in response to the click
}
};

listView.setOnItemClickListener(mMessageClickedHandler);

GridView

GridView 是 ViewGroup 用二維可捲動的格狀方式顯示項目。這些項目可以用 ListAdapter 自動加入 layout。

Example

在這個教學中,你將建立一個格狀的影像縮圖。當一個項目被選擇時,一個 toast message 機會顯示該位置的影像。

  • 開啟一個新專案叫做 HelloGridView
  • 找一些你喜歡圖片,或是下載這些範例圖片。把圖片儲存到專案中的 res/drawable/ 目錄下。
  • 打開 res/layout/activity_main.xml 把這些打進去:

    <?xml version="1.0" encoding="utf-8"?>
    <GridView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/gridview"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:columnWidth="90dp"
    android:numColumns="auto_fit"
    android:verticalSpacing="10dp"
    android:horizontalSpacing="10dp"
    android:stretchMode="columnWidth"
    android:gravity="center"
    />

    這個 GridView 將會填滿整個畫面。

  • 打開 MainActivity.java 把這些程式碼家到 onCreate() 方法中:

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    GridView gridView = (GridView) findViewById(R.id.gridview);
    gridView.setAdapter(new ImageAdapter(this));

    gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
    Toast.makeText(MainActivity.this, "" + position, Toast.LENGTH_SHORT).show();
    }
    });
    }

    在把 ContentView 設為 activity_main 之後,GridView layout 被 findViewById(int) 抓進來。
    之後 setAdapter() 方法設定一個自訂的 Adapter (ImageAdapter) 作為資料來源,讓所有項目被顯示在表格中,ImageAdapter 會在下一步驟建立。
    要讓表格中的項目被點擊時能做些什麼,要用 setOnItemClickListener 方法,傳入一個 new AdapterView.OnItemClickListener。
    這個匿名實例定義了 onItemClick() callback 方法來顯示一個 Toast 來展示被選擇項目的 index position(從0開始)(實際的情況是,position 被用在其他的任務中取得全尺寸的影像)。

  • 建立一個叫做 ImageAdapter 的新 class,extends BaseAdapter:

    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;
    }

    // create a new ImageView for each item referenced by the Adapter
    public View getView(int position, View convertView, ViewGroup parent) {
    ImageView imageView;
    if (convertView == null) {
    // if it's not recycled, initialize some attributes
    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;
    }

    // references to our images
    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
    };
    }

    首先實現一些繼承自 BaseAdapter 的方法,通常 getCount(int) 必須能傳入 position 回傳項目,但是在這個範例中就先忽略了,
    同樣的 getItemId(int) 需要回傳某個項目的 row id,但是在這裡我們也用不到。
    第一個必須的方法是 getView()。這個方法建立一個 View,把每個影像加入 ImageAdapter。當這個方法被呼叫時 view 會被傳入,通常這是可以回收在利用的物件(被呼叫一次以後),
    所以這裡會檢查如果這個物件是不是空的(null),如果是的話會建立一個 ImageView 並把特性設定成影像想要展現的樣子。

    • setLayoutParams(ViewGroup.LayoutParams) 設定 view 的高和寬已確保不論 drawable 的大小,每個影像可以適當的被調整和裁剪成適合的尺寸。
    • setScaleType(ImageView.ScaleType) 宣告這些影像如果需要時會被朝中心裁剪。
    • setPadding(int, int, int, int) 定義每一邊的 padding。(注意,當影像有不同的常寬比時,當他不符合ImageView的尺寸時,較少的 padding 會造成更多的剪裁。)
      如果傳入的 View 不是空值,被傳入的 position 會被用來在 mThumbIds 陣列中選擇影像,他會為 ImageView 設定影像來源。
  • 執行APP!

嘗試看看調整 GridView 和 ImageView 的特性,例如不要用 setLayoutParams(ViewGroup.LayoutParams) 改用 setAdjustViewBounds(boolean)。

文章目錄
  1. 1. Layouts
    1. 1.1. Write the XML
    2. 1.2. Load the XML Resource
    3. 1.3. …中略…
    4. 1.4. Building Layouts with an Adapter
    5. 1.5. Filling an adapter view with data
      1. 1.5.1. ArrayAdapter
      2. 1.5.2. SimpleCursorAdapter
    6. 1.6. Handling click events
  2. 2. GridView
    1. 2.1. Example