同步查询绑定:
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.