一.ListView
1.三種Adapter構建ListView
ListView添加條目的時候, 可使用setAdapter(ListAdapter)方法, 經常使用的ListAdapter有三種
BaseAdapter: 定義一個類繼承BaseAdapter, 重寫4個抽象方法, ListView的條目是由getView()方法構建出來的
SimpleAdapter: 建立SimpleAdapter對象時, 傳入數據(List<Map<String, ?>>), 並指定數據的綁定關係
SimpleCursorAdapter: 建立SimpleCursorAdapter對象時, 傳入一個Cursor, 指定數據的綁定關係css
練習一:html
<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:java
注意:若要修改爲SimpleAdapter,不要忘記了修改AndroidManifest.xml中下面加粗位置部分。android
<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:git
注意:使用SimpleCusorAdapter,在查詢結果中要包含有「_id」這一列,這裏我把id取別名爲_id的方法解決。github
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,遊標中的數據就會放在對應的這些組件上 */ } }
運行結果:sql
2.監聽ListView的點擊
調用ListView.setOnItemClickListener(OnItemClickListener)方法註冊一個監聽器
在監聽器的onItemClick()方法中使用 parent.getItemAtPosition(position) 方法能夠獲取指定條目上的數據
BaseAdapter: 返回的就是自定義的getItem()方法中返回的數據
SimpleAdapter: 返回的是一個Map, 就是建立SimpleAdapter時List中的一個Map
SimpleCursorAdapter: 返回的是一個Cursor, 這個Cursor就是建立時傳入的Cursor, 可是已經經過moveToPosition()方法指定到點擊的索引了shell
練習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>
輔助類:app
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>
結果: