2012年4月4日 星期三

方法。adapter測試。



有關ListView使用的adapter,上面是其類別圖。希望不要有錯才好...
有關UML的內容,可以參考這裡(淺談UML)或是這裡(六大關係)
然後小書做了一個Demo,是將一些常用的adapter實做出來,可以從這裡下載

然後,寫到這裡竟然只有三行?!來把Demo裡面的code貼出來吧...

首先,因為我把Database放SD卡裡面,所以我們必需要增加權限:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="adapter.susan.idea"
    android:versionCode="1"
    android:versionName="1.0" 
    android:installLocation="preferExternal">
    <uses-sdk android:minSdkVersion="8" />

    <application
        android:icon="@drawable/ic_launcher"
        android:label="@string/app_name" >
        <activity
            android:label="@string/app_name"
            android:name=".Main" >
            <intent-filter >
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
</manifest>

然後,把相關的layout做出來,主要有兩種對應的畫面和主畫面的layout,
主畫面main.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" >
    <Spinner
        android:id="@+id/Spinner"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        />
    <ListView
        android:id="@+id/ListView"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" />
</LinearLayout>

第一種row_list.xml和第二種row_list1.xml對應的畫面其實差不多,沒什麼太大的意義:

<linearlayout android:id="@+id/widget31" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
<linearlayout android:id="@+id/widget32" android:layout_height="wrap_content" android:layout_width="fill_parent">

<textview android:gravity="center" android:id="@+id/textView1" android:layout_height="wrap_content" android:layout_weight="0.2" android:layout_width="wrap_content" android:text="TextView">

</textview>

<textview android:gravity="center" android:id="@+id/textView2" android:layout_height="wrap_content" android:layout_weight="1" android:layout_width="wrap_content" android:text="TextView">

</textview>
</linearlayout>

<linearlayout android:id="@+id/linearLayout2" android:layout_height="wrap_content" android:layout_width="match_parent">

    <linearlayout android:id="@+id/linearLayout1" android:layout_height="wrap_content" android:layout_weight="1" android:layout_width="match_parent" android:orientation="vertical">

        <textview android:gravity="center" android:id="@+id/textView3" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="Large Text" android:textappearance="?android:attr/textAppearanceLarge">

        <textview android:gravity="center" android:id="@+id/textView4" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="Medium Text" android:textappearance="?android:attr/textAppearanceMedium">
    </textview></textview></linearlayout>

</linearlayout>

</linearlayout>


<linearlayout android:id="@+id/widget31" android:layout_height="fill_parent" android:layout_width="fill_parent" android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android">
<linearlayout android:id="@+id/widget32" android:layout_height="wrap_content" android:layout_width="fill_parent">

<textview android:gravity="center" android:id="@+id/textView1" android:layout_height="wrap_content" android:layout_weight="0.2" android:layout_width="wrap_content" android:text="TextView">

</textview>

<textview android:gravity="center" android:id="@+id/textView2" android:layout_height="wrap_content" android:layout_weight="1" android:layout_width="wrap_content" android:text="TextView">

</textview>
</linearlayout>

<linearlayout android:id="@+id/linearLayout2" android:layout_height="wrap_content" android:layout_width="fill_parent">

    <linearlayout android:id="@+id/linearLayout1" android:layout_height="wrap_content" android:layout_weight="0.7" android:layout_width="0dp" android:orientation="vertical">

        <textview android:gravity="center" android:id="@+id/textView3" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="Large Text" android:textappearance="?android:attr/textAppearanceLarge">

        <textview android:gravity="center" android:id="@+id/textView4" android:layout_height="wrap_content" android:layout_width="match_parent" android:text="Medium Text" android:textappearance="?android:attr/textAppearanceMedium">
    </textview></textview></linearlayout>

    <imageview android:id="@+id/imageView1" android:layout_height="fill_parent" android:layout_weight="0.3" android:layout_width="0dp" android:src="@android:drawable/ic_menu_mapmode">

</imageview></linearlayout>

</linearlayout>

然後就是DataBase的class了,是拿之前上課的class出來修修修改改改,參考自這裡


update:4/8 修改了class裡面其中一個方法,檔案裡面沒有改過來:
    /** 依照時間間隔獲取資料 */
    public Cursor getIntervalData(Timestamp from, Timestamp to) {
        String cmd = "SELECT * FROM " + TABLE + " WHERE (" + KEY_COLUMNS[0]
                + " >= '" + from.toString() + "') AND (" + KEY_COLUMNS[0]
                + " <= '" + to.toString() + "')";

        return db.rawQuery(cmd, null);
    }


package adapter.susan.idea;

import java.sql.Timestamp;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;

public class DB {

    private static final String TAG = DB.class.getSimpleName();

    static final int VERSION = 1;
    static final String DATABASE = android.os.Environment
    .getExternalStorageDirectory().getAbsolutePath() +"/DataBaseBABABA/databaseTest.db3";
    static final String TABLE = "tableOfthisDataBase";

    /**
     * {  "date", "value", "value2" }
     */
    public static final String[] KEY_COLUMNS = { "date", "value",
            "value2" };

    // //建立資料庫欄位格式
    // public static String createSQL ;

    // DbHelper implementations, 宣告成為靜態
    // DbHelper類別為sqliteDB類別的內隱類別(inner class)
    // 加上static關鍵字,DbHelper就變成sqliteDB類別的巢狀類別(nested class)
    private static class DbHelper extends SQLiteOpenHelper {

        public DbHelper(Context context) {
            super(context, DATABASE, null, VERSION);
        }

        @Override
        // 只會被呼叫一次,在第一次資料庫建立的時候
        public void onCreate(SQLiteDatabase db) {
            // TODO Auto-generated method stub
            Log.i(TAG, "Creating database: " + DATABASE);
            db.execSQL(getCreateSQL());
        }

        public String getCreateSQL() {

            StringBuffer mStringBuffer = new StringBuffer();
            mStringBuffer.append("CREATE TABLE IF NOT EXISTS " + TABLE + " ("
                    + "_id"
                    + " INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "
                    + KEY_COLUMNS[0]
                    + " TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,");
            for (int n = 1; n < KEY_COLUMNS.length; n++) {
                mStringBuffer.append(KEY_COLUMNS[n] + " TEXT, ");
            }
            mStringBuffer.delete(mStringBuffer.length() - 2,
                    mStringBuffer.length());// 刪除", "
            mStringBuffer.append(");");
            return mStringBuffer.toString();
        }

