java攻城狮之路(Android篇)--ListView与ContentProvider
一.ListView
1.三种Adapter构建ListView
ListView添加条目的时候, 可以使用setAdapter(ListAdapter)方法, 常用的ListAdapter有三种
BaseAdapter: 定义一个类继承BaseAdapter, 重写4个抽象方法, ListView的条目是由getView()方法构建出来的
SimpleAdapter: 创建SimpleAdapter对象时, 传入数据(List<Map<String, ?>>), 并指定数据的绑定关系
SimpleCursorAdapter: 创建SimpleCursorAdapter对象时, 传入一个Cursor, 指定数据的绑定关系
练习一:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.shellway.sqlite.MainActivity" android:background="@color/abc_search_url_text_normal"> <ListView android:id="@+id/id_LV" android:layout_width="fill_parent" android:layout_height="fill_parent" /> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:padding="10dp" > <TextView android:id="@+id/idTV" android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="1" /> <TextView android:id="@+id/nameTV" android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="张三" /> <TextView android:id="@+id/balanceTV" android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="10000" /> </LinearLayout>
package com.shellway.sqlite; import java.util.List; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; public class MainActivity extends ActionBarActivity { private ListView lv; private List<Person> persons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView PersonDAO dao = new PersonDAO(this); persons = dao.findAll(); //给ListView添加Adapter,按照Adapter中的方法对ListView添加条目 lv.setAdapter(new myAdapter()); } //定义Adapter,把每个Person对象生成一个条目,将所有条目装入ListView private class myAdapter extends BaseAdapter{ @Override public int getCount() { //返回ListView中要装入的条目的数量 return persons.size(); } @Override public Object getItem(int position) {//点哪个条目就返回哪个条目的对象 return persons.get(position); } @Override public long getItemId(int position) {//返回条目的ID return position; } @Override //返回指定位置上的View,会被添加到ListView中(即一个Person构建成一个View,然后挂到ListView) public View getView(int position, View convertView, ViewGroup parent) { Person p = persons.get(position); //构建成一个条目(View),第三个参数是要挂到谁身上,这里写null它会自动返回到LListView中 View item = View.inflate(getApplicationContext(), R.layout.item, null); TextView idTV = (TextView) item.findViewById(R.id.idTV); TextView nameTV = (TextView) item.findViewById(R.id.nameTV); TextView balanceTV = (TextView) item.findViewById(R.id.balanceTV); idTV.setText(p.getId()+""); nameTV.setText(p.getName()); balanceTV.setText(p.getBalance()+""); return item; } } }
SimpleAdapter:
注意:若要修改成SimpleAdapter,不要忘记了修改AndroidManifest.xml中下面加粗位置部分。
<activity android:name=".SimpleAdapterActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity>
package com.shellway.sqlite; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; public class SimpleAdapterActivity extends ActionBarActivity { private ListView lv; private List<Person> persons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView PersonDAO dao = new PersonDAO(this); persons = dao.findAll(); List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); for (Person p : persons) { Map<String, Object> map = new HashMap<String, Object>(); map.put("id", p.getId()); map.put("name", p.getName()); map.put("balance", p.getBalance()); data.add(map); } lv.setAdapter(new SimpleAdapter(this, data , R.layout.item, new String[]{"id","name","balance"}, new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV})); /**SimpleAdapter * 参数1:上下文环境 * 参数2:数据,List<Map<String, Object>>每个Person装入一个Map,再将Map装入List * 参数3:布局文件的资源id * 参数4:Map中的Key,和参数5中的id对应,将指定key的value放入View中指定id对应和组件上 * 参数5:View中的id */ } }
SimpleCusorAdapter:
注意:使用SimpleCusorAdapter,在查询结果中要包含有“_id”这一列,这里我把id取别名为_id的方法解决。
public Cursor queryAllCusor(){ SQLiteDatabase db = helper.getReadableDatabase(); //Cursor c = db.rawQuery("select id,name,balance from people", null); Cursor c = db.query("people", new String[]{"id as _id","name","balance"}, null, null, null, null, null, null); return c; }
package com.shellway.sqlite; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBarActivity; import android.database.Cursor; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; public class SimpleCusorAdapterActivity extends ActionBarActivity { private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView PersonDAO dao = new PersonDAO(this); Cursor c = dao.queryAllCusor(); lv.setAdapter(new SimpleCursorAdapter(this, R.layout.item, c, new String[]{"_id","name","balance"}, new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV})); /**SimpleAdapter * 参数1:上下文环境 * 参数2:布局文件的资源id * 参数3:包含数据的游标 * 参数4:游标中的列名 * 参数5:条目中的组件的ID,游标中的数据就会放在对应的这些组件上 */ } }
运行结果:
2.监听ListView的点击
调用ListView.setOnItemClickListener(OnItemClickListener)方法注册一个监听器
在监听器的onItemClick()方法中使用 parent.getItemAtPosition(position) 方法可以获取指定条目上的数据
BaseAdapter: 返回的就是自定义的getItem()方法中返回的数据
SimpleAdapter: 返回的是一个Map, 就是创建SimpleAdapter时List中的一个Map
SimpleCursorAdapter: 返回的是一个Cursor, 这个Cursor就是创建时传入的Cursor, 但是已经通过moveToPosition()方法指定到点击的索引了
练习2:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.shellway.sqlite.SimpleAdapterActivity" android:background="@color/abc_search_url_text_normal"> <LinearLayout android:id="@+id/ll" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="horizontal" > <TextView android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="序号" /> <TextView android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="姓名" /> <TextView android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="余额" /> </LinearLayout> <ListView android:id="@+id/id_LV" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_below="@id/ll" /> </RelativeLayout>
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal" android:padding="10dp" > <TextView android:id="@+id/idTV" android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:gravity="center" android:text="1" /> <TextView android:id="@+id/nameTV" android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="张三" /> <TextView android:id="@+id/balanceTV" android:textSize="20sp" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="2" android:gravity="center" android:text="10000" /> </LinearLayout>
package com.shellway.sqlite; import java.util.List; import android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; public class BaseAdapterActivity extends ActionBarActivity { private ListView lv; private List<Person> persons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView PersonDAO dao = new PersonDAO(this); persons = dao.findAll(); //给ListView添加Adapter,按照Adapter中的方法对ListView添加条目 lv.setAdapter(new myAdapter()); //给ListView添加条目点击监听器 lv.setOnItemClickListener(new myOnClickListener()); } private class myOnClickListener implements OnItemClickListener{ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //获取点击的条目上的数据,其内部实际上是调用:myAdapter.getItem() Person p = (Person) parent.getItemAtPosition(position); Toast.makeText(getApplicationContext(), p.getBalance()+"", Toast.LENGTH_SHORT).show(); } } //定义Adapter,把每个Person对象生成一个条目,将所有条目装入ListView private class myAdapter extends BaseAdapter{ @Override public int getCount() { //返回ListView中要装入的条目的数量 return persons.size(); } @Override public Object getItem(int position) {//点哪个条目就返回哪个条目的对象 return persons.get(position); } @Override public long getItemId(int position) {//返回条目的ID return position; } @Override //返回指定位置上的View,会被添加到ListView中(即一个Person构建成一个View,然后挂到ListView) public View getView(int position, View convertView, ViewGroup parent) { Person p = persons.get(position); //构建成一个条目(View),第三个参数是要挂到谁身上,这里写null它会自动返回到LListView中 View item = View.inflate(getApplicationContext(), R.layout.item, null); TextView idTV = (TextView) item.findViewById(R.id.idTV); TextView nameTV = (TextView) item.findViewById(R.id.nameTV); TextView balanceTV = (TextView) item.findViewById(R.id.balanceTV); idTV.setText(p.getId()+""); nameTV.setText(p.getName()); balanceTV.setText(p.getBalance()+""); return item; } } }
package com.shellway.sqlite; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.support.v7.app.ActionBarActivity; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; public class SimpleAdapterActivity extends ActionBarActivity { private ListView lv; private List<Person> persons; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView PersonDAO dao = new PersonDAO(this); persons = dao.findAll(); List<Map<String, Object>> data = new ArrayList<Map<String,Object>>(); for (Person p : persons) { Map<String, Object> map = new HashMap<String, Object>(); map.put("id", p.getId()); map.put("name", p.getName()); map.put("balance", p.getBalance()); data.add(map); } lv.setAdapter(new SimpleAdapter(this, data , R.layout.item, new String[]{"id","name","balance"}, new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV})); /**SimpleAdapter * 参数1:上下文环境 * 参数2:数据,List<Map<String, Object>>每个Person装入一个Map,再将Map装入List * 参数3:布局文件的资源id * 参数4:Map中的Key,和参数5中的id对应,将指定key的value放入View中指定id对应和组件上 * 参数5:View中的id */ lv.setOnItemClickListener(new myOnClickListener()); } private class myOnClickListener implements OnItemClickListener{ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //SimpleAdapter返回的是一个map Map<String,Object> map = (Map<String, Object>) parent.getItemAtPosition(position); Toast.makeText(getApplicationContext(), map.get("name").toString(), Toast.LENGTH_SHORT).show(); } } }
package com.shellway.sqlite; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import android.support.v4.widget.SimpleCursorAdapter; import android.support.v7.app.ActionBarActivity; import android.database.Cursor; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.SimpleAdapter; import android.widget.TextView; import android.widget.Toast; import android.widget.AdapterView.OnItemClickListener; public class SimpleCusorAdapterActivity extends ActionBarActivity { private ListView lv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView PersonDAO dao = new PersonDAO(this); Cursor c = dao.queryAllCusor(); lv.setAdapter(new SimpleCursorAdapter(this, R.layout.item, c, new String[]{"_id","name","balance"}, new int[]{R.id.idTV,R.id.nameTV,R.id.balanceTV})); /**SimpleAdapter * 参数1:上下文环境 * 参数2:布局文件的资源id * 参数3:包含数据的游标 * 参数4:游标中的列名 * 参数5:条目中的组件的ID,游标中的数据就会放在对应的这些组件上 */ lv.setOnItemClickListener(new myOnClickListener()); } private class myOnClickListener implements OnItemClickListener{ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //SimpleCursorAdapter返回的是一个Cursor Cursor c = (Cursor) parent.getItemAtPosition(position); Toast.makeText(getApplicationContext(), c.getString(0), Toast.LENGTH_SHORT).show(); } } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shellway.sqlite" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <instrumentation android:targetPackage="com.shellway.sqlite" android:name="android.test.InstrumentationTestRunner" /> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <uses-library android:name="android.test.runner" /> <activity android:name=".BaseAdapterActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
辅助类:
package com.shellway.sqlite; public class Person { private Integer id; private String name; private Integer balance; public Person() { super(); } public Person(String name, Integer balance) { super(); this.name = name; this.balance = balance; } public Person(Integer id, String name, Integer balance) { super(); this.id = id; this.name = name; this.balance = balance; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getBalance() { return balance; } public void setBalance(Integer balance) { this.balance = balance; } @Override public String toString() { return "person [id=" + id + ", name=" + name + ", balance=" + balance + "]"; } }
package com.shellway.sqlite; import android.content.Context; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; public class DBSQLiteHelper extends SQLiteOpenHelper { public DBSQLiteHelper(Context context){ super(context,"data.db" , null, 4); /** * 由于弗雷没有无参的构造函数,必须显式调用有参的构造函数 * 参数1:上下文环境,用来确定数据库文件存储的目录 * 参数2:数据库文件的名字 * 参数3:生成游标的工厂,填null就是使用默认的 * 参数4:数据库的版本,从1开始 */ } @Override public void onCreate(SQLiteDatabase db) { System.out.println("onCreate"); db.execSQL("CREATE TABLE people(id INTEGER PRIMARY KEY AUTOINCREMENT,name VACHAR(20))"); } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { System.out.println("onUpgrade"); db.execSQL("ALTER TABLE people ADD balance INTEGER"); } }
package com.shellway.sqlite; import java.util.ArrayList; import java.util.List; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; public class PersonDAO { private DBSQLiteHelper helper; public PersonDAO(Context context) { helper = new DBSQLiteHelper(context); } public long insert(Person p){ SQLiteDatabase db = helper.getWritableDatabase();//获取数据库链接(可写的) //db.execSQL("INSERT INTO people(name,balance) VALUES(?,?)", new Object[]{p.getName(),p.getBalance()} ); ContentValues values = new ContentValues(); values.put("name", p.getName()); values.put("balance", p.getBalance()); /** * 这里的第二个参数 可以随便填表里的任意一个字段名,是为了防止插入的字段名为空时会出错, * 当你确定插入的值不会出错时候可以null * 返回值表示最新插入的记录的ID */ long rows = db.insert("people", null, values); db.close(); return rows; } public void delete(Integer id){ SQLiteDatabase db = helper.getWritableDatabase(); //db.execSQL("DELETE FROM people WHERE id = ?", new Object[]{id}); db.delete("people", "id=?", new String[]{id+""}); db.close(); } public void update(Person p){ SQLiteDatabase db = helper.getWritableDatabase(); //db.execSQL("update people set name=?,balance=? where id=? ", new Object[]{p.getName(),p.getBalance(),p.getId()}); ContentValues values = new ContentValues(); values.put("name", p.getName()); values.put("balance", p.getBalance()); db.update("people", values, "id=?", new String[]{p.getId()+""}); db.close(); } //根据id查询某条记录 public Person query(Integer id){ /** * 查询时候应该优先使用getReadableDatabase()而不是getWritableDatabase(), * 其实getReadableDatabase是先获取getWritableDatabase,若获取失败则采用getReadableDatabase */ SQLiteDatabase db = helper.getReadableDatabase(); //Cursor c = db.rawQuery("select name,balance from people where id=?",new String[]{id+""}); Cursor c = db.query("people", new String[]{"id","name","balance"}, "id=?", new String[]{id+""}, null, null, null, null); Person p = null ; if (c.moveToNext()) { String name = c.getString(c.getColumnIndex("name")); int balance = c.getInt(2);//若直接用下标方式,则注意该字段的索引,游标的索引是从0开始的 p = new Person(id,name,balance); } c.close(); db.close(); return p; } //查询全部记录 public List<Person> findAll(){ SQLiteDatabase db = helper.getReadableDatabase(); //Cursor c = db.rawQuery("select id,name,balance from people", null); Cursor c = db.query("people", null, null, null, null, null, null, null); List<Person> persons = new ArrayList<Person>(); while (c.moveToNext()) { Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2)); persons.add(p); } c.close(); db.close(); return persons; } public Cursor queryAllCusor(){ SQLiteDatabase db = helper.getReadableDatabase(); //Cursor c = db.rawQuery("select id,name,balance from people", null); Cursor c = db.query("people", new String[]{"id as _id","name","balance"}, null, null, null, null, null, null); return c; } //查询记录总条数 public int queryCount(){ SQLiteDatabase db = helper.getReadableDatabase(); //Cursor c = db.rawQuery("select count(*) from people", null); Cursor c = db.query("people", new String[]{"count(*)"}, null, null, null, null, null); c.moveToNext(); int i = c.getInt(0); c.close(); db.close(); return i; } //分页查询 public List<Person> queryPage(int pageNum,int capacity){ String offset = (pageNum-1) * capacity +""; //偏移量,即是第几页的页数 String len = capacity + ""; //一页中显示的个数 SQLiteDatabase db = helper.getReadableDatabase(); //Cursor c = db.rawQuery("select id,name,balance from people limit ?,?", new String[]{offset,len}); Cursor c = db.query("people", new String[]{"id","name","balance"}, null, null, null, null, null, offset+","+len); List<Person> persons = new ArrayList<Person>(); while (c.moveToNext()) { Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2)); persons.add(p); } c.close(); db.close(); return persons; } }
package com.shellway.sqlite; import java.util.ArrayList; import java.util.List; import java.util.Random; import android.database.sqlite.SQLiteDatabase; import android.provider.SyncStateContract.Helpers; import android.test.AndroidTestCase; public class TestSQLite extends AndroidTestCase { public void test1(){ DBSQLiteHelper helper = new DBSQLiteHelper(getContext()); SQLiteDatabase sql = helper.getWritableDatabase(); /** * 获取可写的数据库连接 * 数据库文件不存在时,会创建数据库文件,并且执行onCreate()方法 * 数据库文件存在,且版本没有改变时,不执行任何方法 * 数据库文件存在,版本提升,执行onUpdate方法 */ } public void testInsert(){ PersonDAO personDAO = new PersonDAO(getContext()); long rows = personDAO.insert(new Person("KKK",20000)); System.out.println(rows); } public void testDelete(){ PersonDAO personDAO = new PersonDAO(getContext()); personDAO.delete(104); } public void testUpdate(){ PersonDAO personDAO = new PersonDAO(getContext()); personDAO.update(new Person(1,"www",30000)); } public void testQuery(){ PersonDAO personDAO = new PersonDAO(getContext()); System.out.println(personDAO.query(5)); } public void testFindAll(){ PersonDAO personDAO = new PersonDAO(getContext()); List<Person> persons = personDAO.findAll(); for (Person p : persons) { System.out.println(p); } } public void testQueryCount(){ PersonDAO personDAO = new PersonDAO(getContext()); int count = personDAO.queryCount(); System.out.println(count); } public void testQueryPage(){ PersonDAO personDAO = new PersonDAO(getContext()); List<Person> persons = personDAO.queryPage(3, 20); for (Person p : persons) { System.out.println(p); } } }
运行结果:
二.内容提供者(ContentProvider)
1.什么是ContentProvider
ContentProvider可以用来把程序中的数据对外进行共享, 提供增删改查的方法
ContentProvider中可以注册观察者, 监听数据的变化
* 2.怎么创建?
步骤1:在清单文件AndroidManifest.xml中注册
步骤2:定义类继承ContentProvider
package com.shellway.sqlite.provider; import android.content.ContentProvider; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; public class SQLiteProvider extends ContentProvider { @Override public boolean onCreate() { return false; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { System.out.println("query"); return null; } @Override public Uri insert(Uri uri, ContentValues values) { System.out.println("insert"); return null; } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { System.out.println("delete"); return 0; } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { System.out.println("update"); return 0; } @Override public String getType(Uri uri) { return null; } }
步骤3:另创建一个工程访问内容提供者
package com.shellway.other; import android.support.v7.app.ActionBarActivity; import android.content.ContentProvider; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.net.Uri; import android.os.Bundle; import android.view.Menu; import android.view.MenuItem; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //获取解析器对象 ContentResolver resolver = getContentResolver(); //访问内容提供者 ContentValues values = new ContentValues(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider"); resolver.insert(uri, values); resolver.delete(uri, null, null); resolver.update(uri, values, null, null); resolver.query(uri, null, null, null, null); } }
3.在手机上注册
将应用安装到手机上即可, 不用运行程序
* 4.怎么访问
获取解析器ContentResolver, 指定Uri
通过ContentResolver.insert(), delete(), update(), query()方法访问Uri关联的ContentProvider
5.Uri的处理
使用UriMatcher可以检查传入的Uri是否和指定的匹配
如果Uri带了id, 可以使用ContentUris获取id, 插入方法可以使用ContentUris给Uri加上id
练习:
package com.shellway.sqlite.provider; import com.shellway.sqlite.dao.DBSQLiteHelper; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; public class SQLiteProvider extends ContentProvider { private UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private DBSQLiteHelper helper; private static final int PERSON = 1; private static final int PERSON_ID = 2; @Override public boolean onCreate() { helper = new DBSQLiteHelper(getContext()); //设置一个Uri,如果匹配到person,则返回PERSON matcher.addURI("com.shellway.sqlite.provider", "person", PERSON); matcher.addURI("com.shellway.sqlite.provider", "person/#", PERSON_ID); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = helper.getReadableDatabase(); switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection==null ? "id=" + id : selection + " AND id =" + id ; case PERSON: return db.query("people", projection, selection, selectionArgs, null, null, sortOrder); default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = helper.getWritableDatabase(); switch (matcher.match(uri)) { case PERSON: long id = db.insert("people", "id", values); return ContentUris.withAppendedId(uri, id); default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = helper.getWritableDatabase(); switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection==null ? "id=" + id : selection + " AND id =" + id ; case PERSON: return db.delete("people", selection, selectionArgs); default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = helper.getWritableDatabase(); switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection==null ? "id=" + id : selection + " AND id =" + id ; case PERSON: return db.update("people", values, selection, selectionArgs); default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public String getType(Uri uri) { return null; } }
package com.shellway.other; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.test.AndroidTestCase; public class ProviderTest extends AndroidTestCase { public void test1(){ //获取解析器对象 ContentResolver resolver = getContext().getContentResolver(); //访问内容提供者 ContentValues values = new ContentValues(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider"); resolver.insert(uri, values); resolver.delete(uri, null, null); resolver.update(uri, values, null, null); resolver.query(uri, null, null, null, null); } public void testQuery(){ ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person"); Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC"); while (c.moveToNext()) { Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2)); System.out.println(p); } } public void testQuery2(){ ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/10"); Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC"); while (c.moveToNext()) { Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2)); System.out.println(p); } } public void testInsert(){ ContentResolver resolver = getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "Insert"); values.put("balance", "54321"); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person"); Uri count = resolver.insert(uri, values); System.out.println(count); } public void testUpdate(){ ContentResolver resolver = getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "Update"); values.put("balance", "12345"); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/106"); int i = resolver.update(uri, values, null, null); System.out.println(i); } public void testDelete(){ ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/107"); int i = resolver.delete(uri, null, null); System.out.println(i); } }
细节补充:
@Override public String getType(Uri uri) { switch (matcher.match(uri)) { case PERSON_ID: //一般返回的类型是:minetype? image/jpg html/text css/text return "vnd.android.cursor.item/person"; //这里表示返回的是单条的person数据 case PERSON: return "vnd.android.cursor.dir/person";//这里表示返回的是多条的person数据 default: throw new RuntimeException("Uri不能试看识别。。。 "); } }
public void testType(){ ContentResolver resolver = getContext().getContentResolver(); String s1 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person/106")); String s2 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person")); System.out.println(s1); System.out.println(s2); }
注意1:ProviderTest类中的ContentResolver resolver = getContext().getContentResolver();这一行不能放到类的成员变量里边,因为:.class ->.dex ->.app ->安装 ->开启进程(开启主线程)->创建ProviderTest对象 ->setContext ->测试方法 ->getConTest。否则会出现空指针异常,因为还没有setContext就getContext.
注意2:SQLiteProvider中的onCreate() 方法是在第一次启动时执行,然后会长期驻留在后台,除非是被杀死,否则不会再执行。
6.注册观察者
在应用程序中可以对ContentProvider注册一个观察者(ContentObserver)
定义类继承ContentObserver, 重写onChange()方法
使用ContentResolver.registerContentObserver(Uri, boolean, ContentObServer)方法可以注册, 传入指定Uri, 是否监听子级路径, 和一个观察者对象
在收到数据改变通知之后, 会自动执行onChange()方法
7.通知观察者
注册观察者之后, 需要在ContentProvider中进行通知, 观察者才能收到, 使用ContentResolver.notifyChange()方法可以通知数据的改变
练习:
package com.shellway.sqlite.ui; import java.util.List; import com.shellway.sqlite.R; import com.shellway.sqlite.dao.PersonDAO; import com.shellway.sqlite.domain.Person; import android.support.v4.widget.SearchViewCompat.OnCloseListenerCompat; import android.support.v7.app.ActionBarActivity; import android.content.ContentProvider; import android.database.ContentObserver; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.BaseAdapter; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; public class BaseAdapterActivity extends ActionBarActivity { private ListView lv; private List<Person> persons; private PersonDAO dao; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); lv = (ListView) findViewById(R.id.id_LV); //获取ListView dao = new PersonDAO(this); persons = dao.findAll(); //给ListView添加Adapter,按照Adapter中的方法对ListView添加条目 lv.setAdapter(new myAdapter()); //给ListView添加条目点击监听器 lv.setOnItemClickListener(new myOnClickListener()); //注册一个内容观察者 Uri uri = Uri.parse("content://com.shellway.sqlite.provider"); //第二个参数表示监听上面uri子路径下所有的变化,若改为false则只监听uri本身的变化 getContentResolver().registerContentObserver(uri , true, new MyContentObserver()); } private class MyContentObserver extends ContentObserver{ public MyContentObserver() { super(new Handler());//Handler()是一个处理器,目前没有用到 } @Override public void onChange(boolean selfChange) { persons = dao.findAll(); lv.setAdapter(new myAdapter()); } } private class myOnClickListener implements OnItemClickListener{ @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { //获取点击的条目上的数据,其内部实际上是调用:myAdapter.getItem() Person p = (Person) parent.getItemAtPosition(position); Toast.makeText(getApplicationContext(), p.getBalance()+"", Toast.LENGTH_SHORT).show(); } } //定义Adapter,把每个Person对象生成一个条目,将所有条目装入ListView private class myAdapter extends BaseAdapter{ @Override public int getCount() { //返回ListView中要装入的条目的数量 return persons.size(); } @Override public Object getItem(int position) {//点哪个条目就返回哪个条目的对象 return persons.get(position); } @Override public long getItemId(int position) {//返回条目的ID return position; } @Override //返回指定位置上的View,会被添加到ListView中(即一个Person构建成一个View,然后挂到ListView) public View getView(int position, View convertView, ViewGroup parent) { Person p = persons.get(position); //构建成一个条目(View),第三个参数是要挂到谁身上,这里写null它会自动返回到LListView中 View item = View.inflate(getApplicationContext(), R.layout.item, null); TextView idTV = (TextView) item.findViewById(R.id.idTV); TextView nameTV = (TextView) item.findViewById(R.id.nameTV); TextView balanceTV = (TextView) item.findViewById(R.id.balanceTV); idTV.setText(p.getId()+""); nameTV.setText(p.getName()); balanceTV.setText(p.getBalance()+""); return item; } } }
package com.shellway.sqlite.provider; import com.shellway.sqlite.dao.DBSQLiteHelper; import android.content.ContentProvider; import android.content.ContentUris; import android.content.ContentValues; import android.content.UriMatcher; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; import android.net.Uri; public class SQLiteProvider extends ContentProvider { private UriMatcher matcher = new UriMatcher(UriMatcher.NO_MATCH); private DBSQLiteHelper helper; private static final int PERSON = 1; private static final int PERSON_ID = 2; @Override public boolean onCreate() { helper = new DBSQLiteHelper(getContext()); //设置一个Uri,如果匹配到person,则返回PERSON matcher.addURI("com.shellway.sqlite.provider", "person", PERSON); matcher.addURI("com.shellway.sqlite.provider", "person/#", PERSON_ID); return true; } @Override public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs, String sortOrder) { SQLiteDatabase db = helper.getReadableDatabase(); switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection==null ? "id=" + id : selection + " AND id =" + id ; case PERSON: return db.query("people", projection, selection, selectionArgs, null, null, sortOrder); default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public Uri insert(Uri uri, ContentValues values) { SQLiteDatabase db = helper.getWritableDatabase(); switch (matcher.match(uri)) { case PERSON: long id = db.insert("people", "id", values); getContext().getContentResolver().notifyChange(uri, null); return ContentUris.withAppendedId(uri, id); default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public int delete(Uri uri, String selection, String[] selectionArgs) { SQLiteDatabase db = helper.getWritableDatabase(); switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection==null ? "id=" + id : selection + " AND id =" + id ; case PERSON: int count = db.delete("people", selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); return count; default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) { SQLiteDatabase db = helper.getWritableDatabase(); switch (matcher.match(uri)) { case PERSON_ID: long id = ContentUris.parseId(uri); selection = selection==null ? "id=" + id : selection + " AND id =" + id ; case PERSON: int count = db.update("people", values, selection, selectionArgs); getContext().getContentResolver().notifyChange(uri, null); return count; default: throw new RuntimeException("Uri不能试看识别。。。 "); } } @Override public String getType(Uri uri) { switch (matcher.match(uri)) { case PERSON_ID: //一般返回的类型是:minetype? image/jpg html/text css/text return "vnd.android.cursor.item/person"; //这里表示返回的是单条的person数据 case PERSON: return "vnd.android.cursor.dir/person";//这里表示返回的是多条的person数据 default: throw new RuntimeException("Uri不能试看识别。。。 "); } } }
package com.shellway.other; import android.content.ContentResolver; import android.content.ContentValues; import android.database.Cursor; import android.net.Uri; import android.test.AndroidTestCase; public class ProviderTest extends AndroidTestCase { public void test1(){ //获取解析器对象 ContentResolver resolver = getContext().getContentResolver(); //访问内容提供者 ContentValues values = new ContentValues(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider"); resolver.insert(uri, values); resolver.delete(uri, null, null); resolver.update(uri, values, null, null); resolver.query(uri, null, null, null, null); } public void testQuery(){ ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person"); Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC"); while (c.moveToNext()) { Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2)); System.out.println(p); } } public void testQuery2(){ ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/10"); Cursor c = resolver.query(uri, null, "balance>?", new String[]{8000+""}, "balance ASC"); while (c.moveToNext()) { Person p = new Person(c.getInt(0),c.getString(1),c.getInt(2)); System.out.println(p); } } public void testInsert(){ ContentResolver resolver = getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "Test5"); values.put("balance", "12345"); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person"); Uri count = resolver.insert(uri, values); } public void testUpdate(){ ContentResolver resolver = getContext().getContentResolver(); ContentValues values = new ContentValues(); values.put("name", "shellway"); values.put("balance", "10000"); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/116"); int i = resolver.update(uri, values, null, null); } public void testDelete(){ ContentResolver resolver = getContext().getContentResolver(); Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person/122"); int i = resolver.delete(uri, null, null); } public void testType(){ ContentResolver resolver = getContext().getContentResolver(); String s1 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person/106")); String s2 = resolver.getType(Uri.parse("content://com.shellway.sqlite.provider/person")); System.out.println(s1); System.out.println(s2); } }
package com.shellway.contentobserver; import android.support.v7.app.ActionBarActivity; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.view.Menu; import android.view.MenuItem; import android.widget.Toast; public class MainActivity extends ActionBarActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Uri uri = Uri.parse("content://com.shellway.sqlite.provider"); getContentResolver().registerContentObserver(uri , true, new MyContentOberver()); } private class MyContentOberver extends ContentObserver{ public MyContentOberver() { super(new Handler()); } @Override public void onChange(boolean selfChange) { Uri uri = Uri.parse("content://com.shellway.sqlite.provider/person"); Cursor c = getContentResolver().query(uri, null, null, null, "id desc limit 1"); while (c.moveToNext()) { Toast.makeText(getApplicationContext(), c.getString(1), Toast.LENGTH_SHORT).show(); } } } }
结果:一旦在A应用中通过内容提供者改变数据,则注册在该Uri上的观察者都会收到数据发生改变的通知,因为事先在内容提供者里都已经放了一个“通知者”,这里的通知者即是:getContext().getContentResolver().notifyChange(uri, null);所以,SQLite工程和B应用中的观察者都会接收到通知,从而执行自己的onChange()方法。
四.监听短信
1.获取源码
安装GIT工具:
在网站上下载com.android.providers.telephony源码:https://github.com/android
通过清单文件可以查找到Uri
2.监听改变
对指定Uri添加ContentOberver
在onChange方法中查询最新的记录, 收发短信时都会收到修改通知, 这样就能获取刚刚收发的短信了
package com.shellway.smsobserver; import java.text.SimpleDateFormat; import java.util.Date; import android.support.v7.app.ActionBarActivity; import android.content.ContentResolver; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; public class MainActivity extends ActionBarActivity { private Uri uri; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); //注册短信观察者 uri = Uri.parse("content://sms"); getContentResolver().registerContentObserver(uri , true, new MyObserver()); } private class MyObserver extends ContentObserver{ private long flag; public MyObserver() { super(new Handler()); } @Override public void onChange(boolean selfChange) { ContentResolver resolver = getContentResolver(); //查询最新的一条短信信息,因为收发短信,信息都会先往数据库存然后才显示在手机屏幕 Cursor c = resolver.query(uri, null, null, null, "_id desc limit 1"); while(c.moveToNext()){ String addr = c.getString(c.getColumnIndex("address")); String body = c.getString(c.getColumnIndex("body")); int type = c.getInt(c.getColumnIndex("type")); Long date = c.getLong(c.getColumnIndex("date")); SimpleDateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss"); Date d = new Date(date); String dd = sdf.format(d); if(flag!=date){ //因为回短信时会打印出三条重复的信息,这里利用时间来控制打印结果只为一条信息 System.out.println(dd +" " + (type == 1 ? "收":"发") + addr +" "+ body); flag = date; } } } } }
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shellway.smsobserver" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <uses-permission android:name="android.permission.READ_SMS"/> <uses-permission android:name="android.permission.WRITE_SMS"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <activity android:name=".MainActivity" android:label="@string/app_name" > <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> </application> </manifest>
结果:
五.读写联系人
1.获取原码
和监听短信相同, 获取com.android.providers.contacts源码
2.读取联系人
先读raw_contacts表中的id, 在根据id查找data表
3.写出联系人
先向raw_contacts表中写出一个id(自动生成)
在向data表写出3条对应数据, raw_contact_id一列使用刚刚插入的id
练习:(读联系人)
1.新建一个工程:
2.分析:
3.编写程序:
package com.shellway.contacts; import android.content.ContentResolver; import android.database.Cursor; import android.net.Uri; import android.test.AndroidTestCase; public class ContactsTest extends AndroidTestCase { public void testContexts(){ Uri idUri = Uri.parse("content://com.android.contacts/raw_contacts"); Uri dataUri = Uri.parse("content://com.android.contacts/data"); ContentResolver resolver = getContext().getContentResolver(); Cursor c = resolver.query(idUri, new String[]{"_id"}, null, null, null); /* String[] arr = c.getColumnNames();//打印raw_contacts表中的所有列名 for (String s : arr) { System.out.println(s); }*/ while(c.moveToNext()){ int idRaw = c.getInt(0); Cursor datac = resolver.query(dataUri, new String[]{"mimetype","data1","data2","data3"} , "raw_contact_id=?" , new String[]{idRaw+""} , null); while(datac.moveToNext()){ if(datac.getString(0).equals("vnd.android.cursor.item/name")){ System.out.println("姓名: "+ datac.getString(datac.getColumnIndex("data1"))); }else if(datac.getString(0).equals("vnd.android.cursor.item/phone_v2")){ System.out.println("电话: "+ datac.getString(datac.getColumnIndex("data1"))); }else if(datac.getString(0).equals("vnd.android.cursor.item/email_v2")){ System.out.println("邮箱: "+ datac.getString(datac.getColumnIndex("data1"))); } } } /* Cursor datac = resolver.query(dataUri, null, null, null, null);//打印data表中的所有列名 String[] s = datac.getColumnNames(); for (String st : s) { System.out.println(st); }*/ } }
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.shellway.contacts" android:versionCode="1" android:versionName="1.0" > <uses-sdk android:minSdkVersion="8" android:targetSdkVersion="21" /> <instrumentation android:targetPackage="com.shellway.contacts" android:name="android.test.InstrumentationTestRunner" /> <uses-permission android:name="android.permission.READ_CONTACTS"/> <uses-permission android:name="android.permission.WRITE_CONTACTS"/> <application android:allowBackup="true" android:icon="@drawable/ic_launcher" android:label="@string/app_name" android:theme="@style/AppTheme" > <uses-library android:name="android.test.runner" /> </application> </manifest>
结果: