1. 程式人生 > >android簡訊小程式之CursorAdapter繫結ListView

android簡訊小程式之CursorAdapter繫結ListView

同步查詢繫結:


mListView = findViewById(R.id.id_containers);
//定義uri
mALL_conversation_uri = Telephony.Threads.CONTENT_URI.buildUpon().appendQueryParameter("simple", "true").build();
//定義projection
//查詢要返回的欄位
default_all_threads_projection = new String[]{
        Telephony.Threads._ID,
        Telephony.Threads.DATE
, Telephony.Threads.SNIPPET, Telephony.Threads.MESSAGE_COUNT, Telephony.Threads.RECIPIENT_IDS, Telephony.Threads.SNIPPET, Telephony.Threads.SNIPPET_CHARSET, Telephony.Threads.READ, Telephony.Threads.ERROR, Telephony.Threads.HAS_ATTACHMENT
, Telephony.Threads.RECIPIENT_IDS }; //限制條件 mSelection = "_id IN (SELECT DISTINCT thread_id FROM sms where "+ "thread_id NOT NULL UNION SELECT DISTINCT thread_id FROM pdu where "+ "thread_id NOT NULL )"; Cursor query = getContentResolver().query(mALL_conversation_uri, default_all_threads_projection
, mSelection, null, null); mListView.setAdapter(new MyAdapter(MainActivity.this,query));

MyAdaper:

class MyAdapter extends CursorAdapter {
    private Context mContext;
    private final LayoutInflater layoutInflater;

    public MyAdapter(Context context, Cursor c) {
        super(context, c);
        mContext = context;
        layoutInflater = LayoutInflater.from(mContext);
    }

    public MyAdapter(Context context, Cursor c, boolean autoRequery, Context mContext, LayoutInflater layoutInflater) {
        super(context, c, autoRequery);
        this.mContext = mContext;
        this.layoutInflater = layoutInflater;
    }

    public MyAdapter(Context context, Cursor c, int flags, Context mContext, LayoutInflater layoutInflater) {
        super(context, c, flags);
        this.mContext = mContext;
        this.layoutInflater = layoutInflater;
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

        View view = layoutInflater.inflate(R.layout.test_item, parent, false);
        return view;
    }

    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        TextView mContent = view.findViewById(R.id.txt_content);
        String string = cursor.getString(cursor.getColumnIndex(Telephony.Threads.SNIPPET));
        mContent.setText(string);
    }
}

上面這種方法容易報錯ANR,故慎用!!

非同步查詢繫結:

MainActivity:

//開始查詢簡訊
public void startQuery(){
    conversationsQuery = new ConversationQuery(getContentResolver());
    conversationsQuery.startQuery(QueryParameter.ALL_CONVERSATION_QUERY_TOKEN,null,QueryParameter.QUERY_ALL_URI,QueryParameter.QUERY_PROJECTION,QueryParameter.QUERY_SELECTION,null,null);
}

//處理增刪改查
class ConversationQuery extends AsyncQueryHandler{

    public ConversationQuery(ContentResolver cr) {
        super(cr);

    }

    /***
     *
     * @param token 身份證
     * @param cookie
     * @param cursor 遊標 查詢返回的內容
     */
    @Override
    protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
        super.onQueryComplete(token, cookie, cursor);

        if (cursor != null && cursor.getCount() > 0){
            //cursor 開始的時候在-1 需要將其移動到第一個下標
            if (cursor.moveToFirst()){
                /**
                 *  從原始碼中可以看出呼叫此方法後會把當前的mCursor置為新傳過來的cursor把原來的cursor返回去並關掉
                    作用:當我們的Cursor變化時呼叫此方法
                    adapter.changeCursor(cursor),它的功能類似於adapter.notifyDataSetChanged()方法
                 */
                myAdapter.changeCursor(cursor);
            }
        }else{
            Toast.makeText(MainActivity.this,R.string.cursor_null,Toast.LENGTH_SHORT).show();
        }

    }
}

MyAdaper:

/**
 * Created by XX on 2018/4/30.
 */

class MyAdapter extends CursorAdapter {
    private Context mContext;
    private  LayoutInflater layoutInflater;

    /**
     *
     * @param context 上下文
     * @param c 遊標 cursor
     * @param autoRequery true時 資料庫更新將自動重新整理listView
     */
    public MyAdapter(Context context, Cursor c, boolean autoRequery) {
        super(context, c, autoRequery);
        this.mContext = context;
        this.layoutInflater = LayoutInflater.from(mContext);
    }

    public MyAdapter(Context context, Cursor c, int flags) {
        super(context, c, flags);
        this.mContext = context;
        this.layoutInflater = LayoutInflater.from(mContext);
    }