        @Override
        public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
            // TODO Auto-generated method stub
            db.execSQL("DROP TABLE IF EXISTS " + TABLE);
            this.onCreate(db);
        }
    }

    // instance of dbHelper
    private final DbHelper dbHelper; // 不希望被改變
    private SQLiteDatabase db;

    public DB(Context context) {
        dbHelper = new DbHelper(context);
        Log.i(TAG, "Initialized database in Constructor of DB");
    }

    public DB openToWrite() throws SQLException {
        db = dbHelper.getWritableDatabase(); // 若資料庫存在則開啟;若不存在則建立一個新的
        return this;
    }

    public DB openToRead() throws SQLException {
        db = dbHelper.getReadableDatabase(); // 若資料庫存在則開啟;若不存在則建立一個新的
        return this;
    }

    public void close() {
        dbHelper.close();
    }

    /** 添加資料 */
    public void insert(ContentValues values) throws SQLException {
        // db.insertWithOnConflict(TABLE, null, values,
        // SQLiteDatabase.CONFLICT_IGNORE); //這個指令需要And 2.2以上版本
        db.insertOrThrow(TABLE, null, values); // 如果插入錯誤會丟一個例外出來,暫不處理。
        Log.i(TAG, "insert data into db");
    }

    /**
     * 更新資料(測試)
     * 
     * @param column
     *            輸入欄位名稱
     * @param oldValue
     *            輸入欄位內欲查詢的變數
     * @param newValue
     *            輸入要更新的值
     */
    public void update(String column, String oldValue, ContentValues newValue)
            throws SQLException {
        db.update(TABLE, newValue, column + "=" + oldValue, null);
    }

    /**
     * 刪除資料(測試)
     * 
     * @param column
     *            輸入欄位名稱
     * @param value
     *            輸入欄位內欲查詢的變數
     */
    public void deleteData(String column, String value) throws SQLException {
        db.delete(TABLE, column + "=" + value, null);
    }

    /**
     * 獲得資料(測試)
     * 
     * @param column
     *            輸入欄位名稱
     * @param value
     *            輸入欄位內欲查詢的變數
     */
    public Cursor getData(String column, String value) {
        String cmd = "SELECT * FROM " + TABLE + " WHERE " + column + "="
                + value;
        return db.rawQuery(cmd, null);
    }

    /** 獲得全部資料 */
    public Cursor getAll() {
        // return db.rawQuery("SELECT * FROM " + TABLE, null);
        return db.query(TABLE, null, null, null, null, null, null);
    }

    /** 依照時間間隔獲取資料 */
    public Cursor getIntervalData(Timestamp from, Timestamp to) {
        String cmd = "SELECT * FROM " + TABLE + " WHERE (" + KEY_COLUMNS[0]
                + " >= '" + from.toString() + "') AND (" + KEY_COLUMNS[0]
                + " <= '" + to.toString() + "')";

        return db.rawQuery(cmd, null);
    }

    /** 清除全部的資料 */
    public boolean clearAll() {
        return db.delete(TABLE, null, null) > 0;
    }

}


然後是主函式:

package adapter.susan.idea;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;

import android.app.Activity;
import android.content.ContentValues;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import android.widget.SimpleAdapter;
import android.widget.SimpleCursorAdapter;
import android.widget.SimpleCursorAdapter.ViewBinder;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;

