您的位置:首页 > 移动开发 > Android开发

36.Android 自定义ContentProvider

2015-11-15 15:49 369 查看

36.Android 自定义ContentProvider

Android 自定义ContentProvider
ContentProvider 介绍

ContentProvider 优点

Android 自带的ContentProvider

ContentProvider Uri 结构

Google Uri 工具类

UriMatcher 解析

自定义 ContentProvider

ContentProvider 介绍

有一种情景:假如A应用存储的数据,B也用到。那么,要怎么共享数据呢?

其实有很多种解决办法,比如存在SD卡上、网络等等。

其实Android上的相册、联系人也都是Google给Android系统写的App。也提供了对应的一套API给我们调用到其相应的数据,这就是ContentProvider。

ContentProvider是获取数据和存储都用的是统一的封装接口,涉及到Uri的概念。

当然了,如果用到Uri了,我们可以使用Google提供的工具类 UriMatcherContentUris ,以下会讲到这些。

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;
}

}

}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息