本文主要是记录一些零碎的知识点
这篇文章在读取短信库时使用的ContentProvider,以及获取本地多媒体信息时,都是是直接使用的getContentResolver(),感觉有必要好好总结一下
一个应用实现ContentProvider来提供内容给别的应用来操作,
一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。
新建一个类继承ContentProvider,发现有以下几个方法
public boolean onCreate() 在创建ContentProvider时调用
public Cursor query(Uri, String[], String, String[], String) 用于查询指定Uri的ContentProvider,返回一个Cursor
public Uri insert(Uri, ContentValues) 用于添加数据到指定Uri的ContentProvider中
public int update(Uri, ContentValues, String, String[]) 用于更新指定Uri的ContentProvider中的数据
public int delete(Uri, String, String[]) 用于从指定Uri的ContentProvider中删除数据
public String getType(Uri) 用于返回指定的Uri中的数据的MIME类型
唉,本来只是想写个简单的demo实现一下这些功能,结果越写越多,强迫症呀,说一下具体实现,其实还想再做一些,做编辑的,做下拉上滑刷新的功能,唉,只是个demo呀,忘了初衷了。。。。。看见的实现的功能其实离要记录的已经很远了。。。。。
回归具体一步一步实现。
首先新建一个类,继承SQLiteOpenHelper,用来管理数据库的创建,打开,更新
import android.content.Context;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
/**
* Created by chenling on 2016/3/20.
*/
public class SlackDbHelper extends SQLiteOpenHelper {
public SlackDbHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) {
super(context, name, factory, version);
Log.i("slack", "SlackDbHelper SQLiteDatabase..........");
}
@Override
public void onCreate(SQLiteDatabase db) {
String ddl="create table users (_id integer primary key autoincrement,username varchar(100))";
db.execSQL(ddl);
Log.i("slack","onCreate SQLiteDatabase..........");
}
//更新表,版本号变化时
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
String ddl="create table users (id integer primary key autoincrement,username varchar(100),password varchar(100))";
db.execSQL(ddl);
Log.i("slack", "onUpgrade SQLiteDatabase..........");
}
}
接着新建一个Provider类继承ContentProvider,里面封装要做的操作,我这里是封装了一个数据库
import android.content.ContentProvider;
import android.content.ContentValues;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.os.Environment;
import android.support.annotation.Nullable;
import android.util.Log;
import java.io.File;
/**
* Created by chenling on 2016/3/20.
* 自定义的 ContentProvider,提供自定义的数据的CRUD
*/
public class SlackContentProvider extends ContentProvider {
private SlackDbHelper slackDbHelper;
private SQLiteDatabase sqLiteDatabase;
private String databasename;
private String tablename;
private File sdcardDir;
@Override
public boolean onCreate() {
Log.i("slack", "onCreate SlackContentProvider..........");
databasename = "userinfo";
tablename = "users";
sdcardDir= Environment.getExternalStorageDirectory();
Log.i("slack", "onCreate.........."+sdcardDir.getAbsolutePath());
slackDbHelper = new SlackDbHelper(getContext(),databasename,null,1);
sqLiteDatabase = slackDbHelper.getWritableDatabase();//得到可读可写的数据库
//return true 表示创建
return true;
}
@Nullable
@Override
public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) {
Log.i("slack", "query SlackContentProvider..........");
//public Cursor query (String table, String[] columns, String selection, String[] selectionArgs, String groupBy, String having, String orderBy)
return sqLiteDatabase.query(tablename,projection,selection,selectionArgs,null,null,sortOrder);
}
@Nullable
@Override
public String getType(Uri uri) {
return null;
}
@Nullable
@Override
public Uri insert(Uri uri, ContentValues values) {
Log.i("slack", "insert SlackContentProvider..........");
//public long insert (String table, String nullColumnHack, ContentValues values)
sqLiteDatabase.insert(tablename,null,values);
return null;
}
@Override
public int delete(Uri uri, String selection, String[] selectionArgs) {
Log.i("slack", "delete SlackContentProvider..........");
return sqLiteDatabase.delete(tablename,selection,selectionArgs);
}
@Override
public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
Log.i("slack", "update SlackContentProvider..........");
return sqLiteDatabase.update(tablename,values,selection,selectionArgs);
}
}
需要在AndroidManifest.xml文件里进行provider的注册,在加上对内存的读写权限,贴上全部代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.cl.android.content">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".SlcakContentResolverActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!--自定义ContentProvider注册-->
<provider
android:authorities="com.slack.cl.User_Info_Provider"
android:name=".SlackContentProvider"
android:exported="true"></provider>
</application>
</manifest>
这样一个provider我们就简单封装好了,其他的应用可以通过你注册时的uri使用这个对象,比如我上面在authorities属性里写的
看一下我们怎么使用,新建一个activity,我们要做的是通过uri调用我们自定义的provider
import android.app.AlertDialog;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.database.Cursor;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
/**
* Created by chenling on 2016/3/20.
* ContentResolver 操作自定义的数据
* */
public class SlcakContentResolverActivity extends AppCompatActivity {
private ListView listView;
private Uri uri;
private EditText username;
private ContentValues values;
private static final int EDIT_ID = 1;//编辑
private static final int DELETE_ID = 2;//删除
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (ListView)findViewById(R.id.dataListView);
username = (EditText)findViewById(R.id.username);
uri = Uri.parse("content://com.slack.cl.User_Info_Provider"); //自定义的URi ,这个是 Restful 风格的
values = new ContentValues();
/**
* ContextMenu用户手指长按某个View触发的菜单
* 实现场景:用户长按某个List元素,则弹出ContextMenu,选择菜单“Delete”,按下后,弹出AlertDialog,
* 请用户再去确定是否删除,确定后将数据从SQLite中删除,并更新ListView的显示。
* */
//向ListView注册Context Menu,当系统检测到用户长按某单元是,触发Context Menu弹出
registerForContextMenu(listView);
}
// 步骤2:创建ContextMenu同OptionMenu,用户长按元素后,会弹出菜单
@Override
public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
menu.add(Menu.NONE, EDIT_ID , Menu.NONE, "Edit");
menu.add(Menu.NONE, DELETE_ID , Menu.NONE, "Delete");
super.onCreateContextMenu(menu, v, menuInfo);
}
//步骤 3: ContextMenu的触发操作,例子将触发delete() 还可以做编辑等待
@Override
public boolean onContextItemSelected(MenuItem item) {
/* 在此处,我们关键引入 AdapterView.AdapterContextMenuInfo来获取单元的信息。
在有三个重要的信息。
1、id:The row id of the item for which the context menu is being displayed ,
在cursorAdaptor中,实际就是表格的_id序号;
2、position 是list的元素的顺序;
3、view就可以获得list中点击元素的View, 通过view可以获取里面的显示的信息
*/
AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo)item.getMenuInfo();
switch(item.getItemId()){
case DELETE_ID:
delete(info.id);
return true;
case EDIT_ID:
// edit(info.id);
return true;
default:
break;
}
return super.onContextItemSelected(item);
}
//步骤4: 对触发弹框,和Add的相似,确定后,更新数据库和更新ListView的显示,上次学习已有相类的例子,不再重复。其中getNameById是通过id查名字的方法。值得注意的是,为了内部类中使用,delete的参数采用来final的形式。
private void delete(final long rowId){
if(rowId>0){
new AlertDialog.Builder(this)
.setTitle("确定要删除?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String [] selectionArgs = new String[1];
selectionArgs[0] = String.valueOf(rowId);
String where = "_id = ?";
//public final int delete (Uri url, String where, String[] selectionArgs)
getContentResolver().delete(uri,where,selectionArgs);
selectAllInfo();
}
})
.setNegativeButton("取消", null)
.show();
}
}
//查询所有数据的按钮
public void selectAllData(View view) {
Log.i("slack", "selectAllData SlcakContentResolverActivity..........");
selectAllInfo();
}
//查询所有的数据,在listView里显示
private void selectAllInfo() {
// Log.i("slack", "selectAllInfo SlcakContentResolverActivity..........");
//public final Cursor query (Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder)
Cursor cursor = getContentResolver().query(uri,null,null,null,null);
// Log.i("slack", "query done SlcakContentResolverActivity..........");
// 用 CursorAdapter 必须是 _id 做为id的列名
CursorAdapter cursorAdapter= new CursorAdapter(getApplicationContext(),cursor,true) {
//新建一个视图来保存cursor指向的数据
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//找到布局和控件
// LayoutInflater inflater = LayoutInflater.from(context);
//一般都这样写,返回列表行元素,注意这里返回的就是bindView中的view
return LayoutInflater.from(context).inflate(R.layout.item,parent,false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
String username = cursor.getString(cursor.getColumnIndex("username"));
TextView textView = (TextView)view.findViewById(R.id.textView);
textView.setText(username);
}
};
Log.i("slack", "selectAllInfo done SlcakContentResolverActivity..........");
// listView.setAdapter(mSimpleAdapter);
listView.setAdapter(cursorAdapter);
}
public void addAData(View view) {
Log.i("slack", "addAData SlcakContentResolverActivity..........");
if(!TextUtils.isEmpty(username.getText().toString())){
Log.i("slack", "isEmpty username SlcakContentResolverActivity..........");
values.clear();
values.put("username", username.getText().toString());
getContentResolver().insert(uri, values);
Log.i("slack", "getContentResolver().insert SlcakContentResolverActivity..........");
//添加完查询一次,数据量要是大,不可以这么查,上滑下拉刷新
selectAllInfo();
}else{
Toast.makeText(this, "username null", Toast.LENGTH_SHORT).show();
}
}
}
上面的代码有几个地方需要解释一下,
添加数据时,使用的是getContentResolver().insert(uri, values);由于provider已经封装好了,所以新增时
privateContentValuesvalues;
values.put("username", username.getText().toString());是一个键值对的形式,键是对应的数据库的列名。
查询时,没有什么,由于是查询所有,没有任何参数 Cursor cursor = getContentResolver().query(uri,null,null,null,null);
就是显示时出现了一些问题,我想把数据显示在listView里,一开始使用的是这篇文章里的SimpleCursorAdapter,后来发现被废弃了,查阅说在参数后面加一个into flag,就加了一“0”,确实不报错了,但是数据就是显示不了,很郁闷,后来就干脆直接使用其父类CursorAdapder,但是依旧报错,好坑呀,后来看报错信息发现要使用这个Adapder,数据库id 的名字必须是“_id”
// 用 CursorAdapter 必须是 _id 做为id的列名
CursorAdapter cursorAdapter= new CursorAdapter(getApplicationContext(),cursor,true) {
//新建一个视图来保存cursor指向的数据
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
//找到布局和控件
// LayoutInflater inflater = LayoutInflater.from(context);
//一般都这样写,返回列表行元素,注意这里返回的就是bindView中的view
return LayoutInflater.from(context).inflate(R.layout.item,parent,false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
String username = cursor.getString(cursor.getColumnIndex("username"));
TextView textView = (TextView)view.findViewById(R.id.textView);
textView.setText(username);
}
};
估计
SimpleCursorAdapter也是同样的原因,没有验证。
又做了一个删除的,使用的是ContentMenu,用户长按时出现的菜单,这里把listView添加进去了,没有写listView的点击监听事件
这个一共ContentMenu分三步,(上面的代码都有详细的注释)
1.在onCreate函数里 注册 :registerForContextMenu(listView);
2.创建ContentMenu,这是需要重写onCreateContextMenu
3.ContentMenu里的元素的触发事件,需要重写onContextItemSelected
删除时写了一个弹窗 使用的是 AlertDialog里面的按钮也需要设置监听事件。
new AlertDialog.Builder(this)
.setTitle("确定要删除?")
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
String [] selectionArgs = new String[1];
selectionArgs[0] = String.valueOf(rowId);
String where = "_id = ?";
//public final int delete (Uri url, String where, String[] selectionArgs)
getContentResolver().delete(uri,where,selectionArgs);
selectAllInfo();
}
})
.setNegativeButton("取消", null)
.show();
这样就基本完成了,时间限制,编辑信息的没有实现,但是provider里都封装好了,最后再总结一下
一个应用实现ContentProvider来提供内容给别的应用来操作,
一个应用通过ContentResolver来操作别的应用数据,当然在自己的应用中也可以。
附件:源码下载:http://download.youkuaiyun.com/detail/i_do_can/9468004
gitHub地址:https://github.com/CL-window/content