public class Main extends Activity {
    private ListView listView;
    private Spinner spinner;// 切換ListView用的
    private DB db;// 資料庫
    private Cursor cursor = null;
    /** ArrayAdapter顯示的字串,也是作為添加資料庫的的變數值 */
    private final String[] mString = { "如何讓你遇見我", "在我最美麗的時刻 為這", "我已再佛前求了五百年",
            "求祂讓我們結一段塵緣", "佛於是把我化成一棵樹", "長在你必經的路旁", "陽光下慎重地開滿花", "朵朵都是我前世的盼望",
            "當你走近", "請你細聽", "那顫抖得葉是我等待的熱情", "而當你終於無視地走過", "在你身後落了一地的",
            "朋友啊 那不是花辦 那是我凋零的心" };

    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        init();

    }

    /** 變數初始化 */
    private void init() {
        listView = (ListView) this.findViewById(R.id.ListView);
        spinner = (Spinner) this.findViewById(R.id.Spinner);
        db = new DB(this);
        db.openToWrite();
        createData();

        ArrayAdapter<String> adapter;// 做sprinner的adapter
        adapter = new ArrayAdapter<String>(this,
                android.R.layout.simple_spinner_item, new String[] {
                        "ArrayAdapter<String>", "SimpleAdapter",
                        "SimpleCursorAdapter",
                        "SimpleCursorAdapter,using ViewBinder" });

        // 設置下拉菜單的風格
        adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);

        // 產生畫面
        spinner.setAdapter(adapter);
        Toast.makeText(this, "" + spinner.getAdapter().getClass().getName(),
                Toast.LENGTH_LONG).show();
        // 這邊會發現spinner的adapter抓出來的type是SpinnerAdapter
        spinner.setOnItemSelectedListener(mOnItemSelectedListener);
    }

    /** show出ListView的畫面 */
    private void showListView(int type) {
        // ArrayAdapter<T>, HeaderViewListAdapter,SimpleAdapter,
        // SimpleCursorAdapter,
        // BaseAdapter,CursorAdapter,ResourceCursorAdapter 抽象的
        // ListAdapter,SpinnerAdapter,WrapperListAdapter 介面

        final int[] ID_VIEW = { R.id.textView1, R.id.textView3, R.id.textView2,
                R.id.textView4 };// id,time,value1,value2.
        final String[] KEY_COLUMN_NAME = { "_id", DB.KEY_COLUMNS[0],
                DB.KEY_COLUMNS[1], DB.KEY_COLUMNS[2] };
        switch (type) {
        case 0:// 顯示ArrayAdapter所設定的畫面
            Toast.makeText(this, "ArrayAdapter<String>", Toast.LENGTH_SHORT)
                    .show();

            ArrayAdapter<String> mArrayAdapter = new ArrayAdapter<String>(this,
                    android.R.layout.simple_list_item_1, mString);
            listView.setAdapter(mArrayAdapter);
            break;
        case 1:// 顯示SimpleAdapter所設定的畫面
            /* 我們在這裡用database來做為資料來源,
             * 這樣顯示的內容應該是跟SimpleCursorAdapter一樣的。 
             * 但是為了表現出SimpleAdapter的靈活,我們在HashMap<String, String>那邊動了些手腳。*/

            Toast.makeText(this, "SimpleAdapter", Toast.LENGTH_SHORT).show();
            cursor = db.getAll();
            if (cursor.getCount() > 0) {
                cursor.moveToFirst();
                ArrayList<HashMap<String, String>> values = new ArrayList<HashMap<String, String>>();
                int n = 0;
                while (cursor.moveToNext()) {// 有很多方法可以移動cursor,這個算是目前比較簡便的方式
                    HashMap<String, String> item = new HashMap<String, String>();
                    item.put(KEY_COLUMN_NAME[0],
                            "SimpleAdapter" + String.valueOf(n));
                    /*在SimpleCursorAdapter裡面,KEY_COLUMN_NAME[0]會成為"_id"的內容,
                     * 不過我們把它改成了自定義的字串。如果要在SimpleCursorAdapter內實作一樣的效果,
                     * 則必須使用ViewBinder(見case 3)*/
                    for (String columnName : DB.KEY_COLUMNS) {
                        item.put(columnName, cursor.getString(cursor
                                .getColumnIndex(columnName)));
                    }
                    values.add(item);
                    n++;
                }

                SimpleAdapter mSimpleAdapter = new SimpleAdapter(this, values,
                        R.layout.row_list, KEY_COLUMN_NAME, ID_VIEW);
                listView.setAdapter(mSimpleAdapter);
            }

            break;
        case 2:// 顯示SimpleCursorAdapter所設定的畫面
            Toast.makeText(this, "SimpleCursorAdapter", Toast.LENGTH_SHORT)
                    .show();
            cursor = db.getAll();
            if (cursor.getCount() > 0) {
//                startManagingCursor(cursor);
                SimpleCursorAdapter mSimpleCursorAdapter = new SimpleCursorAdapter(
                        this, R.layout.row_list, cursor, KEY_COLUMN_NAME,
                        ID_VIEW);
                listView.setAdapter(mSimpleCursorAdapter);
            }
            break;

        case 3:// 顯示SimpleCursorAdapter所設定的畫面,使用viewBinder的結果
            Toast.makeText(this, "SimpleCursorAdapter ,use ViewBinder.",
                    Toast.LENGTH_SHORT).show();
            cursor = db.getAll();
            if (cursor.getCount() > 0) {
//                startManagingCursor(cursor);

                SimpleCursorAdapter mSimpleCursorAdapter = new SimpleCursorAdapter(
                        this, R.layout.row_list1, cursor, KEY_COLUMN_NAME,
                        ID_VIEW);
                // Sets the binder used to bind data to views.
                mSimpleCursorAdapter.setViewBinder(new ViewBinder() {

                    @Override
                    public boolean setViewValue(View view, Cursor cursor,
                            int columnIndex) {
                        // 設定view和cursor對應的內容
                        /*
                         * 参数view.getId()得到的值对应的不是实例化Adapter时传入的layout的ID。
                         * 实际对应的是实例化adapter里传入的参数int[] to里的值。
                         * 然后再由该值找到对应的View控件,进而改变相应的显示内容、样式等
                         */
                        if (view.getId() == R.id.textView4) {
                            TextView mTextView = (TextView) view;
                            mTextView.setText("強制變身!登登!這是第"
                                    + cursor.getPosition() + "位置。");
                            Log.d("Test", "if(view.getId()==R.id.textView4)");
                            return true;
                        }
                        // Returns:
                        // true if the data was bound to the view, false
                        // otherwise
                        return false;
                    }
                });

                listView.setAdapter(mSimpleCursorAdapter);
            }
            break;

        default:
            showListView(0);
            break;
        }

    }

    OnItemSelectedListener mOnItemSelectedListener = new OnItemSelectedListener() {

        @Override
        public void onItemSelected(AdapterView<?> parent, View view,
                int position, long id) {
            showListView(position);
        }

        @Override
        public void onNothingSelected(AdapterView<?> parent) {
            showListView(0);
        }
    };

    @Override
    public void onDestroy() {
        super.onDestroy();

        db.close();
        if (cursor != null) {
            cursor.close();
        }
    }

    /** 產生測試用的data資料 */
    private void createData() {
        db.clearAll();// 做個清除的動作。
        Calendar c = Calendar.getInstance();
        Timestamp timestamp = new Timestamp(c.getTimeInMillis());

        int n = 0;
        while (n < mString.length) {
            timestamp.setTime(timestamp.getTime() + 86399000 * n);
            ContentValues values = new ContentValues();
            // "2012-1-12 10:35:24"
            values.put(DB.KEY_COLUMNS[0], timestamp.toString()); // 使用系統目前的時間
            values.put(DB.KEY_COLUMNS[1], "第" + n + "句。");
            values.put(DB.KEY_COLUMNS[2], mString[n]);
            db.insert(values);
            n++;
        }
    }
}


