編輯:關於Android編程
t Provider 屬於Android應用程序的組件之一,作為應用程序之間唯一的共享數據的途徑,Content Provider 主要的功能就是存儲並檢索數據以及向其他應用程序提供訪問數據的借口。
Android 系統為一些常見的數據類型(如音樂、視頻、圖像、手機通信錄聯系人信息等)內置了一系列的 Content Provider, 這些都位於android.provider包下。持有特定的許可,可以在自己開發的應用程序中訪問這些Content Provider。
讓自己的數據和其他應用程序共享有兩種方式:創建自己的Content Provier(即繼承自ContentProvider的子類) 或者是將自己的數據添加到已有的Content Provider中去,後者需要保證現有的Content Provider和自己的數據類型相同且具有該 Content Provider的寫入權限。對於Content Provider,最重要的就是數據模型(data model) 和 URI。
1.數據模型
Content Provider 將其存儲的數據以數據表的形式提供給訪問者,在數據表中每一行為一條記錄,每一列為具有特定類型和意義的數據。每一條數據記錄都包括一個 "_ID" 數值字段,改字段唯一標識一條數據。
2.URI
URI,每一個Content Provider 都對外提供一個能夠唯一標識自己數據集(data set)的公開URI, 如果一個Content Provider管理多個數據集,其將會為每個數據集分配一個獨立的URI。所有的Content Provider 的URI 都以"content://" 開頭,其中"content:"是用來標識數據是由Content Provider管理的 schema。
在幾乎所有的Content Provider 的操作中都會用到URI,因此一般來講,如果是自己開發的Content Provider,最好將URI定義為常量,這樣在簡化開發的同時也提高了代碼的可維護性。
首先來介紹如何訪問Content Provider中的數據,訪問 Content Provider中的數據主要通過ContentResolver對象,ContentResolver類提供了成員方法可以用來對Content Provider 中的數據進行查詢、插入、修改和刪除等操作。 以查詢為例,查詢一個 Content Provider 需要掌握如下的信息。
唯一標識Content Provider 的URI
需要訪問的數據字段名稱。
該數據字段的數據類型
提示: 如果需要訪問特定的某條數據記錄,只需該記錄的ID 即可。
查詢Content Provider的方法有兩個:ContentResolver的query() 和 Activity 對象的 managedQuery(),二者接收的參數均相同,返回的都是Cursor 對象,唯一不同的是 使用managedQuery 方法可以讓Activity 來管理 Cursor 的生命周期。
被管理的Cursor 會在 Activity進入暫停狀態的時候調用自己的 deactivate 方法自行卸載,而在Activity回到運行狀態時會調用自己的requery 方法重新查詢生成的Cursor對象。如果一個未被管理的Cursor對象想被Activity管理,可以調用Activity的 startManagingCursor方法來實現。
Android應用程序可以使用文件或SqlLite數據庫來存儲數據。Content Provider提供了一種多應用間數據共享的方式,比如:聯系人信息可以被多個應用程序訪問。Content Provider是個實現了一組用於提供其他應用程序存取數據的標准方法的類。
應用程序可以在Content Provider中執行如下操作:
查詢數據
修改數據
添加數據
刪除數據
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/MainActivity.java
package com.amaker.ch10.app;import android.app.Activity;import android.content.ContentUris;import android.content.ContentValues;import android.database.Cursor;import android.net.Uri;import android.os.Bundle;import android.util.Log;import com.amaker.ch10.app.Employees.Employee;public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
// 添加 insert();
// 查詢 query();
// 更新 update();
// 查詢 query();
// 刪除 del();
// 查詢 query();
}
// 刪除方法 private void del(){
// 刪除ID為1的記錄 Uri uri = ContentUris.withAppendedId(Employee.CONTENT_URI, 1);
// 獲得ContentResolver,並刪除 getContentResolver().delete(uri, null, null);
}
// 更新 private void update(){
// 更新ID為1的記錄 Uri uri = ContentUris.withAppendedId(Employee.CONTENT_URI, 1);
ContentValues values = new ContentValues();
// 添加員工信息 values.put(Employee.NAME, "hz.guo");
values.put(Employee.GENDER, "male");
values.put(Employee.AGE,31);
// 獲得ContentResolver,並更新 getContentResolver().update(uri, values, null, null);
}
// 查詢 private void query(){
// 查詢列數組 String[] PROJECTION = new String[] {
Employee._ID, // 0 Employee.NAME, // 1 Employee.GENDER, // 2 Employee.AGE // 3 };
// 查詢所有備忘錄信息 Cursor c = managedQuery(Employee.CONTENT_URI, PROJECTION, null,
null, Employee.DEFAULT_SORT_ORDER);
// 判斷游標是否為空 if (c.moveToFirst()) {
// 遍歷游標 for (int i = 0; i < c.getCount(); i++) {
c.moveToPosition(i);
// 獲得姓名 String name = c.getString(1);
String gender = c.getString(2);
int age = c.getInt(3);
// 輸出日志 Log.i("emp", name+":"+gender+":"+age);
}
}
}
// 插入 private void insert(){
// 聲明Uri Uri uri = Employee.CONTENT_URI;
// 實例化ContentValues ContentValues values = new ContentValues();
// 添加員工信息 values.put(Employee.NAME, "amaker");
values.put(Employee.GENDER, "male");
values.put(Employee.AGE,30);
// 獲得ContentResolver,並插入 getContentResolver().insert(uri, values);
}}
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/Employees.java
package com.amaker.ch10.app;import android.net.Uri;import android.provider.BaseColumns;/** * 通訊錄常量類
*/ public final class Employees {
// 授權常量 public static final String AUTHORITY = "com.amaker.provider.Employees";
private Employees() {}
// 內部類 public static final class Employee implements BaseColumns {
// 構造方法 private Employee() {}
// 訪問Uri public static final Uri CONTENT_URI = Uri.parse("content://" + AUTHORITY + "/employee");
// 內容類型 public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.amaker.employees";
public static final String CONTENT_ITEM_TYPE = "vnd.android.cursor.item/vnd.amaker.employees";
// 默認排序常量 public static final String DEFAULT_SORT_ORDER = "name DESC";// 按姓名排序
// 表字段常量 public static final String NAME = "name"; // 姓名 public static final String GENDER= "gender"; // 性別 public static final String AGE = "age"; // 年齡 }}
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/EmployeeProvider.java
package com.amaker.ch10.app;import java.util.HashMap;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.database.sqlite.SQLiteQueryBuilder;import android.net.Uri;import android.text.TextUtils;import com.amaker.ch10.app.Employees.Employee;public class EmployeeProvider extends ContentProvider{
// 數據庫幫助類 private DBHelper dbHelper;
// Uri工具類 private static final UriMatcher sUriMatcher;
// 查詢、更新條件 private static final int EMPLOYEE = 1;
private static final int EMPLOYEE_ID = 2;
// 查詢列集合 private static HashMap<String, String> empProjectionMap;
static {
// Uri匹配工具類 sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
sUriMatcher.addURI(Employees.AUTHORITY, "employee", EMPLOYEE);
sUriMatcher.addURI(Employees.AUTHORITY, "employee/#", EMPLOYEE_ID);
// 實例化查詢列集合 empProjectionMap = new HashMap<String, String>();
// 添加查詢列 empProjectionMap.put(Employee._ID, Employee._ID);
empProjectionMap.put(Employee.NAME, Employee.NAME);
empProjectionMap.put(Employee.GENDER, Employee.GENDER);
empProjectionMap.put(Employee.AGE, Employee.AGE);
}
// 創建是調用 public boolean onCreate() {
// 實例化數據庫幫助類 dbHelper = new DBHelper(getContext());
return true;
}
// 添加方法 public Uri insert(Uri uri, ContentValues values) {
// 獲得數據庫實例 SQLiteDatabase db = dbHelper.getWritableDatabase();
// 插入數據,返回行ID long rowId = db.insert(DBHelper.EMPLOYEES_TABLE_NAME, Employee.NAME, values);
// 如果插入成功返回uri if (rowId > 0) {
Uri empUri = ContentUris.withAppendedId(Employee.CONTENT_URI, rowId);
getContext().getContentResolver().notifyChange(empUri, null);
return empUri;
}
return null;
}
// 刪除方法 public int delete(Uri uri, String selection, String[] selectionArgs) {
// 獲得數據庫實例 SQLiteDatabase db = dbHelper.getWritableDatabase();
// 獲得數據庫實例 int count;
switch (sUriMatcher.match(uri)) {
// 根據指定條件刪除 case EMPLOYEE:
count = db.delete(DBHelper.EMPLOYEES_TABLE_NAME, selection, selectionArgs);
break;
// 根據指定條件和ID刪除 case EMPLOYEE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.delete(DBHelper.EMPLOYEES_TABLE_NAME, Employee._ID + "=" + noteId
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("錯誤的 URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}
// 獲得類型 public String getType(Uri uri) {
return null;
}
// 查詢方法 public Cursor query(Uri uri, String[] projection, String selection,
String[] selectionArgs, String sortOrder) {
SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
switch (sUriMatcher.match(uri)) {
// 查詢所有 case EMPLOYEE:
qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);
qb.setProjectionMap(empProjectionMap);
break;
// 根據ID查詢 case EMPLOYEE_ID:
qb.setTables(DBHelper.EMPLOYEES_TABLE_NAME);
qb.setProjectionMap(empProjectionMap);
qb.appendWhere(Employee._ID + "=" + uri.getPathSegments().get(1));
break;
default:
throw new IllegalArgumentException("Uri錯誤! " + uri);
}
// 使用默認排序 String orderBy;
if (TextUtils.isEmpty(sortOrder)) {
orderBy = Employee.DEFAULT_SORT_ORDER;
} else {
orderBy = sortOrder;
}
// 獲得數據庫實例 SQLiteDatabase db = dbHelper.getReadableDatabase();
// 返回游標集合 Cursor c = qb.query(db, projection, selection, selectionArgs, null, null, orderBy);
c.setNotificationUri(getContext().getContentResolver(), uri);
return c;
}
// 更新方法 public int update(Uri uri, ContentValues values, String selection,
String[] selectionArgs) {
// 獲得數據庫實例 SQLiteDatabase db = dbHelper.getWritableDatabase();
int count;
switch (sUriMatcher.match(uri)) {
// 根據指定條件更新 case EMPLOYEE:
count = db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, selection, selectionArgs);
break;
// 根據指定條件和ID更新 case EMPLOYEE_ID:
String noteId = uri.getPathSegments().get(1);
count = db.update(DBHelper.EMPLOYEES_TABLE_NAME, values, Employee._ID + "=" + noteId
+ (!TextUtils.isEmpty(selection) ? " AND (" + selection + ')' : ""), selectionArgs);
break;
default:
throw new IllegalArgumentException("錯誤的 URI " + uri);
}
getContext().getContentResolver().notifyChange(uri, null);
return count;
}}
/Chapter10_ContentProvider_01_Test02/src/com/amaker/ch10/app/DBHelper.java
package com.amaker.ch10.app;import android.content.Context;import android.database.sqlite.SQLiteDatabase;import android.database.sqlite.SQLiteOpenHelper;import com.amaker.ch10.app.Employees.Employee;/** *
* 數據庫工具類
*/ public class DBHelper extends SQLiteOpenHelper{
// 數據庫名稱常量 private static final String DATABASE_NAME = "Employees.db";
// 數據庫版本常量 private static final int DATABASE_VERSION = 1;
// 表名稱常量 public static final String EMPLOYEES_TABLE_NAME = "employee";
// 構造方法 public DBHelper(Context context) {
// 創建數據庫 super(context, DATABASE_NAME,null, DATABASE_VERSION);
}
// 創建時調用 public void onCreate(SQLiteDatabase db) {
db.execSQL("CREATE TABLE " + EMPLOYEES_TABLE_NAME + " (" + Employee._ID + " INTEGER PRIMARY KEY," + Employee.NAME + " TEXT," + Employee.GENDER + " TEXT," + Employee.AGE + " INTEGER" + ");");
}
// 版本更新時調用 public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
// 刪除表 db.execSQL("DROP TABLE IF EXISTS employee");
onCreate(db);
}}
/Chapter10_ContentProvider_01_Test02/AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.amaker.ch10.app" android:versionCode="1" android:versionName="1.0"> <application android:icon="@drawable/icon" android:label="@string/app_name">
<provider android:name="EmployeeProvider"
android:authorities="com.amaker.provider.Employees"/>
<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> <uses-sdk android:minSdkVersion="3" /> </manifest>
首先從字面意思理解兩個詞onTouchEvent:觸發觸摸事件onInterceptTouchEvent:觸發攔截觸摸事件通過查看源代碼及類繼承關系onIntercept
本文主要介紹Android ViewGroup/View的繪制流程,及常用的自定義ViewGroup的方法。在此基礎上介紹動態控制View的位置的三種方法,並給出最佳的
這是我在使用Android Studio過程中接觸到的一些快捷鍵,和大家分享,後面會繼續完善此文,也歡迎大家踴躍補充,一起完善。快捷鍵刪除並剪貼行:Ctrl+X復制一行:
Android開發之給應用簽名打包什麼是簽名打包?在Android 系統中,所有安裝到系統的應用程序都必有一個數字證書,此數字證書用於標識應用程序的作者和在應用程序之間建