36.Android 自定义ContentProvider
2015-11-15 15:49
369 查看
36.Android 自定义ContentProvider
Android 自定义ContentProviderContentProvider 介绍
ContentProvider 优点
Android 自带的ContentProvider
ContentProvider Uri 结构
Google Uri 工具类
UriMatcher 解析
自定义 ContentProvider
ContentProvider 介绍
有一种情景:假如A应用存储的数据,B也用到。那么,要怎么共享数据呢?其实有很多种解决办法,比如存在SD卡上、网络等等。
其实Android上的相册、联系人也都是Google给Android系统写的App。也提供了对应的一套API给我们调用到其相应的数据,这就是ContentProvider。
ContentProvider是获取数据和存储都用的是统一的封装接口,涉及到Uri的概念。
当然了,如果用到Uri了,我们可以使用Google提供的工具类 UriMatcher 和 ContentUris ,以下会讲到这些。
ContentProvider 优点
可以获取系统原生一些App的数据。不同App之间可以共享数据。
对数据进行封装,存储和获取提供统一的API。
Android 自带的ContentProvider
如果想获取一些系统自带App的数据,可以参考:android.provider包下的ContentProvider。
ContentProvider Uri 结构
content://com.camnter.content.provider/message/6
content:// :Scheme
com.camnter.content.provider :Authority(主机名),这个ContentProvider的唯一标识。
message :Table(表名)
6 :Id
几个例子:
content://com.camnter.content.provider/message:表示操作message表的所有记录。
content://com.camnter.content.provider/message/6:表示操作message表的Id = 6的记录。
content://com.camnter.content.provider/message/6/content:表示操作message表的Id = 6的记录的content字段。
Google Uri 工具类
UriMatcher :public void addURI(String authority, String path, int code):添加Uri,Uri会被添加到一个ArrayList里,提供
match(Uri uri)去匹配。
public int match(Uri uri):根据Uri匹配改Uri是否被添加。
ContentUris:
public static long parseId(Uri contentUri):解析Uri取得Id。
public static Uri.Builder appendId(Uri.Builder builder, long id):添加Id。
public static Uri withAppendedId(Uri contentUri, long id):添加Id。
UriMatcher 解析
ContentUris的源码特别简单,整个类就调用了Uri类的两个方法 T T# 。所以就看看UriMatcher源码吧。
由于对其他类的依赖不多,我就自定义了一份UriMatcher。
import android.net.Uri; import java.util.ArrayList; import java.util.List; /** Utility class to aid in matching URIs in content providers. <p>To use this class, build up a tree of <code>UriMatcher</code> objects. For example: <pre> private static final int PEOPLE = 1; private static final int PEOPLE_ID = 2; private static final int PEOPLE_PHONES = 3; private static final int PEOPLE_PHONES_ID = 4; private static final int PEOPLE_CONTACTMETHODS = 7; private static final int PEOPLE_CONTACTMETHODS_ID = 8; private static final int DELETED_PEOPLE = 20; private static final int PHONES = 9; private static final int PHONES_ID = 10; private static final int PHONES_FILTER = 14; private static final int CONTACTMETHODS = 18; private static final int CONTACTMETHODS_ID = 19; private static final int CALLS = 11; private static final int CALLS_ID = 12; private static final int CALLS_FILTER = 15; private static final UriMatcher sURIMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { sURIMatcher.addURI("contacts", "people", PEOPLE); sURIMatcher.addURI("contacts", "people/#", PEOPLE_ID); sURIMatcher.addURI("contacts", "people/#/phones", PEOPLE_PHONES); sURIMatcher.addURI("contacts", "people/#/phones/#", PEOPLE_PHONES_ID); sURIMatcher.addURI("contacts", "people/#/contact_methods", PEOPLE_CONTACTMETHODS); sURIMatcher.addURI("contacts", "people/#/contact_methods/#", PEOPLE_CONTACTMETHODS_ID); sURIMatcher.addURI("contacts", "deleted_people", DELETED_PEOPLE); sURIMatcher.addURI("contacts", "phones", PHONES); sURIMatcher.addURI("contacts", "phones/filter/*", PHONES_FILTER); sURIMatcher.addURI("contacts", "phones/#", PHONES_ID); sURIMatcher.addURI("contacts", "contact_methods", CONTACTMETHODS); sURIMatcher.addURI("contacts", "contact_methods/#", CONTACTMETHODS_ID); sURIMatcher.addURI("call_log", "calls", CALLS); sURIMatcher.addURI("call_log", "calls/filter/*", CALLS_FILTER); sURIMatcher.addURI("call_log", "calls/#", CALLS_ID); } </pre> <p>Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, paths can start with a leading slash. For example: <pre> sURIMatcher.addURI("contacts", "/people", PEOPLE); </pre> <p>Then when you need to match against a URI, call {@link #match}, providing the URL that you have been given. You can use the result to build a query, return a type, insert or delete a row, or whatever you need, without duplicating all of the if-else logic that you would otherwise need. For example: <pre> public String getType(Uri url) { int match = sURIMatcher.match(url); switch (match) { case PEOPLE: return "vnd.android.cursor.dir/person"; case PEOPLE_ID: return "vnd.android.cursor.item/person"; ... snip ... return "vnd.android.cursor.dir/snail-mail"; case PEOPLE_ADDRESS_ID: return "vnd.android.cursor.item/snail-mail"; default: return null; } } </pre> instead of: <pre> public String getType(Uri url) { List<String> pathSegments = url.getPathSegments(); if (pathSegments.size() >= 2) { if ("people".equals(pathSegments.get(1))) { if (pathSegments.size() == 2) { return "vnd.android.cursor.dir/person"; } else if (pathSegments.size() == 3) { return "vnd.android.cursor.item/person"; ... snip ... return "vnd.android.cursor.dir/snail-mail"; } else if (pathSegments.size() == 3) { return "vnd.android.cursor.item/snail-mail"; } } } return null; } </pre> */ public class UriMatcher { public static final int NO_MATCH = -1; /** * Creates the root node of the URI tree. * 创建Uri树的根节点 * * @param code the code to match for the root URI */ public UriMatcher(int code) { mCode = code; mWhich = -1; mChildren = new ArrayList<UriMatcher>(); mText = null; } private UriMatcher() { mCode = NO_MATCH; mWhich = -1; mChildren = new ArrayList<UriMatcher>(); mText = null; } /** * Add a URI to match, and the code to return when this URI is * matched. URI nodes may be exact match string, the token "*" * that matches any text, or the token "#" that matches only * numbers. * 添加一个URI匹配,并且这里添加code值会在这个URI匹配成功的时候返回。URI节点可能精确匹配字符串, * token为“*”的时候会匹配为text,token为“#”的时候只会匹配为数字 * <p> * Starting from API level {@link android.os.Build.VERSION_CODES#JELLY_BEAN_MR2}, * this method will accept a leading slash in the path. * * @param authority the authority to match * ContentProvider的authority * * @param path the path to match. * may be used as a wild card for * any text, and # may be used as a wild card for numbers. * 匹配路径,*可能是作为一个通配符用于任何文本,和#可能用作通配符数字。 * * @param code the code that is returned when a URI is matched * against the given components. Must be positive. * 该URI匹配成功时返回的code */ public void addURI(String authority, String path, int code) { if (code < 0) { throw new IllegalArgumentException("code " + code + " is invalid: it must be positive"); } String[] tokens = null; if (path != null) { String newPath = path; // Strip leading slash if present. if (path.length() > 0 && path.charAt(0) == '/') { newPath = path.substring(1); } tokens = newPath.split("/"); } int numTokens = tokens != null ? tokens.length : 0; UriMatcher node = this; for (int i = -1; i < numTokens; i++) { String token = i < 0 ? authority : tokens[i]; ArrayList<UriMatcher> children = node.mChildren; int numChildren = children.size(); UriMatcher child; int j; for (j = 0; j < numChildren; j++) { child = children.get(j); if (token.equals(child.mText)) { node = child; break; } } if (j == numChildren) { // Child not found, create it child = new UriMatcher(); if (token.equals("#")) { child.mWhich = NUMBER; } else if (token.equals("*")) { child.mWhich = TEXT; } else { child.mWhich = EXACT; } child.mText = token; node.mChildren.add(child); node = child; } } node.mCode = code; } /** * Try to match against the path in a url. * 尝试匹配一个url的路径。 * * @param uri The url whose path we will match against. * 将匹配的url路径。 * * @return The code for the matched node (added using addURI), * or -1 if there is no matched node. * 匹配节点设置的code(在使用addURI时,给对应URI设置code), * 或者没有匹配节点,则反悔-1。 */ public int match(Uri uri) { final List<String> pathSegments = uri.getPathSegments(); final int li = pathSegments.size(); UriMatcher node = this; if (li == 0 && uri.getAuthority() == null) { return this.mCode; } for (int i=-1; i<li; i++) { String u = i < 0 ? uri.getAuthority() : pathSegments.get(i); ArrayList<UriMatcher> list = node.mChildren; if (list == null) { break; } node = null; int lj = list.size(); for (int j=0; j<lj; j++) { UriMatcher n = list.get(j); which_switch: switch (n.mWhich) { case EXACT: if (n.mText.equals(u)) { node = n; } break; case NUMBER: int lk = u.length(); for (int k=0; k<lk; k++) { char c = u.charAt(k); if (c < '0' || c > '9') { break which_switch; } } node = n; break; case TEXT: node = n; break; } if (node != null) { break; } } if (node == null) { return NO_MATCH; } } return node.mCode; } private static final int EXACT = 0; private static final int NUMBER = 1; private static final int TEXT = 2; private int mCode; private int mWhich; private String mText; private ArrayList<UriMatcher> mChildren; }
以上注释还有使用,Google告诉我们怎么使用UriMatcher的范例代码。
自定义 ContentProvider
代码都重复看过,都写上了注释。BaseContentProvider
public abstract class BaseContentProvider extends ContentProvider { // 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头 protected static final String MIME_SINGLE = "vnd.android.cursor.item/"; // 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头 protected static final String MIME_MULTIPLE = "vnd.android.cursor.dir/"; /** * Implement this to initialize your content provider on startup. * This method is called for all registered content providers on the * application main thread at application launch time. It must not perform * lengthy operations, or application startup will be delayed. * <p/> * <p>You should defer nontrivial initialization (such as opening, * upgrading, and scanning databases) until the content provider is used * (via {@link #query}, {@link #insert}, etc). Deferred initialization * keeps application startup fast, avoids unnecessary work if the provider * turns out not to be needed, and stops database errors (such as a full * disk) from halting application launch. * <p/> * <p>If you use SQLite, {@link SQLiteOpenHelper} * is a helpful utility class that makes it easy to manage databases, * and will automatically defer opening until first use. If you do use * SQLiteOpenHelper, make sure to avoid calling * {@link SQLiteOpenHelper#getReadableDatabase} or * {@link SQLiteOpenHelper#getWritableDatabase} * from this method. (Instead, override * {@link SQLiteOpenHelper#onOpen} to initialize the * database when it is first opened.) * * @return true if the provider was successfully loaded, false otherwise */ @Override public abstract boolean onCreate(); /** * Implement this to handle query requests from clients. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. * <p/> * Example client call:<p> * <pre>// Request a specific record. * Cursor managedCursor = managedQuery( * ContentUris.withAppendedId(Contacts.People.CONTENT_URI, 2), * projection, // Which columns to return. * null, // WHERE clause. * null, // WHERE clause value substitution * People.NAME + " ASC"); // Sort order.</pre> * Example implementation:<p> * <pre>// SQLiteQueryBuilder is a helper class that creates the * // proper SQL syntax for us. * SQLiteQueryBuilder qBuilder = new SQLiteQueryBuilder(); * <p/> * // Set the table we're querying. * qBuilder.setTables(DATABASE_TABLE_NAME); * <p/> * // If the query ends in a specific record number, we're * // being asked for a specific record, so set the * // WHERE clause in our query. * if((URI_MATCHER.match(uri)) == SPECIFIC_MESSAGE){ * qBuilder.appendWhere("_id=" + uri.getPathLeafId()); * } * <p/> * // Make the query. * Cursor c = qBuilder.query(mDb, * projection, * selection, * selectionArgs, * groupBy, * having, * sortOrder); * c.setNotificationUri(getContext().getContentResolver(), uri); * return c;</pre> * * @param uri The URI to query. This will be the full URI sent by the client; * if the client is requesting a specific record, the URI will end in a record number * that the implementation should parse and add to a WHERE or HAVING clause, specifying * that _id value. * @param projection The list of columns to put into the cursor. If * {@code null} all columns are included. * @param selection A selection criteria to apply when filtering rows. * If {@code null} then all rows are included. * @param selectionArgs You may include ?s in selection, which will be replaced by * the values from selectionArgs, in order that they appear in the selection. * The values will be bound as Strings. * @param sortOrder How the rows in the cursor should be sorted. * If {@code null} then the provider is free to define the sort order. * @return a Cursor or {@code null}. */ @Override public abstract Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder); /** * Implement this to handle requests for the MIME type of the data at the * given URI. The returned MIME type should start with * <code>vnd.android.cursor.item</code> for a single record, * or <code>vnd.android.cursor.dir/</code> for multiple items. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. * <p/> * <p>Note that there are no permissions needed for an application to * access this information; if your content provider requires read and/or * write permissions, or is not exported, all applications can still call * this method regardless of their access permissions. This allows them * to retrieve the MIME type for a URI when dispatching intents. * * @param uri the URI to query. * @return a MIME type string, or {@code null} if there is no type. */ @Override public abstract String getType(Uri uri); /** * Implement this to handle requests to insert a new row. * As a courtesy, call {@link ContentResolver#notifyChange(Uri, ContentObserver) notifyChange()} * after inserting. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. * * @param uri The content:// URI of the insertion request. This must not be {@code null}. * @param values A set of column_name/value pairs to add to the database. * This must not be {@code null}. * @return The URI for the newly inserted item. */ @Override public abstract Uri insert(Uri uri, ContentValues values); /** * Implement this to handle requests to delete one or more rows. * The implementation should apply the selection clause when performing * deletion, allowing the operation to affect multiple rows in a directory. * As a courtesy, call {@link ContentResolver#notifyChange(Uri, ContentObserver) notifyChange()} * after deleting. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. * <p/> * <p>The implementation is responsible for parsing out a row ID at the end * of the URI, if a specific row is being deleted. That is, the client would * pass in <code>content://contacts/people/22</code> and the implementation is * responsible for parsing the record number (22) when creating a SQL statement. * * @param uri The full URI to query, including a row ID (if a specific record is requested). * @param selection An optional restriction to apply to rows when deleting. * @param selectionArgs * @return The number of rows affected. * @throws SQLException */ @Override public abstract int delete(Uri uri, String selection, String[] selectionArgs); /** * Implement this to handle requests to update one or more rows. * The implementation should update all rows matching the selection * to set the columns according to the provided values map. * As a courtesy, call {@link ContentResolver#notifyChange(Uri, ContentObserver) notifyChange()} * after updating. * This method can be called from multiple threads, as described in * <a href="{@docRoot}guide/topics/fundamentals/processes-and-threads.html#Threads">Processes * and Threads</a>. * * @param uri The URI to query. This can potentially have a record ID if this * is an update request for a specific record. * @param values A set of column_name/value pairs to update in the database. * This must not be {@code null}. * @param selection An optional filter to match rows to update. * @param selectionArgs * @return the number of rows affected. */ @Override public abstract int update(Uri uri, ContentValues values, String selection, String[] selectionArgs); }
MessageSQLiteHelper
public class MessageSQLiteHelper extends SQLiteOpenHelper { private static final String TAG = "MessageSQLiteHelper"; private static final String DB_NAME = "message.db"; private static final int VERSION = 1; public static final String TB_MESSAGE = "tb_message"; public static final String TB_MESSAGE_SQL = "CREATE TABLE IF NOT EXISTS " + TB_MESSAGE + "(_id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT," + " content text)"; private static MessageSQLiteHelper ourInstance; public MessageSQLiteHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, name, factory, version); } public static MessageSQLiteHelper getInstance(Context context) { if (ourInstance == null) ourInstance = new MessageSQLiteHelper(context); return ourInstance; } public MessageSQLiteHelper(Context context) { this(context, DB_NAME, null, VERSION); } @Override public void onCreate(SQLiteDatabase db) { db.execSQL(TB_MESSAGE_SQL); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { Log.i(TAG, "onUpgrade"); } }
MessageContentProvider
public class MessageContentProvider extends BaseContentProvider { // 主机名 private static final String AUTHORITY = "com.camnter.content.provider"; // Message uri public static final Uri MESSAGE_URI = Uri.parse("content://" + AUTHORITY + "/message"); // 数据集的MIME类型字符串则应该以vnd.android.cursor.dir/开头 private static final String TOPIC_SINGLE = MIME_SINGLE + "message"; // 单一数据的MIME类型字符串应该以vnd.android.cursor.item/开头 private static final String TOPIC_MULTIPLE = MIME_MULTIPLE + "message"; // 有id匹配码 private static final int MESSAGE = 6; // 有无匹配码 public static final int MESSAGES = 7; // Message SQLite helper private MessageSQLiteHelper messageSQLiteHelper; private static final UriMatcher messageUriMatcher = new UriMatcher(UriMatcher.NO_MATCH); static { // content://com.camnter.content.provider/message messageUriMatcher.addURI(AUTHORITY, "message", MESSAGES); // content://com.camnter.content.provider/message/# messageUriMatcher.addURI(AUTHORITY, "message/#", MESSAGE); } @Override public boolean onCreate() { this.messageSQLiteHelper = MessageSQLiteHelper.getInstance(this.getContext()); return true; } @Override public String getType(Uri uri) { int match = messageUriMatcher.match(uri); switch (match) { case MESSAGE: return TOPIC_SINGLE; case MESSAGES: return TOPIC_MULTIPLE; default: return null; } } @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = this.messageSQLiteHelper.getWritableDatabase(); long id; switch (messageUriMatcher.match(uri)) { case MESSAGE: id = db.insert(MessageSQLiteHelper.TB_MESSAGE, "content", values); this.getContext().getContentResolver().notifyChange(uri, null); return ContentUris.withAppendedId(uri, id); case MESSAGES: id = db.insert(MessageSQLiteHelper.TB_MESSAGE, "content", values); String path = uri.toString(); this.getContext().getContentResolver().notifyChange(uri, null); // 新id的Uri替换旧id的Uri return Uri.parse(path.substring(0, path.lastIndexOf("/")) +"/"+ id); default: throw new IllegalArgumentException("Unknown URI " + uri); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = this.messageSQLiteHelper.getWritableDatabase(); int count; switch (messageUriMatcher.match(uri)) { case MESSAGE: count = db.delete(MessageSQLiteHelper.TB_MESSAGE, selection, selectionArgs); this.getContext().getContentResolver().notifyChange(uri, null); break; case MESSAGES: long messageId = ContentUris.parseId(uri); // 指定id String where = "_id=" + messageId; // 把其它条件附加上 where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; count = db.delete(MessageSQLiteHelper.TB_MESSAGE, where, selectionArgs); this.getContext().getContentResolver().notifyChange(uri, null); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = this.messageSQLiteHelper.getWritableDatabase(); int count; switch (messageUriMatcher.match(uri)) { case MESSAGE: long messageId = ContentUris.parseId(uri); // 指定id String where = "_id=" + messageId; // 把其它条件附加上 where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; count = db.update(MessageSQLiteHelper.TB_MESSAGE, values, where, selectionArgs); this.getContext().getContentResolver().notifyChange(uri, null); break; case MESSAGES: count = db.update(MessageSQLiteHelper.TB_MESSAGE, values, selection, selectionArgs); this.getContext().getContentResolver().notifyChange(uri, null); break; default: throw new IllegalArgumentException("Unknown URI " + uri); } return count; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = this.messageSQLiteHelper.getReadableDatabase(); Cursor cursor; switch (messageUriMatcher.match(uri)) { case MESSAGE: long messageId = ContentUris.parseId(uri); // 指定id String where = "_id=" + messageId; // 把其它条件附加上 where += !TextUtils.isEmpty(selection) ? " and (" + selection + ")" : ""; cursor = db.query(MessageSQLiteHelper.TB_MESSAGE, projection, where, selectionArgs, null, null, sortOrder); this.getContext().getContentResolver().notifyChange(uri, null); return cursor; case MESSAGES: cursor = db.query(MessageSQLiteHelper.TB_MESSAGE, projection, selection, selectionArgs, null, null, sortOrder); this.getContext().getContentResolver().notifyChange(uri, null); return cursor; default: throw new IllegalArgumentException("Unknown URI " + uri); } } }
CustomContentProviderActivity
public class CustomContentProviderActivity extends AppCompatActivity { private RecyclerView providerRV; private ProviderRecyclerViewAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); this.setContentView(R.layout.activity_custom_content_provider); this.getContentResolver().registerContentObserver(MessageContentProvider.MESSAGE_URI, true, new MessageProviderObserver(new Handler())); this.providerRV = (RecyclerView) this.findViewById(R.id.provider_rv); this.adapter = new ProviderRecyclerViewAdapter(this.getContentResolver(), MessageContentProvider.MESSAGE_URI); this.providerRV.setAdapter(this.adapter); this.initRecyclerView(); } private void initRecyclerView() { // 实例化LinearLayoutManager LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this); // 设置垂直布局 linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL); // 设置布局管理器 this.providerRV.setLayoutManager(linearLayoutManager); this.providerRV.setItemAnimator(new DefaultItemAnimator()); this.providerRV.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST)); // 使RecyclerView保持固定的大小,该信息被用于自身的优化 this.providerRV.setHasFixedSize(true); ArrayList<SQLiteData> allData = new ArrayList<>(); allData.add(new SQLiteData()); this.adapter.setList(allData); this.adapter.notifyDataSetChanged(); } private class MessageProviderObserver extends ContentObserver { /** * Creates a content observer. * * @param handler The handler to run {@link #onChange} on, or null if none. */ public MessageProviderObserver(Handler handler) { super(handler); } @Override public void onChange(boolean selfChange, Uri uri) { super.onChange(selfChange, uri); } } /** * Provider RecyclerView Adapter */ private class ProviderRecyclerViewAdapter extends EasyRecyclerViewAdapter implements View.OnClickListener { private static final int ITEM_PROVIDER_OPERATION = 0; private static final int ITEM_PROVIDER_DATA = 1; private ContentResolver resolver; private Uri uri; public ProviderRecyclerViewAdapter(ContentResolver resolver, Uri uri) { this.resolver = resolver; this.uri = uri; } @Override public int[] getItemLayouts() { return new int[]{R.layout.item_content_provider_operation, R.layout.item_content_provider_data}; } @Override public void onBindRecycleViewHolder(EasyRecyclerViewHolder easyRecyclerViewHolder, int position) { int itemType = this.getRecycleViewItemType(position); switch (itemType) { case ITEM_PROVIDER_OPERATION: easyRecyclerViewHolder.findViewById(R.id.provider_add_bt).setOnClickListener(this); easyRecyclerViewHolder.findViewById(R.id.provider_del_bt).setOnClickListener(this); easyRecyclerViewHolder.findViewById(R.id.provider_mod_bt).setOnClickListener(this); easyRecyclerViewHolder.findViewById(R.id.provider_query_bt).setOnClickListener(this); break; case ITEM_PROVIDER_DATA: ProviderData data = (ProviderData) this.getList().get(position); TextView idTV = easyRecyclerViewHolder.findViewById(R.id.provider_id_tv); TextView contentTV = easyRecyclerViewHolder.findViewById(R.id.provider_content_tv); idTV.setText(data.id + ""); contentTV.setText(data.content + ""); break; } } @Override public int getRecycleViewItemType(int i) { if (i == 0) { return ITEM_PROVIDER_OPERATION; } else { return ITEM_PROVIDER_DATA; } } /** * Called when a view has been clicked. * * @param v The view that was clicked. */ @Override public void onClick(View v) { switch (v.getId()) { case R.id.provider_add_bt: { ContentValues values = new ContentValues(); values.put("content", "Save you from anything"); this.resolver.insert(this.uri, values); this.refresh(); break; } case R.id.provider_del_bt: { this.resolver.delete(this.uri, null, null); this.refresh(); break; } case R.id.provider_mod_bt: { List<ProviderData> allData = this.queryAll(); int firstId = allData.get(0).id; ContentValues values = new ContentValues(); values.put("content", UUID.randomUUID().toString()); String path = this.uri.toString(); this.resolver.update(Uri.parse(path.substring(0, path.lastIndexOf("/")) +"/message/"+ firstId), values, null, null); this.refresh(); break; } case R.id.provider_query_bt: { this.refresh(); break; } } } private void refresh() { List<ProviderData> l = this.queryAll(); l.add(0,new ProviderData()); this.setList(l); this.notifyDataSetChanged(); } private List<ProviderData> queryAll() { List<ProviderData> allData = new ArrayList<>(); Cursor result = this.resolver.query(this.uri, null, null, null, null); for (result.moveToFirst(); !result.isAfterLast(); result.moveToNext()) { ProviderData data = new ProviderData(); data.id = result.getInt(result.getColumnIndex("_id")); data.content = result.getString(result.getColumnIndex("content")); allData.add(data); } result.close(); return allData; } } }
相关文章推荐
- 使用C++实现JNI接口需要注意的事项
- Android IPC进程间通讯机制
- Android Manifest 用法
- [转载]Activity中ConfigChanges属性的用法
- Android之获取手机上的图片和视频缩略图thumbnails
- Android之使用Http协议实现文件上传功能
- Android学习笔记(二九):嵌入浏览器
- android string.xml文件中的整型和string型代替
- i-jetty环境搭配与编译
- android之定时器AlarmManager
- android wifi 无线调试
- Android Native 绘图方法
- Android java 与 javascript互访(相互调用)的方法例子
- android 代码实现控件之间的间距
- android FragmentPagerAdapter的“标准”配置
- Android"解决"onTouch和onClick的冲突问题
- android:installLocation简析
- android searchView的关闭事件
- SourceProvider.getJniDirectories