編輯:關於Android編程
有選擇性的翻譯自:https://developer.android.com/training/contacts-provider/index.html
Contacts Provider是用戶通信信息倉庫,包含通訊錄應用程序和社交網絡應用程序的數據。我們可以通過直接調用ContactsResolver的方法或直接發送調用通訊錄應用程序的intent來獲取Contacts Provider提供的信息。
根據以下三種類型匹配獲取列表:
- 匹配通信人姓名
- 匹配某類型數據,如電話號碼
- 匹配任意數據
在使用之前需要申請如下權限:
實現方案是將字符串與通訊錄提供者(Contact Provider)的ContactsContract.Contacts表的一個或多個通信人的姓名進行匹配。
這裡以ListView來展示結果為例介紹整個過程。
創建整體布局文件: res/layout/contacts_list_view.xml:
該文件使用內置的Android ListView組件android:id/list.
然後定義每一項的布局文件 contacts_list_item.xml :
該文件使用內置的Android TextView組件:android:text1.
接下來定義使用上述UI展示通信人列表的代碼。
為了更好的幫助開發者查詢Contacts Provider,Android框架提供了名為ContactsContract的協議類,它定義了用來存取provider的常量和方法。使用該類,你無需定義URI,表名或列名等常量。
我們使用CursorLoader 從provider獲取數據, 因此必須實現接口:LoaderManager.LoaderCallbacks. 另外,實現AdapterView.OnItemClickListener 以獲取用戶在搜索列表中選擇的聯系人信息。相應代碼如下:
...
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.widget.AdapterView;
...
public class ContactsFragment extends Fragment implements
LoaderManager.LoaderCallbacks,
AdapterView.OnItemClickListener {
...
/*
* 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;
...
添加空的結構體,然後在onCreateView()中加載Fragment對象的UI。如下:
// 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
return inflater.inflate(R.layout.contact_list_fragment,
container, false);
}
使用SimpleCursorAdapter 將搜索結果與ListView關聯。如下:
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
...
// Gets the ListView from the View list of the parent activity
mContactsList =
(ListView) getActivity().findViewById(R.layout.contact_list_view);
// Gets a CursorAdapter
mCursorAdapter = new SimpleCursorAdapter(
getActivity(),
R.layout.contact_list_item,
null,
FROM_COLUMNS, TO_IDS,
0);
// Sets the adapter for the ListView
mContactsList.setAdapter(mCursorAdapter);
}
當展示了搜索列表後,我們會允許用戶點擊某一聯系人以做進一步的操作。如,當用戶點擊聯系人後,在地圖上顯示聯系人的地址。為做到這一點,首先定義一個實現了AdapterView.OnItemClickListener 接口的Fragment,就像在定義Fragment展示通信人列表*這一節中所講的。
然後在onActivityCreated()中調用setOnItemClickListener()將監聽器與ListView做關聯。如:
public void onActivityCreated(Bundle savedInstanceState) {
...
// Set the item click listener to be the current fragment.
mContactsList.setOnItemClickListener(this);
...
}
定義一個常量,包含所有你打算返回的列名。如:
...
@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
};
其中,列名Contacts._ID會在SimpleCursorAdapter 綁定過程中用到。Contacts._ID和 LOOKUP_KEY 一起用來構造用戶選取的聯系人的內容URI。 而ListView在展示聯系人列表是會用到聯系人姓名。該列在Android 3.0(API 11)及以後,列名叫Contacts.DISPLAY_NAME_PRIMARY,之前叫Contacts.DISPLAY_NAME。
獲取某cursor某列的數據時,需要該列在Cursor中的列索引值。因為該索引值使用的是上一節定義的規劃中的列的順序,因此可以把列的索引值定義為常量。
// 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;
使用文字表達式指出要搜索的數據列,盡管該表達式可以包含參數值,推薦使用占位符“?”來代替參數值。使用“?”可以保證搜索通過連接而不是SQL編譯來生成,避免惡意SQL注入的可能性。
使用變量來表示待搜索的內容,即SQL中替換“?”的內容。代碼如下:
// 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
private String mSearchString;
// Defines the array to hold values that replace the ?
private String[] mSelectionArgs = { mSearchString };
實現上一節的監聽器,如下:
@Override
public void onItemClick(
AdapterView parent, View item, int position, long rowID) {
// Get the Cursor
Cursor cursor = parent.getAdapter().getCursor();
// Move to the selected contact
cursor.moveToPosition(position);
// Get the _ID value
mContactId = getLong(CONTACT_ID_INDEX);
// Get the selected LOOKUP KEY
mContactKey = getString(CONTACT_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.
*/
}
既然使用CursorLoader 來獲取數據,必須初始化控制異步獲取數據的後台線程和其他變量。在onActivityCreated()方法中做初始化,該函數會在Fragment的UI出現之前被調用。如下:
public class ContactsFragment extends Fragment implements
LoaderManager.LoaderCallbacks {
...
// Called just before the Fragment displays its UI
@Override
public void onActivityCreated(Bundle savedInstanceState) {
// Always call the super method first
super.onActivityCreated(savedInstanceState);
...
// Initializes the loader
getLoaderManager().initLoader(0, null, this);
該方法會在我們調用initLoader()後立即被系統調用。
在onCreateLoader()中,設置搜索串模式。字符串中插入:“%”表示0或更多個字符串,“_”代表一個字符。如,模式”%Jefferson%”會匹配“Thomas Jefferson”和”Jefferson Davis”. 只有內容URI,使用Contacts.CONTENT_URI, 它指向整個表。代碼如下:
...
@Override
public Loader onCreateLoader(int loaderId, 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
);
}
Loader框架在Contacts Provider返回搜索結果時調用onLoadFinished().在該方法中,將結果Cursor放入SimpleCursorAdapter中,這樣會自動使用搜索結果更新ListView:
public void onLoadFinished(Loader loader, Cursor cursor) {
// Put the result Cursor in the adapter for the ListView
mCursorAdapter.swapCursor(cursor);
}
onLoaderReset()方法會在loader框架發現Cursor結果包含舊數據時被調用。在該方法中,需要清除SimpleCursorAdapter對已有Cursor的引用,負責loader框架不會回收該Cursor,會引起內地洩漏。
@Override
public void onLoaderReset(Loader loader) {
// Delete the reference to the existing Cursor
mCursorAdapter.swapCursor(null);
}
至此,已經講完了搜索通信人姓名並將搜索結果在ListView中展現的所有關鍵點。
實現這一類型的數據獲取,首先需要像上一節一樣實現以下代碼:
申請度通訊錄的權限 定義ListView的布局 定義Fragment展示通信人列表 定義全局變量 初始化Fragment 給ListView設置CursorAdapter 設置點擊通訊錄列表的監聽器 定義Cursor列的索引值常量 定義onItemClick方法 初始化CursorLoader 實現onLoadFinished和onLoadReset除上述步驟外,需要額外的代碼來獲取匹配某些類型數據的通訊錄信息。接下來是與上一節實現方面有不同的步驟。
搜索某類型的具體數據,需要知道該類型對應的MIME類型。所有的數據類型都有一個唯一的MIME類型值,該值是ContactsContract.CommonDataKinds 中與數據類型匹配的子類中定義的常量CONTENT_ITEM_TYPE。如,Email數據對應的子類是ContactsContract.CommonDataKinds.Email, 而它的MIME類型是Email.CONTENT_ITEM_TYPE常量.
選擇完數據類型後,選擇該使用哪個表。使用ContactsContract.Data表,該表中定義或繼承了所有需要查詢的列名或如何排序等常量。
可以選擇ContactsContract.Data或它繼承的類中的一個或多個列。Contacts Provider在返回結果前會對ContactsContract.Data表和其他表做一個隱式的連接。如下:
@SuppressLint("InlinedApi")
private static final String[] PROJECTION =
{
/*
* The detail data row ID. To make a ListView work,
* this column is required.
*/
Data._ID,
// The primary display name
Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ?
Data.DISPLAY_NAME_PRIMARY :
Data.DISPLAY_NAME,
// The contact's _ID, to construct a content URI
Data.CONTACT_ID,
// The contact's LOOKUP_KEY, to construct a content URI
Data.LOOKUP_KEY
};
使用如下數據構造一個搜索語句:
- 包含待搜索字符串信息的列的名字 根據搜索類型的不同而變化,因此需要先找出與搜索類型對應的ContactsContract.CommonDataKinds 的子類,然後選擇該子類的列名。如,使用列名Email.ADDRESS來搜索email地址。
- 搜索字符串 在搜索語句中使用“?”來代替。
- 包含MIME類型的列名 該值是固定值:Data.MIMETYPE.
- 搜索類型對應的MIME值 ContactsContract.CommonDataKinds 中與數據類型匹配的子類中定義的常量CONTENT_ITEM_TYPE。如,Email數據對應的子類是ContactsContract.CommonDataKinds.Email, 而它的MIME類型是Email.CONTENT_ITEM_TYPE常量. 需要使用單引號將該常量括起來,否則provider會把它當作變量名而不是常量。
代碼如下:
/*
* Constructs search criteria from the search string
* and email MIME type
*/
private static final String SELECTION =
/*
* Searches for an email address
* that matches the search string
*/
Email.ADDRESS + " LIKE ? " + "AND " +
/*
* Searches for a MIME type that matches
* the value of the constant
* Email.CONTENT_ITEM_TYPE. Note the
* single quotes surrounding Email.CONTENT_ITEM_TYPE.
*/
Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
接下來定義包含搜索參數的變量:
String mSearchString;
String[] mSelectionArgs = { "" };
與上一節不同的是,這裡的內容URI,使用Data.CONTENT_URI.如下:
@Override
public Loader onCreateLoader(int loaderId, Bundle args) {
// OPTIONAL: Makes search string into pattern
mSearchString = "%" + mSearchString + "%";
// Puts the search string into the selection criteria
mSelectionArgs[0] = mSearchString;
// Starts the query
return new CursorLoader(
getActivity(),
Data.CONTENT_URI,
PROJECTION,
SELECTION,
mSelectionArgs,
null
);
}
至此,匹配某類型數據的搜索實現已結束。
返回匹配某數據的聯系人列表,而不管該數據是名字,Email地址,通信地址或電話號碼等。
實現這一類型的數據獲取,首先需要像上一節一樣實現以下代碼:
接下來是與上一節實現方面有不同的步驟。
不用定義SELECTION常量或mSelectionArgs變量。
不再需要將字符串轉換為模式,因為Contacts Provider會自動為你做。使用Contacts.CONTENT_FILTER_URI作為基礎URI, 通過調用Uri.withAppendedPath()將待搜索字符串追加到上述URI. 使用處理後的URI來搜索任意類型的數據。代碼如下:
@Override
public Loader 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
);
}
至此,匹配任意類型數據的搜索實現已結束。
一說到支付寶,相信沒有人不知道,生活中付款,轉賬都會用到。今天來詳細介紹下在android中如何集成支付寶支付到自己的APP中去。讓APP能夠擁有方便,快捷的支付功能。准
生成apk最懶惰的方法是:只要你運行過android項目,到工作目錄的bin文件夾下就能找到與項目同名的apk文件,這種apk默認是已經使用debug用戶簽名的。如果想要
在我們的手持設備中,一般都會自帶設備公司自己開發的文件管理系統、拍照系統之類的東東,今天我給大伙說說入門級開發的文件夾管理器,代碼贼少 總共就6個類吧,沒有
本文實例分析了Android模擬器接收UDP數據包的若干問題。分享給大家供大家參考,具體如下:android模擬器無法接收UDP數據包代碼如下:DatagramPacke
在研究源碼之前,我們對Handler的了解一般是這樣的概念:在主線程中,