大概是這樣,最後附上SimpleCursorAdapter的源碼(2.2):
/*
 * Copyright (C) 2006 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package android.widget;

import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.view.View;

/**
 * An easy adapter to map columns from a cursor to TextViews or ImageViews
 * defined in an XML file. You can specify which columns you want, which
 * views you want to display the columns, and the XML file that defines
 * the appearance of these views.
 *
 * Binding occurs in two phases. First, if a
 * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
 * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
 * is invoked. If the returned value is true, binding has occured. If the
 * returned value is false and the view to bind is a TextView,
 * {@link #setViewText(TextView, String)} is invoked. If the returned value
 * is false and the view to bind is an ImageView,
 * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
 * binding can be found, an {@link IllegalStateException} is thrown.
 *
 * If this adapter is used with filtering, for instance in an
 * {@link android.widget.AutoCompleteTextView}, you can use the
 * {@link android.widget.SimpleCursorAdapter.CursorToStringConverter} and the
 * {@link android.widget.FilterQueryProvider} interfaces
 * to get control over the filtering process. You can refer to
 * {@link #convertToString(android.database.Cursor)} and
 * {@link #runQueryOnBackgroundThread(CharSequence)} for more information.
 */
public class SimpleCursorAdapter extends ResourceCursorAdapter {
    /**
     * A list of columns containing the data to bind to the UI.
     * This field should be made private, so it is hidden from the SDK.
     * {@hide}
     */
    protected int[] mFrom;
    /**
     * A list of View ids representing the views to which the data must be bound.
     * This field should be made private, so it is hidden from the SDK.
     * {@hide}
     */
    protected int[] mTo;

    private int mStringConversionColumn = -1;
    private CursorToStringConverter mCursorToStringConverter;
    private ViewBinder mViewBinder;

    String[] mOriginalFrom;

    /**
     * Constructor the enables auto-requery.
     *
     * @deprecated This option is discouraged, as it results in Cursor queries
     * being performed on the application's UI thread and thus can cause poor
     * responsiveness or even Application Not Responding errors.  As an alternative,
     * use {@link android.app.LoaderManager} with a {@link android.content.CursorLoader}.
     */
    @Deprecated
    public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from, int[] to) {
        super(context, layout, c);
        mTo = to;
        mOriginalFrom = from;
        findColumns(from);
    }

    /**
     * Standard constructor.
     * 
     * @param context The context where the ListView associated with this
     *            SimpleListItemFactory is running
     * @param layout resource identifier of a layout file that defines the views
     *            for this list item. The layout file should include at least
     *            those named views defined in "to"
     * @param c The database cursor.  Can be null if the cursor is not available yet.
     * @param from A list of column names representing the data to bind to the UI.  Can be null 
     *            if the cursor is not available yet.
     * @param to The views that should display column in the "from" parameter.
     *            These should all be TextViews. The first N views in this list
     *            are given the values of the first N columns in the from
     *            parameter.  Can be null if the cursor is not available yet.
     * @param flags Flags used to determine the behavior of the adapter,
     * as per {@link CursorAdapter#CursorAdapter(Context, Cursor, int)}.
     */
    public SimpleCursorAdapter(Context context, int layout, Cursor c, String[] from,
            int[] to, int flags) {
        super(context, layout, c, flags);
        mTo = to;
        mOriginalFrom = from;
        findColumns(from);
    }

    /**
     * Binds all of the field names passed into the "to" parameter of the
     * constructor with their corresponding cursor columns as specified in the
     * "from" parameter.
     *
     * Binding occurs in two phases. First, if a
     * {@link android.widget.SimpleCursorAdapter.ViewBinder} is available,
     * {@link ViewBinder#setViewValue(android.view.View, android.database.Cursor, int)}
     * is invoked. If the returned value is true, binding has occured. If the
     * returned value is false and the view to bind is a TextView,
     * {@link #setViewText(TextView, String)} is invoked. If the returned value is
     * false and the view to bind is an ImageView,
     * {@link #setViewImage(ImageView, String)} is invoked. If no appropriate
     * binding can be found, an {@link IllegalStateException} is thrown.
     *
     * @throws IllegalStateException if binding cannot occur
     * 
     * @see android.widget.CursorAdapter#bindView(android.view.View,
     *      android.content.Context, android.database.Cursor)
     * @see #getViewBinder()
     * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
     * @see #setViewImage(ImageView, String)
     * @see #setViewText(TextView, String)
     */
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        final ViewBinder binder = mViewBinder;
        final int count = mTo.length;
        final int[] from = mFrom;
        final int[] to = mTo;

        for (int i = 0; i < count; i++) {
            final View v = view.findViewById(to[i]);
            if (v != null) {
                boolean bound = false;
                if (binder != null) {
                    bound = binder.setViewValue(v, cursor, from[i]);
                }

                if (!bound) {
                    String text = cursor.getString(from[i]);
                    if (text == null) {
                        text = "";
                    }

                    if (v instanceof TextView) {
                        setViewText((TextView) v, text);
                    } else if (v instanceof ImageView) {
                        setViewImage((ImageView) v, text);
                    } else {
                        throw new IllegalStateException(v.getClass().getName() + " is not a " +
                                " view that can be bounds by this SimpleCursorAdapter");
                    }
                }
            }
        }
    }

    /**
     * Returns the {@link ViewBinder} used to bind data to views.
     *
     * @return a ViewBinder or null if the binder does not exist
     *
     * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
     * @see #setViewBinder(android.widget.SimpleCursorAdapter.ViewBinder)
     */
    public ViewBinder getViewBinder() {
        return mViewBinder;
    }

    /**
     * Sets the binder used to bind data to views.
     *
     * @param viewBinder the binder used to bind data to views, can be null to
     *        remove the existing binder
     *
     * @see #bindView(android.view.View, android.content.Context, android.database.Cursor)
     * @see #getViewBinder()
     */
    public void setViewBinder(ViewBinder viewBinder) {
        mViewBinder = viewBinder;
    }

    /**
     * Called by bindView() to set the image for an ImageView but only if
     * there is no existing ViewBinder or if the existing ViewBinder cannot
     * handle binding to an ImageView.
     *
     * By default, the value will be treated as an image resource. If the
     * value cannot be used as an image resource, the value is used as an
     * image Uri.
     *
     * Intended to be overridden by Adapters that need to filter strings
     * retrieved from the database.
     *
     * @param v ImageView to receive an image
     * @param value the value retrieved from the cursor
     */
    public void setViewImage(ImageView v, String value) {
        try {
            v.setImageResource(Integer.parseInt(value));
        } catch (NumberFormatException nfe) {
            v.setImageURI(Uri.parse(value));
        }
    }

    /**
     * Called by bindView() to set the text for a TextView but only if
     * there is no existing ViewBinder or if the existing ViewBinder cannot
     * handle binding to an TextView.
     *
     * Intended to be overridden by Adapters that need to filter strings
     * retrieved from the database.
     * 
     * @param v TextView to receive text
     * @param text the text to be set for the TextView
     */    
    public void setViewText(TextView v, String text) {
        v.setText(text);
    }

    /**
     * Return the index of the column used to get a String representation
     * of the Cursor.
     *
     * @return a valid index in the current Cursor or -1
     *
     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
     * @see #setStringConversionColumn(int) 
     * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
     * @see #getCursorToStringConverter()
     */
    public int getStringConversionColumn() {
        return mStringConversionColumn;
    }

    /**
     * Defines the index of the column in the Cursor used to get a String
     * representation of that Cursor. The column is used to convert the
     * Cursor to a String only when the current CursorToStringConverter
     * is null.
     *
     * @param stringConversionColumn a valid index in the current Cursor or -1 to use the default
     *        conversion mechanism
     *
     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
     * @see #getStringConversionColumn()
     * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
     * @see #getCursorToStringConverter()
     */
    public void setStringConversionColumn(int stringConversionColumn) {
        mStringConversionColumn = stringConversionColumn;
    }

    /**
     * Returns the converter used to convert the filtering Cursor
     * into a String.
     *
     * @return null if the converter does not exist or an instance of
     *         {@link android.widget.SimpleCursorAdapter.CursorToStringConverter}
     *
     * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter)
     * @see #getStringConversionColumn()
     * @see #setStringConversionColumn(int)
     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
     */
    public CursorToStringConverter getCursorToStringConverter() {
        return mCursorToStringConverter;
    }

    /**
     * Sets the converter  used to convert the filtering Cursor
     * into a String.
     *
     * @param cursorToStringConverter the Cursor to String converter, or
     *        null to remove the converter
     *
     * @see #setCursorToStringConverter(android.widget.SimpleCursorAdapter.CursorToStringConverter) 
     * @see #getStringConversionColumn()
     * @see #setStringConversionColumn(int)
     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
     */
    public void setCursorToStringConverter(CursorToStringConverter cursorToStringConverter) {
        mCursorToStringConverter = cursorToStringConverter;
    }

    /**
     * Returns a CharSequence representation of the specified Cursor as defined
     * by the current CursorToStringConverter. If no CursorToStringConverter
     * has been set, the String conversion column is used instead. If the
     * conversion column is -1, the returned String is empty if the cursor
     * is null or Cursor.toString().
     *
     * @param cursor the Cursor to convert to a CharSequence
     *
     * @return a non-null CharSequence representing the cursor
     */
    @Override
    public CharSequence convertToString(Cursor cursor) {
        if (mCursorToStringConverter != null) {
            return mCursorToStringConverter.convertToString(cursor);
        } else if (mStringConversionColumn > -1) {
            return cursor.getString(mStringConversionColumn);
        }

        return super.convertToString(cursor);
    }

    /**
     * Create a map from an array of strings to an array of column-id integers in mCursor.
     * If mCursor is null, the array will be discarded.
     * 
     * @param from the Strings naming the columns of interest
     */
    private void findColumns(String[] from) {
        if (mCursor != null) {
            int i;
            int count = from.length;
            if (mFrom == null || mFrom.length != count) {
                mFrom = new int[count];
            }
            for (i = 0; i < count; i++) {
                mFrom[i] = mCursor.getColumnIndexOrThrow(from[i]);
            }
        } else {
            mFrom = null;
        }
    }

    @Override
    public Cursor swapCursor(Cursor c) {
        // super.swapCursor() will notify observers before we have
        // a valid mapping, make sure we have a mapping before this
        // happens
        if (mFrom == null) {
            findColumns(mOriginalFrom);
        }
        Cursor res = super.swapCursor(c);
        // rescan columns in case cursor layout is different
        findColumns(mOriginalFrom);
        return res;
    }
    
    /**
     * Change the cursor and change the column-to-view mappings at the same time.
     *  
     * @param c The database cursor.  Can be null if the cursor is not available yet.
     * @param from A list of column names representing the data to bind to the UI.  Can be null 
     *            if the cursor is not available yet.
     * @param to The views that should display column in the "from" parameter.
     *            These should all be TextViews. The first N views in this list
     *            are given the values of the first N columns in the from
     *            parameter.  Can be null if the cursor is not available yet.
     */
    public void changeCursorAndColumns(Cursor c, String[] from, int[] to) {
        mOriginalFrom = from;
        mTo = to;
        // super.changeCursor() will notify observers before we have
        // a valid mapping, make sure we have a mapping before this
        // happens
        if (mFrom == null) {
            findColumns(mOriginalFrom);
        }
        super.changeCursor(c);
        findColumns(mOriginalFrom);
    }

    /**
     * This class can be used by external clients of SimpleCursorAdapter
     * to bind values fom the Cursor to views.
     *
     * You should use this class to bind values from the Cursor to views
     * that are not directly supported by SimpleCursorAdapter or to
     * change the way binding occurs for views supported by
     * SimpleCursorAdapter.
     *
     * @see SimpleCursorAdapter#bindView(android.view.View, android.content.Context, android.database.Cursor)
     * @see SimpleCursorAdapter#setViewImage(ImageView, String) 
     * @see SimpleCursorAdapter#setViewText(TextView, String)
     */
    public static interface ViewBinder {
        /**
         * Binds the Cursor column defined by the specified index to the specified view.
         *
         * When binding is handled by this ViewBinder, this method must return true.
         * If this method returns false, SimpleCursorAdapter will attempts to handle
         * the binding on its own.
         *
         * @param view the view to bind the data to
         * @param cursor the cursor to get the data from
         * @param columnIndex the column at which the data can be found in the cursor
         *
         * @return true if the data was bound to the view, false otherwise
         */
        boolean setViewValue(View view, Cursor cursor, int columnIndex);
    }

    /**
     * This class can be used by external clients of SimpleCursorAdapter
     * to define how the Cursor should be converted to a String.
     *
     * @see android.widget.CursorAdapter#convertToString(android.database.Cursor)
     */
    public static interface CursorToStringConverter {
        /**
         * Returns a CharSequence representing the specified Cursor.
         *
         * @param cursor the cursor for which a CharSequence representation
         *        is requested
         *
         * @return a non-null CharSequence representing the cursor
         */
        CharSequence convertToString(Cursor cursor);
    }

}


沒有留言:

張貼留言

你好,我是小書,如果文章內容有錯誤,或是看到有建議以及任何感想時,歡迎提出分享,我們一起學習一起努力。

追蹤者