2011年4月29日 星期五

自訂 ListView 項目

想要讓 ListView 顯示比較複雜的樣子,可以利用自訂 Adapter 的方式來達到目的。


1、首先先建立一個 list_view_item.xml,把每個項目要顯示的樣子先設定好。

list_view_item.xml 

<?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">
 <LinearLayout android:orientation="horizontal"
  android:layout_width="fill_parent" android:layout_height="wrap_content">
  <RelativeLayout android:gravity="left" android:layout_weight="1"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   <TextView
    android:id="@+id/ListItem_date"
    android:layout_width="250px" android:layout_height="wrap_content"
    android:layout_weight="6" android:textSize="21dip" android:textStyle="bold"
    android:textColor="#FF00FF00" ></TextView>
  </RelativeLayout>
  <RelativeLayout android:gravity="right" android:layout_weight="1"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   <TextView
    android:id="@+id/ListItem_company"
    android:layout_width="150px" android:layout_height="wrap_content"
    android:layout_weight="4" android:textSize="18dip" android:textStyle="bold"
    android:textColor="#FF0000" ></TextView>
  </RelativeLayout>
 </LinearLayout>
 <LinearLayout android:orientation="horizontal"
  android:layout_width="fill_parent" android:layout_height="wrap_content">
  <RelativeLayout android:gravity="left" android:layout_weight="2"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   
   <TextView android:id="@+id/ListItem_money"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:textSize="12dip" android:textColor="#AAAAAA" ></TextView>
  </RelativeLayout>
  <RelativeLayout android:gravity="right" android:layout_weight="2"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   
   <TextView android:id="@+id/ListItem_miles"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:textSize="12dip" android:textColor="#AAAAAA" ></TextView>
  </RelativeLayout>
 </LinearLayout>
 <LinearLayout android:orientation="horizontal"
  android:layout_width="fill_parent" android:layout_height="wrap_content">
  <RelativeLayout android:gravity="left" android:layout_weight="2"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   
   <TextView android:id="@+id/ListItem_price_per_litre"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:textSize="12dip" android:textColor="#AAAAAA" ></TextView>
  </RelativeLayout>
  <RelativeLayout android:gravity="center" android:layout_weight="2"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   
   <TextView android:id="@+id/ListItem_price_per_mile"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:textSize="12dip" android:textColor="#AAAAAA" ></TextView>
  </RelativeLayout>
  <RelativeLayout android:gravity="right" android:layout_weight="2"
   android:layout_width="fill_parent" android:layout_height="wrap_content">
   
   <TextView android:id="@+id/ListItem_miles_per_litre"
    android:layout_width="wrap_content" android:layout_height="wrap_content"
    android:textSize="12dip" android:textColor="#AAAAAA" ></TextView>
  </RelativeLayout>
 </LinearLayout>
</LinearLayout>

當然,也要設定一個包含 ListView 的 xml 檔給 Activity 使用。

list_iew.xml
 

<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent">
    <ListView android:id="@android:id/list"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent" />
</LinearLayout>

2、Adapter 本身要 extends BaseAdapter,內容最主要是設定 getView( ) 函式去決定 ListView 的項目該怎麼顯示,而資料的提供來源是一個 ArrayList<HashMap<String, String>> 型態的物件,也就是一個 ArrayList 的每個項目都是 HashMap,而每個 HashMap 都包含 String, String 兩個字串。

ListViewItem.java