    @Override
    public View newView(Context context, Cursor cursor, ViewGroup parent) {

        View view = layoutInflater.inflate(R.layout.test_item, parent, false);
        return view;
    }

    /***
     *  instanceof: java裡面的二元運算子,
     * 判斷左邊的物件是否是右邊類的例項。假如是的話,返回true;假如不是的話,返回false。
     */
    @Override
    public void bindView(View view, Context context, Cursor cursor) {
        ConversationItem conversationItem = null;
        if (view instanceof ConversationItem){
            conversationItem = (ConversationItem) view;
        }

        if (conversationItem != null){
            ConversationBean conversationBean = new ConversationBean(cursor);
            conversationItem.bindValues(conversationBean);
        }
    }
}

ConversationItem:

/**
 * Created by Chauncy_Tan on 2018/5/10.
 *
 * ConversationItem 繼承了LinearLayout 沒錯我們的list item整個佈局的最外層就是一個LinearLayout
 * 為了更好地賦值我們將使用我們這個自定義的LinearLayout
 */



public class ConversationItem extends LinearLayout {

    private TextView mContent;

    public ConversationItem(Context context) {
        super(context);
    }

    public ConversationItem(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public ConversationItem(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        mContent = findViewById(R.id.txt_content);
    }
    //用於繫結為控制元件賦值
    public void bindValues(ConversationBean conversationBean){
        mContent.setText(conversationBean.getmContent());
    }
}

ConversationBean:

/**
 * Created by XX on 2018/5/10.
 */

public class ConversationBean {
    public ConversationBean(Cursor cursor) {
        if (cursor != null){
            String snippet = cursor.getString(cursor.getColumnIndex(Telephony.Threads.SNIPPET));
            setmContent(snippet);
        }
    }
    private String mContent;

    public String getmContent() {
        return mContent;
    }

    public void setmContent(String mContent) {
        this.mContent = mContent;
    }
}

ListView的每一個Item佈局檔案:

<com.example.test.ConversationItem
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="56dp">

    <TextView
        android:id="@+id/txt_content"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center"
        android:lines="1"
        android:text="TextView" />

   
</com.example.test.ConversationItem>

AsyncQueryHandler內部實現

AsyncQueryHandler類封裝了呼叫者執行緒與工作執行緒的互動過程。互動的主體是兩個Handler,一個執行在呼叫者執行緒中,一個執行在工作者執行緒中。通過提供onXXXComplete的回撥介面,實現事件的完成處理。


剛剛接觸query()以及startQuery()時對它的引數我是一臉懵逼,其實這些引數是能夠拼接成一個完整的SQL語句

startQuery()

token:就好比人的身份證,startQuery()可以多次呼叫返回多個結果,用token來區分

cookie:你想傳給onXXXComplete方法使用的一個物件。(暫時我還沒用到一直是null)

uri:你可以理解為從哪一個表查(from xxxtable)

projection:你查詢需要返回的欄位,null代表 * 

selection:你的查詢條件

selectionArgs:查詢引數

orderBy:排序條件

@param token A token passed into {@link #onQueryComplete} to identify
*  the query.
* @param cookie An object that gets passed into {@link #onQueryComplete}
* @param uri The URI, using the content:// scheme, for the content to
*         retrieve.
* @param projection A list of which columns to return. Passing null will
*         return all columns, which is discouraged to prevent reading data
*         from storage that isn't going to be used.
* @param selection A filter declaring which rows to return, formatted as an
*         SQL WHERE clause (excluding the WHERE itself). Passing null will
*         return all rows for the given URI.
* @param selectionArgs You may include ?s in selection, which will be
*         replaced by the values from selectionArgs, in the order that they
*         appear in the selection. The values will be bound as Strings.
* @param orderBy How to order the rows, formatted as an SQL ORDER BY
*         clause (excluding the ORDER BY itself). Passing null will use the
*         default sort order, which may be unordered.

query()

query沒有token以及Cookie引數沒有token因為它是同步的,cookie我還在瞭解

@param uri The URI, using the content:// scheme, for the content to
*         retrieve.
* @param projection A list of which columns to return. Passing null will
*         return all columns, which is inefficient.
* @param selection A filter declaring which rows to return, formatted as an
*         SQL WHERE clause (excluding the WHERE itself). Passing null will
*         return all rows for the given URI.
* @param selectionArgs You may include ?s in selection, which will be
*         replaced by the values from selectionArgs, in the order that they
*         appear in the selection. The values will be bound as Strings.
* @param sortOrder How to order the rows, formatted as an SQL ORDER BY
*         clause (excluding the ORDER BY itself). Passing null will use the
*         default sort order, which may be unordered.