正如ContentProvider提供了數(shù)據(jù)跨進(jìn)程訪問的標(biāo)準(zhǔn)接口,Contacts Provider提供了通訊錄app和社交app的用戶信息和數(shù)據(jù)的標(biāo)準(zhǔn)分訪問接口,你可以在你的app中通過調(diào)用ContentResolver的相關(guān)方法來訪問這些信息.
注: 本文主要關(guān)注如何使用intent(實際上好像不怎么focus,只占了1/4)來獲取,展示,和修改聯(lián)系人信息,比較簡單介紹整個處理的流程,當(dāng)然每一項都可以延伸下去以滿足更復(fù)雜的需求,具體可參考下列文章:
1. 檢索聯(lián)系人列表
要檢索出滿足一定條件的聯(lián)系人,要使用下面的機(jī)制:
- 匹配聯(lián)系人名字.
- 匹配某個具體的數(shù)據(jù)類型,如手機(jī)號.
- 匹配任意的數(shù)據(jù)類型.
1.1 請求Provider讀取權(quán)限
要對Contacts Provider進(jìn)行任何形式的查詢查找,都需要有READ_CONTACTS權(quán)限:
<uses-permission android:name="android.permission.READ_CONTACTS" />
1.2 顯示名字匹配結(jié)果
下面進(jìn)行的匹配操作是針對于ContactsContract.Contacts.
1.2.1 定義ListView和item的layout文件
目錄為res/layout/
:
// contacts_list_view.xml
<?xml version="1.0" encoding="utf-8"?>
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
// contacts_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"/>
1.2.2 定義一個Fragment來展示數(shù)據(jù)
...
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.widget.AdapterView;
// 方便常量獲取
import android.provider.ContactsContract.Contacts;
...
public class ContactsFragment extends Fragment implements
LoaderManager.LoaderCallbacks<Cursor>,
AdapterView.OnItemClickListener {
1.2.3 聲明全局變量
...
/*
* Defines an array that contains column names to move from
* the Cursor to the ListView.
*/
@SuppressLint("InlinedApi")
private final static String[] FROM_COLUMNS = {
Build.VERSION.SDK_INT
>= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY :
Contacts.DISPLAY_NAME
};
/*
* Defines an array that contains resource ids for the layout views
* that get the Cursor column contents. The id is pre-defined in
* the Android framework, so it is prefaced with "android.R.id"
*/
private final static int[] TO_IDS = {
android.R.id.text1
};
// Define global mutable variables
// Define a ListView object
ListView mContactsList;
// Define variables for the contact the user selects
// The contact's _ID value
long mContactId;
// The contact's LOOKUP_KEY
String mContactKey;
// A content URI for the selected contact
Uri mContactUri;
// An adapter that binds the result Cursor to the ListView
private SimpleCursorAdapter mCursorAdapter;
...
注: Contacts.DISPLAY_NAME_PRIMARY這個是在Android 3.0(API 11)引入的,如果你的項目的
minSdkVersion
設(shè)置的小于11,則Android Lint會產(chǎn)生警告,為了關(guān)掉對此的警告,就在FROM_COLUMNS
添加了@SuppressLint("InlinedApi")
注解.
1.2.4 初始化Fragment
// Empty public constructor, required by the system
public ContactsFragment() {
}
// A UI Fragment must inflate its View
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the fragment layout
View layout= inflater.inflate(R.layout.contacts_list_view,
container, false);
// Gets the ListView
mContactsList= (ListView) layout.findViewById(R.id.list);
return layout;
}
1.2.5 設(shè)置CursorAdapter
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
// Gets a CursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.contacts_list_item,
null,
FROM_COLUMNS, TO_IDS,
0);
// Sets the adapter for the ListView
mContactsList.setAdapter(mCursorAdapter);
}
1.2.6 設(shè)置item點擊事件
public void onActivityCreated(Bundle savedInstanceState) {
...
// Set the item click listener to be the current fragment.
mContactsList.setOnItemClickListener(this);
...
}
1.2.7 定義一個projection
也就是想要獲取的數(shù)據(jù)列.
...
@SuppressLint("InlinedApi")
private static final String[] PROJECTION =
{
Contacts._ID,
Contacts.LOOKUP_KEY,
Build.VERSION.SDK_INT
>= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY :
Contacts.DISPLAY_NAME
};
1.2.8 定義Cursor列索引常數(shù)
要從Cursor中獲取數(shù)據(jù),需要傳入列的索引值,該索引值是根據(jù)projection中的順序從0開始增加的,這里我們先定義:
// The column index for the _ID column
private static final int CONTACT_ID_INDEX = 0;
// The column index for the LOOKUP_KEY column
private static final int LOOKUP_KEY_INDEX = 1;
1.2.9 設(shè)置篩選條件
// Defines the text expression
@SuppressLint("InlinedApi")
private static final String SELECTION =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Contacts.DISPLAY_NAME_PRIMARY + " LIKE ?" :
Contacts.DISPLAY_NAME + " LIKE ?";
// Defines a variable for the search string,default as blank
private String mSearchString="";
// Defines the array to hold values that replace the ?
private String[] mSelectionArgs = { mSearchString };
1.2.10 重寫onItemClick()方法.
在1.2.6中調(diào)用了mContactsList.setOnItemClickListener(this)
方法將事件設(shè)置給類this,也就是我們這個Fragment,就要去實現(xiàn)這個Listener的抽象方法:
@Override
public void onItemClick(
AdapterView<?> parent, View item, int position, long rowID) {
// Get the Cursor
Cursor cursor = ((SimpleCursorAdapter) parent.getAdapter()).getCursor();
// Move to the selected contact
cursor.moveToPosition(position);
// Get the _ID value
mContactId = cursor.getLong(CONTACT_ID_INDEX);
// Get the selected LOOKUP KEY
mContactKey = cursor.getString(LOOKUP_KEY_INDEX);
// Create the contact's content Uri
mContactUri = Contacts.getLookupUri(mContactId, mContactKey);
/*
* You can use mContactUri as the content URI for retrieving
* the details for a contact.
*/
}
1.2.11 初始化loader
// Initializes the loader
getLoaderManager().initLoader(0, null, this);
1.2.12 實現(xiàn)LoaderManager.LoaderCallbacks的抽象方法.
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
/*
* Makes search string into pattern and
* stores it in the selection array
*/
mSelectionArgs[0] = "%" + mSearchString + "%";
// Starts the query
return new CursorLoader(
getActivity(),
Contacts.CONTENT_URI,
PROJECTION,
SELECTION,
mSelectionArgs,
null
);
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
// Put the result Cursor in the adapter for the ListView
mCursorAdapter.swapCursor(cursor);
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
// Delete the reference to the existing Cursor
mCursorAdapter.swapCursor(null);
}
注: 在SQL中,"%"是通配符,只能與LIKE運(yùn)算符一起使用,表示替代一個或多個字符.
在CursorLoader的構(gòu)造參數(shù)中,URI還可以調(diào)用Uri.withAppendedPath()拼接Contacts.CONTENT_FILTER_URI和關(guān)鍵字來實現(xiàn)名字匹配,如下:
@Override
public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
/*
* Appends the search string to the base URI. Always
* encode search strings to ensure they're in proper
* format.
*/
Uri contentUri = Uri.withAppendedPath(
Contacts.CONTENT_FILTER_URI,
Uri.encode(mSearchString));
// Starts the query
return new CursorLoader(
getActivity(),
contentUri,
PROJECTION,
null,
null,
null
);
}
2. 獲取聯(lián)系人詳情
在獲取了聯(lián)系人列表之后,你可能還需要查看詳情信息,比如手機(jī)號,地址等等.
2.1 獲取聯(lián)系人的所有詳情信息
聯(lián)系人詳情信息存放在ContactsContract.Data這個表中,可以用聯(lián)系人的LOOKUP_KEY來獲取詳情.因為LOOKUP_KEY是ContactsContract.Contacts表中的一列,而ContactsContract.Contacts又與ContactsContract.Data是關(guān)聯(lián)關(guān)系.
注意: 獲取聯(lián)系人的所有詳情信息會影響設(shè)備的性能,因為這需要去ContactsContract.Data獲取所有列,你在是否獲取聯(lián)系人全部詳情的時候要注意考慮這個性能問題.
2.1.1 請求權(quán)限
<uses-permission android:name="android.permission.READ_CONTACTS" />
2.1.2 設(shè)置一個projection
projection的設(shè)置視你要獲取的信息而定,這里我們要獲取所有的列,所以把所有的列都列出來.如果你要將Cursor綁定到ListView中則你要記得獲取Data._ID這個列,同時也要獲取Data.MIMETYPE這列以便識別改行數(shù)據(jù)類型.如下示例:
private static final String PROJECTION =
{
Data._ID,
Data.MIMETYPE,
Data.DATA1,
Data.DATA2,
Data.DATA3,
Data.DATA4,
Data.DATA5,
Data.DATA6,
Data.DATA7,
Data.DATA8,
Data.DATA9,
Data.DATA10,
Data.DATA11,
Data.DATA12,
Data.DATA13,
Data.DATA14,
Data.DATA15
};
2.1.3 設(shè)置檢索條件
// Defines the selection clause
private static final String SELECTION = ContactsContract.Data.LOOKUP_KEY + " = ?";
// Defines the array to hold the search criteria
private String[] mSelectionArgs = { "" };
/*
* Defines a variable to contain the selection value. Once you
* have the Cursor from the Contacts table, and you've selected
* the desired row, move the row's LOOKUP_KEY value into this
* variable.
*/
private String mLookupKey;
2.1.4 設(shè)置排序順序
/*
* Defines a string that specifies a sort order of MIME type
*/
private static final String SORT_ORDER = Data.MIMETYPE;
2.1.5 Loader相關(guān)方法設(shè)置
@Override
protected void onCreate(Bundle savedInstanceState) {
...
// 從前面的列表中拿到lookup key
mLookupKey = getIntent().getStringExtra("lookup_key");
// Initializes the loader framework
getSupportLoaderManager().initLoader(0, null, this);
}
...
@Override
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
// Assigns the selection parameter
mSelectionArgs[0] = mLookupKey;
// Starts the query
CursorLoader mLoader =
new CursorLoader(
this,
ContactsContract.Data.CONTENT_URI,
PROJECTION,
SELECTION,
mSelectionArgs,
SORT_ORDER
);
return mLoader;
}
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
/*
* Process the resulting Cursor here.
*/
}
@Override
public void onLoaderReset(Loader<Cursor> loader) {
/*
* If you have current references to the Cursor,
* remove them here.
*/
}
上述設(shè)置之后即可拿到聯(lián)系人詳細(xì)信息
2.2 獲取聯(lián)系人詳情的某些信息
要獲取聯(lián)系人的某些信息,只需要按需調(diào)整一下參數(shù)即可.例如要獲取郵箱信息,可以使用CommonDataKinds這個類,這里面定義ContactsContract.Data表中使用的數(shù)據(jù)的類型,比如下面要用到的Email類.如下示例,代碼中你只要做下面一些改變:
- Projection
- Selection
- Sort order
private static final String[] PROJECTION =
{
Email._ID,
Email.ADDRESS,
Email.TYPE,
Email.LABEL
};
/*
* Defines the selection clause. Search for a lookup key
* and the Email MIME type
*/
private static final String SELECTION =
Data.LOOKUP_KEY + " = ?" +
" AND " +
Data.MIMETYPE + " = " +
"'" + Email.CONTENT_ITEM_TYPE + "'";
// Defines the array to hold the search criteria
private String[] mSelectionArgs = { "" };
// Define a sort order
private static final String SORT_ORDER = Email.TYPE + " ASC ";
3. 使用Intent修改聯(lián)系人信息
下面介紹如何使用Intent來插入或修改聯(lián)系人信息,而不是直接操作Contacts Provider,同時這種方式是你要優(yōu)先考慮的,有三點原因:
- 你無需寫聯(lián)系人處理相關(guān)的UI和其他處理代碼,省時省力.
- 避免了修改時違背Contact Provider的規(guī)則而導(dǎo)致的錯誤.
- 減少了權(quán)限的請求.
3.1 使用Intent插入聯(lián)系人信息
很多時候你需要允許用戶在獲取到新數(shù)據(jù)的時候插入新的聯(lián)系人信息.比如,一個酒店評價app就可以允許用戶在瀏覽該酒店信息時將其添加到通訊錄中.
下面將介紹調(diào)用聯(lián)系人app插入新將數(shù)據(jù)到raw contact(ContactsContract.RawContacts)這個表中,具體看Retrieval and modification with intents.
新建Intent時使用的是action是Intents.Insert.ACTION,MIME類型是RawContacts.CONTENT_TYPE,如下示例:
...
// Creates a new Intent to insert a contact
Intent intent = new Intent(Intents.Insert.ACTION);
// Sets the MIME type to match the Contacts Provider
intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
如果你已經(jīng)有了一些信息也可直接放在Intent對象中,而存放的時候要注意key要從Intents.Insert獲取,如下示例:
...
private EditText mEmailAddress;
private EditText mPhoneNumber;
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
...
/* Assumes EditText fields in your UI contain an email address
* and a phone number.
*
*/
mEmailAddress = (EditText) findViewById(R.id.email);
mPhoneNumber = (EditText) findViewById(R.id.phone);
}
public void submit(View v) {
// Creates a new Intent to insert a contact
Intent intent = new Intent(Intents.Insert.ACTION);
// Sets the MIME type to match the Contacts Provider
intent.setType(ContactsContract.RawContacts.CONTENT_TYPE);
/*
* Inserts new data into the Intent. This data is passed to the
* contacts app's Insert screen
*/
// Inserts an email address
intent.putExtra(Intents.Insert.EMAIL, mEmailAddress.getText())
/*
* In this example, sets the email type to be a work email.
* You can set other email types as necessary.
*/
.putExtra(Intents.Insert.EMAIL_TYPE, CommonDataKinds.Email.TYPE_WORK)
// Inserts a phone number
.putExtra(Intents.Insert.PHONE, mPhoneNumber.getText())
/*
* In this example, sets the phone type to be a work phone.
* You can set other phone types as necessary.
*/
.putExtra(Intents.Insert.PHONE_TYPE, CommonDataKinds.Phone.TYPE_WORK);
/* Sends the Intent
*/
startActivity(intent);
}
啟動之后會打開聯(lián)系人的新建聯(lián)系人頁面,就可以進(jìn)行相應(yīng)操作.
3.2 使用Intent編輯聯(lián)系人信息
編輯聯(lián)系人信息的流程和插入類型,但是要添加聯(lián)系人的Contacts.CONTENT_LOOKUP_URI和MIME類型Contacts.CONTENT_ITEM_TYPE到intent中,當(dāng)然你還可以將已有數(shù)據(jù)傳到intent中,然后啟動intent即可.
注意: 有一些列通過是無法通過intent修改的,具體在ContactsContract.Contacts中的"Update"標(biāo)題下可以查看.
如下示例:
// The Cursor that contains the Contact row
public Cursor mCursor;
// The index of the lookup key column in the cursor
public int mLookupKeyIndex;
// The index of the contact's _ID value
public int mIdIndex;
// The lookup key from the Cursor
public String mCurrentLookupKey;
// The _ID value from the Cursor
public long mCurrentId;
// A content URI pointing to the contact
Uri mSelectedContactUri;
...
/*
* Once the user has selected a contact to edit,
* this gets the contact's lookup key and _ID values from the
* cursor and creates the necessary URI.
*/
// Gets the lookup key column index
mLookupKeyIndex = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
// Gets the lookup key value
mCurrentLookupKey = mCursor.getString(mLookupKeyIndex);
// Gets the _ID column index
mIdIndex = mCursor.getColumnIndex(Contacts._ID);
mCurrentId = mCursor.getLong(mIdIndex);
mSelectedContactUri =
Contacts.getLookupUri(mCurrentId, mCurrentLookupKey);
...
// Creates a new Intent to edit a contact
Intent editIntent = new Intent(Intent.ACTION_EDIT);
/*
* Sets the contact URI to edit, and the data type that the
* Intent must match
*/
editIntent.setDataAndType(mSelectedContactUri,Contacts.CONTENT_ITEM_TYPE);
注意:在Android 4.0(API 14)及以上,使用帶edit action的intent啟動聯(lián)系人app之后,當(dāng)用戶點完成之后無法正確返回到自己的app中,解決方案是添加一行代碼,如下:
// Sets the special extended data for navigation
editIntent.putExtra("finishActivityOnSaveCompleted", true);
3.3 使用Intent讓用戶選擇插入還是編輯
除了直接插入和編輯之外,你還可以讓用戶選擇要哪種操作,使用ACTION_INSERT_OR_EDIT為action,如下示例:
// Creates a new Intent to insert or edit a contact
Intent intentInsertEdit = new Intent(Intent.ACTION_INSERT_OR_EDIT);
// Sets the MIME type
intentInsertEdit.setType(Contacts.CONTENT_ITEM_TYPE);
// Add code here to insert extended data, if desired
...
// Sends the Intent with an request ID
startActivity(intentInsertEdit);
4. 聯(lián)系人Badge的展示
本節(jié)將展示如何添加QuickContactBadge到UI并且綁定數(shù)據(jù). QuickContactBadge是一個用于展示縮略圖的控件,雖然你可以給它使用任意的Bitmap,但是通常你還是應(yīng)該給它設(shè)置縮略圖.
QuickContactBadge表現(xiàn)的像一個控制器,當(dāng)用戶點擊的時候,它會展開成一個dialog,這個dialog包含以下信息:
- 一個大圖. 這個大圖與聯(lián)系人綁定在一起,如果該聯(lián)系人無圖片,則顯示默認(rèn)圖片.
- App icons. 每一條數(shù)據(jù)的app icon都可以被內(nèi)置的app所處理. 比如, 如果聯(lián)系人詳情中有一個或多個email地址,則email icon就會出現(xiàn),當(dāng)用戶點擊這個icon的時候,該條聯(lián)系人的所有的email地址都會出現(xiàn),當(dāng)用戶點擊email地址時,就會跳轉(zhuǎn)到email app來處理.
QuickContactBadge提供了一個快速訪問聯(lián)系人詳情的方法,用戶因此就可以省去查找,復(fù)制和粘貼聯(lián)系人信息到其他地方,取而代之的時選擇合適的方法來直接發(fā)送聯(lián)系人信息.
4.1 添加一個QuickContactBadge View
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<QuickContactBadge
android:id=@+id/quickbadge
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="centerCrop"/>
...
</RelativeLayout>
4.2 獲取provider數(shù)據(jù)
為了在QuickContactBadge中展示聯(lián)系人信息,你需要給它提供一個content URI和一個Bitmap,而這兩個可以從Contacts Provider中獲取,要獲取這些數(shù)據(jù)你的projection需要這樣設(shè)置:
a. Android 3.0(API 11)及以后:
b. Android 2.3.3(API 10)及以前:
4.3 設(shè)置Contact URI和縮略圖
4.3.1 設(shè)置Contact URI
首先通過getLookupUri(id,lookupKey)方法獲取到一個CONTENT_LOOKUP_URI,然后調(diào)用assignContactUri()設(shè)置contact uri,如下示例:
// The Cursor that contains contact rows
Cursor mCursor;
// The index of the _ID column in the Cursor
int mIdColumn;
// The index of the LOOKUP_KEY column in the Cursor
int mLookupKeyColumn;
// A content URI for the desired contact
Uri mContactUri;
// A handle to the QuickContactBadge view
QuickContactBadge mBadge;
...
mBadge = (QuickContactBadge) findViewById(R.id.quickbadge);
/*
* Insert code here to move to the desired cursor row
*/
// Gets the _ID column index
mIdColumn = mCursor.getColumnIndex(Contacts._ID);
// Gets the LOOKUP_KEY index
mLookupKeyColumn = mCursor.getColumnIndex(Contacts.LOOKUP_KEY);
// Gets a content URI for the contact
mContactUri =
Contacts.getLookupUri(
mCursor.getLong(mIdColumn),
mCursor.getString(mLookupKeyColumn)
);
mBadge.assignContactUri(mContactUri);
4.3.2 設(shè)置縮略圖
給QuickContactBadge設(shè)置了contact URI之后不會自動加載聯(lián)系人頭像縮略圖,要手動加載.先從聯(lián)系人的Cursor中拿到頭像的URI,然后使用它來打開這個文件,接著將該文件讀入Bitmap中.
注意: PHOTO_THUMBNAIL_URI這一列在Android 3.0之前沒有,所以你必須要從Contacts.Photo這個子表中獲取.
首先,設(shè)置變量:
// The column in which to find the thumbnail ID
int mThumbnailColumn;
/*
* The thumbnail URI, expressed as a String.
* Contacts Provider stores URIs as String values.
*/
String mThumbnailUri;
...
/*
* Gets the photo thumbnail column index if
* platform version >= Honeycomb
*/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
mThumbnailColumn =
mCursor.getColumnIndex(Contacts.PHOTO_THUMBNAIL_URI);
// Otherwise, sets the thumbnail column to the _ID column
} else {
mThumbnailColumn = mIdColumn;
}
/*
* Assuming the current Cursor position is the contact you want,
* gets the thumbnail ID
*/
mThumbnailUri = mCursor.getString(mThumbnailColumn);
...
然后,定義一個方法來獲取聯(lián)系人頭像相關(guān)信息,并返回一個Bitmap:
/**
* Load a contact photo thumbnail and return it as a Bitmap,
* resizing the image to the provided image dimensions as needed.
* @param photoData photo ID Prior to Honeycomb, the contact's _ID value.
* For Honeycomb and later, the value of PHOTO_THUMBNAIL_URI.
* @return A thumbnail Bitmap, sized to the provided width and height.
* Returns null if the thumbnail is not found.
*/
private Bitmap loadContactPhotoThumbnail(String photoData) {
// Creates an asset file descriptor for the thumbnail file.
AssetFileDescriptor afd = null;
// try-catch block for file not found
try {
// Creates a holder for the URI.
Uri thumbUri;
// If Android 3.0 or later
if (Build.VERSION.SDK_INT
>=
Build.VERSION_CODES.HONEYCOMB) {
// Sets the URI from the incoming PHOTO_THUMBNAIL_URI
thumbUri = Uri.parse(photoData);
} else {
// Prior to Android 3.0, constructs a photo Uri using _ID
/*
* Creates a contact URI from the Contacts content URI
* incoming photoData (_ID)
*/
final Uri contactUri = Uri.withAppendedPath(
Contacts.CONTENT_URI, photoData);
/*
* Creates a photo URI by appending the content URI of
* Contacts.Photo.
*/
thumbUri =
Uri.withAppendedPath(
contactUri, Photo.CONTENT_DIRECTORY);
}
/*
* Retrieves an AssetFileDescriptor object for the thumbnail
* URI
* using ContentResolver.openAssetFileDescriptor
*/
afd = getActivity().getContentResolver().
openAssetFileDescriptor(thumbUri, "r");
/*
* Gets a file descriptor from the asset file descriptor.
* This object can be used across processes.
*/
FileDescriptor fileDescriptor = afd.getFileDescriptor();
// Decode the photo file and return the result as a Bitmap
// If the file descriptor is valid
if (fileDescriptor != null) {
// Decodes the bitmap
return BitmapFactory.decodeFileDescriptor(
fileDescriptor, null, null);
}
// If the file isn't found
} catch (FileNotFoundException e) {
/*
* Handle file not found errors
*/
// In all cases, close the asset file descriptor
} finally {
if (afd != null) {
try {
afd.close();
} catch (IOException e) {}
}
}
return null;
}
最后,調(diào)用上述方法來獲取一個Bitmap并設(shè)置到QuickContactBadge中:
...
/*
* Decodes the thumbnail file to a Bitmap.
*/
Bitmap mThumbnail =
loadContactPhotoThumbnail(mThumbnailUri);
/*
* Sets the image in the QuickContactBadge
* QuickContactBadge inherits from ImageView, so
*/
mBadge.setImageBitmap(mThumbnail);
Add a QuickContactBad
4.4 添加QuickContactBadge到ListView
4.4.1 設(shè)置ListView的Item View
contact_item_layout.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<QuickContactBadge
android:id="@+id/quickcontact"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:scaleType="centerCrop"/>
<TextView android:id="@+id/displayname"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@+id/quickcontact"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"/>
</RelativeLayout>
4.4.2 自定義CursorAdapter
CursorAdapter顧名思義,適用于處理Cursor的Adapter,它是一個抽象類,里面有兩個抽象方法需要子類來實現(xiàn):
如下示例:
private class ContactsAdapter extends CursorAdapter {
private LayoutInflater mInflater;
...
public ContactsAdapter(Context context) {
super(context, null, 0);
/*
* Gets an inflater that can instantiate
* the ListView layout from the file.
*/
mInflater = LayoutInflater.from(context);
...
}
...
/**
* Defines a class that hold resource IDs of each item layout
* row to prevent having to look them up each time data is
* bound to a row.
*/
private class ViewHolder {
TextView displayname;
QuickContactBadge quickcontact;
}
..
@Override
public View newView(
Context context,
Cursor cursor,
ViewGroup viewGroup) {
/* Inflates the item layout. Stores resource IDs in a
* in a ViewHolder class to prevent having to look
* them up each time bindView() is called.
*/
final View view =
mInflater.inflate(
R.layout.contact_list_layout,
viewGroup,
false
);
final ViewHolder holder = new ViewHolder();
holder.displayname =
(TextView) view.findViewById(R.id.displayname);
holder.quickcontact =
(QuickContactBadge)
view.findViewById(R.id.quickcontact);
view.setTag(holder);
return view;
}
...
@Override
public void bindView(
View view,
Context context,
Cursor cursor) {
final ViewHolder holder = (ViewHolder) view.getTag();
final String photoData =
cursor.getString(mPhotoDataIndex);
final String displayName =
cursor.getString(mDisplayNameIndex);
...
// Sets the display name in the layout
holder.displayname.setText(displayName);
...
/*
* Generates a contact URI for the QuickContactBadge.
*/
final Uri contactUri = Contacts.getLookupUri(
cursor.getLong(mIdIndex),
cursor.getString(mLookupKeyIndex));
holder.quickcontact.assignContactUri(contactUri);
/*
* Decodes the thumbnail file to a Bitmap.
* The method loadContactPhotoThumbnail() is defined
* in the section "Set the Contact URI and Thumbnail"
*/
Bitmap thumbnailBitmap =
loadContactPhotoThumbnail(photoData);
/*
* Sets the image in the QuickContactBadge
* QuickContactBadge inherits from ImageView
*/
holder.quickcontact.setImageBitmap(thumbnailBitmap);
}
4.4.3 設(shè)置變量
// Defines a ListView
private ListView mListView;
// Defines a ContactsAdapter
private ContactsAdapter mAdapter;
// Defines a Cursor to contain the retrieved data
private Cursor mCursor;
/*
* Defines a projection based on platform version. This ensures
* that you retrieve the correct columns.
*/
private static final String[] PROJECTION =
{
Contacts._ID,
Contacts.LOOKUP_KEY,
(Build.VERSION.SDK_INT >=
Build.VERSION_CODES.HONEYCOMB) ?
Contacts.DISPLAY_NAME_PRIMARY :
Contacts.DISPLAY_NAME,
(Build.VERSION.SDK_INT >=
Build.VERSION_CODES.HONEYCOMB) ?
Contacts.PHOTO_THUMBNAIL_URI :
/*
* Although it's not necessary to include the
* column twice, this keeps the number of
* columns the same regardless of version
*/
Contacts._ID
};
/*
* As a shortcut, defines constants for the
* column indexes in the Cursor. The index is
* 0-based and always matches the column order
* in the projection.
*/
// Column index of the _ID column
private int mIdIndex = 0;
// Column index of the LOOKUP_KEY column
private int mLookupKeyIndex = 1;
// Column index of the display name column
private int mDisplayNameIndex = 3;
/*
* Column index of the photo data column.
* It's PHOTO_THUMBNAIL_URI for Honeycomb and later,
* and _ID for previous versions.
*/
private int mPhotoDataIndex =
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
3 :
0;
4.4.4 設(shè)置ListView
@Override
protected void onCreate(Bundle savedInstanceState) {
...
/*
* Instantiates the subclass of
* CursorAdapter
*/
mAdapter = new ContactsAdapter(this);
/*
* Gets a handle to the ListView in the file
* contact_list_layout.xml
*/
mListView = (ListView) findViewById(R.id.list);
// Sets up the adapter for the ListView
mListView.setAdapter(mAdapter);
...
}
還有一個關(guān)鍵代碼:
@Override
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
mAdapter.swapCursor(data);
}
OK,跑起來就可以看到效果了.
總結(jié)
在本例中,官方的training中有很多地方的代碼是有問題的,并且官方提供的demo和training中代碼也不是很一致,最好的方式還是跟著training的思路,然后自己寫demo,官方的demo拿來做參考.
同時在訪問Contacts數(shù)據(jù)的過程中,有一些要注意的地方:
- 使用Contacts Provider時需要設(shè)置權(quán)限.
- 不同Android版本的數(shù)據(jù)庫的字段可能不一致.
- 類的導(dǎo)入要注意,v4就統(tǒng)一v4.
- 盡量使用Intent來操作.