import java.util.ArrayList;
import java.util.HashMap;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class OilListAdapter extends BaseAdapter {
 private ArrayList<HashMap<String, String>> data;
 private LayoutInflater layoutInflater;
 
 public OilListAdapter (Context context, ArrayList<HashMap<String, String>> dt) {
  this.data = dt;
  this.layoutInflater = LayoutInflater.from(context); // 取得 LayoutInflater
 }

 public int getCount() {
  return this.data.size();
 }

 public Object getItem(int position) {
  // TODO Auto-generated method stub
  return this.data.get(position);
 }

 public long getItemId(int arg0) {
  return 0;
 }

 public View getView(int position, View view, ViewGroup parent) {
  // TODO Auto-generated method stub
  if(view == null) // 設定 ListView 的 item 要使用的 View
   view = this.layoutInflater.inflate(R.layout.list_item, null);
  
  // 讀取要顯示的 TextView
  TextView date = (TextView) view.findViewById(R.id.OilListItem_date);
  TextView company = (TextView) view.findViewById(R.id.ListItem_company);
  TextView miles = (TextView) view.findViewById(R.id.ListItem_miles);
  TextView money = (TextView) view.findViewById(R.id.ListItem_money);
  TextView milesPerLitre = (TextView) view.findViewById(R.id.ListItem_miles_per_litre);
  TextView pricePerLitre = (TextView) view.findViewById(R.id.ListItem_price_per_litre);
  TextView pricePerMile = (TextView) view.findViewById(R.id.ListItem_price_per_mile);
  
  // 填入項目的內容
  date.setText(this.data.get(position).get("refuel_date"));
  company.setText(this.data.get(position).get("refuel_company"));
  miles.setText(this.data.get(position).get("refuel_miles")+"公里");
  money.setText(this.data.get(position).get("refuel_price")+"元");
  pricePerLitre.setText("油價:"+getPricePerLitre(position, arith)+"元/公升"); //油價
  pricePerMile.setText(getPricePerMile(position, arith)+"元/公里"); // 單位油價
  milesPerLitre.setText(getAvgOfMilesPerLitre(position, arith)+"公里/公升"); //本次油耗
  
  return view;
 }
}

3、 設定好 Adapter 之後,要在程式中設定 ListView 要以這個 Adapter 作為資料存取的來源。

ListActivity.java

public class RecordList extends ListActivity {
 private ListView List;
 private ListAdapter ListAdapter;
 private ArrayList<HashMap<String, String>> ListArray;  // ListView 的項目清單
 
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.list_view);
  this.List = this.getListView();
  this.setListView();
 }
 
 //----------------------------------------------------------//
 //------------------------ ListView ------------------------//
 //----------------------------------------------------------//
 private void setListView () {
  // 設定 ListView 的 Adapter
  this.ListArray = new ArrayList<HashMap<String, String>>();
  this.ListAdapter = new ListAdapter(this, this.ListArray);
  this.List.setAdapter(this.ListAdapter);
  this.updateRecordList();
 }
 
 //------------------------------------------------------------------//
 //------------------------ update functions ------------------------//
 //------------------------------------------------------------------//
 /**
  * 更新列表內容
  */
 private void updateRecordList () {
  this.ListArray.clear();
  SQLiteDatabase db = this.openOrCreateDatabase(db_name, Context.MODE_PRIVATE, null);
  
  String sql = "SELECT * FROM table";
  Cursor cs = db.rawQuery(sql, null);
  HashMap<String, String> hm;
  int numOfRows = cs.getCount();
  cs.moveToFirst();
  
  for(int i=0 ; i<numOfRows ; i++) {
   hm  = new HashMap<String, String>();
   hm.put("refuel_id", cs.getString(0));
   hm.put("refuel_company", cs.getString(2)); // 廠商名稱
   hm.put("refuel_amount", cs.getString(3)); // 加油量
   hm.put("refuel_miles", cs.getString(4)); // 里程
   hm.put("refuel_price", cs.getString(5)); // 加油金額
   hm.put("refuel_date", cs.getString(6)); // 加油日期
   this.ListArray.add(hm);
   cs.moveToNext();
  }
  db.close();
  this.ListAdapter.notifyDataSetChanged();
 }
}

其中 updateRecordList() 這個函式是我設定來更新資料的,動作是從資料庫讀取資料後,填到 ArrayList<HashMap<String, String>> 裡,然後通知 Adapter 資料有更新。

沒有留言: