במדריך הזה תלמדו איך לאחזר רשימה של אנשי קשר שהנתונים שלהם תואמים לכל מחרוזת החיפוש או לחלק ממנה, באמצעות השיטות הבאות:
- התאמה של שמות של אנשי קשר
- אחזור רשימת אנשי קשר על ידי התאמת מחרוזת החיפוש לכל הנתונים של שם איש הקשר או לחלק מהם. ספק אנשי הקשר מאפשר כמה מופעים של אותו שם, כך ששיטה זו יכולה להחזיר רשימה של התאמות.
- התאמה לסוג ספציפי של נתונים, כמו מספר טלפון
- אחזור רשימת אנשי קשר על ידי התאמת מחרוזת החיפוש לסוג מסוים של נתוני פרטים, כמו כתובת אימייל. לדוגמה, הטכניקה הזו מאפשרת לכם לקבל רשימה של כל אנשי הקשר שכתובת האימייל שלהם תואמת למחרוזת החיפוש.
- התאמה לכל סוג של נתונים
- מאחזרים רשימה של אנשי קשר על ידי התאמת מחרוזת החיפוש לנתוני פרטים מכל סוג, כולל שם, מספר טלפון, רחוב, כתובת אימייל וכן הלאה. לדוגמה, הטכניקה הזו מאפשרת לקבל כל סוג של נתונים עבור מחרוזת חיפוש, ולאחר מכן להציג את אנשי הקשר שהנתונים שלהם תואמים למחרוזת.
הערה: בכל הדוגמאות בשיעור הזה נעשה שימוש ב-CursorLoader
כדי לאחזר נתונים מהספק של אנשי הקשר. CursorLoader
מריץ את השאילתה שלו על שרשור שנפרד מהשרשור בממשק המשתמש. כך אפשר לוודא שהשאילתה לא תאט את זמני התגובה של ממשק המשתמש ותגרום לחוויית משתמש גרועה. מידע נוסף זמין בכיתה של Android
טעינה של נתונים ברקע.
שליחת בקשה לקבלת הרשאה לקריאת הספק
כדי לבצע כל סוג של חיפוש אצל ספק אנשי הקשר, לאפליקציה צריכה להיות ההרשאה READ_CONTACTS
.
כדי לבקש זאת, מוסיפים את האלמנט <uses-permission>
לקובץ המניפסט כאלמננט צאצא של <manifest>
:
<uses-permission android:name="android.permission.READ_CONTACTS" />
התאמה של איש קשר לפי שם ורישום התוצאות
בשיטה הזו מתבצעת התאמה של מחרוזת חיפוש לשם של איש קשר או של אנשי קשר בטבלה ContactsContract.Contacts
של ספק אנשי הקשר. בדרך כלל כדאי להציג את התוצאות בחלונית ListView
כדי לאפשר למשתמש לבחור מבין אנשי הקשר שתואמים.
הגדרת תצוגת רשימה ופריסות פריטים
כדי להציג את תוצאות החיפוש ב-ListView
, צריך קובץ פריסה ראשי שמגדיר את כל ממשק המשתמש, כולל ה-ListView
, וקובץ פריסה של פריט שמגדיר שורה אחת ב-ListView
. לדוגמה, אפשר ליצור את קובץ הפריסה הראשי res/layout/contacts_list_view.xml
באמצעות ה-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"/>
קובץ ה-XML הזה משתמש בווידג'ט ListView
המובנה של Android
android:id/list
.
מגדירים את קובץ פריסת הפריטים contacts_list_item.xml
באמצעות ה-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"/>
קובץ ה-XML הזה משתמש בווידג'ט TextView
המובנה של Android
android:text1
.
הערה: השיעור הזה לא מתאר את ממשק המשתמש לקבלת מחרוזת חיפוש מהמשתמש, כי אולי תרצו לקבל את המחרוזת באופן עקיף. לדוגמה, תוכלו לתת למשתמש אפשרות לחפש אנשי קשר שהשם שלהם תואם למחרוזת בהודעת טקסט נכנסת.
שני קובצי הפריסה שכתבתם מגדירים ממשק משתמש שמוצג בו ListView
. השלב הבא הוא לכתוב קוד שמשתמש בממשק המשתמש הזה כדי להציג רשימה של אנשי קשר.
הגדרת קטע (Fragment) שבו מוצגת רשימת אנשי הקשר
כדי להציג את רשימת אנשי הקשר, קודם מגדירים Fragment
שמורחב על ידי Activity
. השימוש ב-Fragment
הוא טכניקה גמישה יותר, כי אפשר להשתמש ב-Fragment
אחד כדי להציג את הרשימה וב-Fragment
שני כדי להציג את הפרטים של איש הקשר שהמשתמש בוחר מהרשימה. בגישה הזו תוכלו לשלב את אחת מהשיטות שמוצגות בשיעור הזה עם אחת מהשיעורים
אחזור פרטים של איש קשר.
במאמר
יצירת ממשק משתמש דינמי באמצעות 'קטעים' מוסבר איך משתמשים באובייקט Fragment
אחד או יותר מתוך Activity
.
כדי לעזור לכם לכתוב שאילתות ל-Contacts Provider, מסגרת Android מספקת סוג של חוזים שנקרא ContactsContract
, שמגדיר קבועים ושיטות שימושיים לגישה לספק. כשמשתמשים בכיתה הזו, לא צריך להגדיר קבועים משלכם למזהי URI של תוכן, לשמות של טבלאות או לעמודות. כדי להשתמש בכיתה הזו, צריך לכלול את ההצהרה הבאה:
Kotlin
import android.provider.ContactsContract
Java
import android.provider.ContactsContract;
מכיוון שהקוד משתמש ב-CursorLoader
כדי לאחזר נתונים מהספק, צריך לציין שהוא מטמיע את ממשק הטעינה LoaderManager.LoaderCallbacks
. בנוסף, כדי לזהות את איש הקשר שהמשתמש בוחר מרשימת תוצאות החיפוש, מטמיעים את ממשק המתאם AdapterView.OnItemClickListener
. לדוגמה:
Kotlin
... import android.support.v4.app.Fragment import android.support.v4.app.LoaderManager import android.widget.AdapterView ... class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener {
Java
... 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<Cursor>, AdapterView.OnItemClickListener {
הגדרת משתנים גלובליים
מגדירים משתנים גלובליים שנעשה בהם שימוש בחלקים אחרים בקוד:
Kotlin
... /* * Defines an array that contains column names to move from * the Cursor to the ListView. */ @SuppressLint("InlinedApi") private val FROM_COLUMNS: Array<String> = arrayOf( if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB)) { ContactsContract.Contacts.DISPLAY_NAME_PRIMARY } else { ContactsContract.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 val TO_IDS: IntArray = intArrayOf(android.R.id.text1) ... class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor>, AdapterView.OnItemClickListener { ... // Define global mutable variables // Define a ListView object lateinit var contactsList: ListView // Define variables for the contact the user selects // The contact's _ID value var contactId: Long = 0 // The contact's LOOKUP_KEY var contactKey: String? = null // A content URI for the selected contact var contactUri: Uri? = null // An adapter that binds the result Cursor to the ListView private val cursorAdapter: SimpleCursorAdapter? = null
Java
... /* * 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 ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY : ContactsContract.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 contactsList; // Define variables for the contact the user selects // The contact's _ID value long contactId; // The contact's LOOKUP_KEY String contactKey; // A content URI for the selected contact Uri contactUri; // An adapter that binds the result Cursor to the ListView private SimpleCursorAdapter cursorAdapter; ...
הערה:
ב-Contacts.DISPLAY_NAME_PRIMARY
נדרשת גרסת Android 3.0 (גרסת API 11) ואילך, לכן אם מגדירים
את הערך minSdkVersion
של האפליקציה לערך 10 ומטה, נוצרת אזהרה של Androidlint
ב-Android Studio. כדי להשבית את האזהרה הזו, מוסיפים את ההערה @SuppressLint("InlinedApi")
לפני ההגדרה של FROM_COLUMNS
.
אתחול המקטע
מפעילים את Fragment
. מוסיפים את ה-constructor הציבורי הריק שנדרש למערכת Android, ומנפחים את ממשק המשתמש של האובייקט Fragment
בשיטת ה-callback onCreateView()
.
לדוגמה:
Kotlin
// A UI Fragment must inflate its View override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { // Inflate the fragment layout return inflater.inflate(R.layout.contact_list_fragment, container, false) }
Java
// 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); }
הגדרת CursorAdapter ל-ListView
מגדירים את SimpleCursorAdapter
שמקשר את תוצאות החיפוש ל-ListView
. כדי לקבל את האובייקט ListView
שמוצגים בו אנשי הקשר, צריך להפעיל את Activity.findViewById()
באמצעות הפעילות ההורה של Fragment
. כשקוראים לפונקציה setAdapter()
, צריך להשתמש ב-Context
של פעילות ההורים.
לדוגמה:
Kotlin
override fun onActivityCreated(savedInstanceState: Bundle?) { super.onActivityCreated(savedInstanceState) ... // Gets the ListView from the View list of the parent activity activity?.also { contactsList = it.findViewById<ListView>(R.id.contact_list_view) // Gets a CursorAdapter cursorAdapter = SimpleCursorAdapter( it, R.layout.contact_list_item, null, FROM_COLUMNS, TO_IDS, 0 ) // Sets the adapter for the ListView contactsList.adapter = cursorAdapter } }
Java
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); ... // Gets the ListView from the View list of the parent activity contactsList = (ListView) getActivity().findViewById(R.layout.contact_list_view); // Gets a CursorAdapter cursorAdapter = new SimpleCursorAdapter( getActivity(), R.layout.contact_list_item, null, FROM_COLUMNS, TO_IDS, 0); // Sets the adapter for the ListView contactsList.setAdapter(cursorAdapter); }
הגדרת האזנה לאנשי הקשר שנבחרה
כשמציגים את תוצאות החיפוש, בדרך כלל רוצים לאפשר למשתמש לבחור איש קשר אחד לעיבוד נוסף. לדוגמה, כשהמשתמש לוחץ על איש קשר, אפשר להציג את הכתובת של איש הקשר במפה. כדי לספק את התכונה הזו, קודם מגדירים את Fragment
הנוכחי בתור מאזין הקליקים, על ידי ציון שהכיתה מיישמת את AdapterView.OnItemClickListener
, כפי שמתואר בקטע הגדרת 'קטע' שמוצגת בו רשימת אנשי הקשר.
כדי להמשיך בהגדרת המאזין, צריך לקשר אותו ל-ListView
על ידי קריאה ל-method setOnItemClickListener()
ב-onActivityCreated()
. לדוגמה:
Kotlin
fun onActivityCreated(savedInstanceState:Bundle) { ... // Set the item click listener to be the current fragment. contactsList.onItemClickListener = this ... }
Java
public void onActivityCreated(Bundle savedInstanceState) { ... // Set the item click listener to be the current fragment. contactsList.setOnItemClickListener(this); ... }
מכיוון שציינת שה-Fragment
הנוכחי הוא
OnItemClickListener
של
ListView
, עכשיו צריך להטמיע את ה-method הנדרש
onItemClick()
, המטפל
באירוע הקליק. הנושא הזה מתואר בקטע הבא.
הגדרת היטל
מגדירים קבוע שמכיל את העמודות שרוצים להחזיר מהשאילתה. בכל פריט ב-ListView
מוצג שם הקשר המוצג, שמכיל את צורת השם הראשית של איש הקשר. בגרסה Android 3.0 (גרסת API 11) ואילך, שם העמודה הזו הוא
Contacts.DISPLAY_NAME_PRIMARY
. בגרסאות קודמות, השם של העמודה הוא Contacts.DISPLAY_NAME
.
העמודה Contacts._ID
משמשת את תהליך הקישור של SimpleCursorAdapter
.
משתמשים ב-Contacts._ID
וב-LOOKUP_KEY
ביחד כדי ליצור URI של תוכן לאיש הקשר שהמשתמש בוחר.
Kotlin
... @SuppressLint("InlinedApi") private val PROJECTION: Array<out String> = arrayOf( ContactsContract.Contacts._ID, ContactsContract.Contacts.LOOKUP_KEY, if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ContactsContract.Contacts.DISPLAY_NAME_PRIMARY else ContactsContract.Contacts.DISPLAY_NAME )
Java
... @SuppressLint("InlinedApi") private static final String[] PROJECTION = { Contacts._ID, Contacts.LOOKUP_KEY, Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? ContactsContract.Contacts.DISPLAY_NAME_PRIMARY : ContactsContract.Contacts.DISPLAY_NAME };
הגדרת קבועים עבור אינדקסים של עמודות הסמן
כדי לקבל נתונים מעמודה מסוימת ב-Cursor
, צריך את אינדקס העמודה ב-Cursor
. אפשר להגדיר קבועים למפתחות של העמודות Cursor
, כי המפתחות זהים לסדר של שמות העמודות בתחזית. לדוגמה:
Kotlin
// The column index for the _ID column private const val CONTACT_ID_INDEX: Int = 0 // The column index for the CONTACT_KEY column private const val CONTACT_KEY_INDEX: Int = 1
Java
// The column index for the _ID column private static final int CONTACT_ID_INDEX = 0; // The column index for the CONTACT_KEY column private static final int CONTACT_KEY_INDEX = 1;
ציון הקריטריונים לבחירה
כדי לציין את הנתונים הרצויים, צריך ליצור שילוב של ביטויי טקסט ומשתנים שמציינים לספק את עמודות הנתונים לחיפוש ואת הערכים שצריך למצוא.
בביטוי הטקסט, מגדירים קבוע שמפרט את עמודות החיפוש. הביטוי הזה יכול להכיל גם ערכים, אבל מומלץ לייצג את הערכים באמצעות placeholder של '?'. במהלך אחזור הנתונים, ה-placeholder מוחלף בערכים ממערך. השימוש ב-'?' כ-placeholder מבטיח שהמפרט של החיפוש נוצר על ידי קישור ולא על ידי הידור של SQL. השיטה הזו מבטלת את האפשרות להחדרת SQL זדונית. לדוגמה:
Kotlin
// Defines the text expression @SuppressLint("InlinedApi") private val SELECTION: String = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) "${ContactsContract.Contacts.DISPLAY_NAME_PRIMARY} LIKE ?" else "${ContactsContract.Contacts.DISPLAY_NAME} LIKE ?" ... // Defines a variable for the search string private val searchString: String = ... // Defines the array to hold values that replace the ? private val selectionArgs = arrayOf<String>(searchString)
Java
// 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 searchString; // Defines the array to hold values that replace the ? private String[] selectionArgs = { searchString };
הגדרת השיטה onItemClick()
בקטע הקודם הגדרתם את האירוע של לחיצה על פריט עבור ListView
.
עכשיו מטמיעים את הפעולה למאזין על ידי הגדרת השיטה
AdapterView.OnItemClickListener.onItemClick()
:
Kotlin
override fun onItemClick(parent: AdapterView<*>, view: View?, position: Int, id: Long) { // Get the Cursor val cursor: Cursor? = (parent.adapter as? CursorAdapter)?.cursor?.apply { // Move to the selected contact moveToPosition(position) // Get the _ID value contactId = getLong(CONTACT_ID_INDEX) // Get the selected LOOKUP KEY contactKey = getString(CONTACT_KEY_INDEX) // Create the contact's content Uri contactUri = ContactsContract.Contacts.getLookupUri(contactId, mContactKey) /* * You can use contactUri as the content URI for retrieving * the details for a contact. */ } }
Java
@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 contactId = cursor.getLong(CONTACT_ID_INDEX); // Get the selected LOOKUP KEY contactKey = cursor.getString(CONTACT_KEY_INDEX); // Create the contact's content Uri contactUri = Contacts.getLookupUri(contactId, mContactKey); /* * You can use contactUri as the content URI for retrieving * the details for a contact. */ }
אתחול של מערך הטעינה
בגלל ש-CursorLoader
מאחזר נתונים, צריך לאתחל את ה-thread ברקע ומשתנים אחרים ששולטים באחזור אסינכרוני. מבצעים את האינטוליזציה ב-onCreate()
, כפי שמתואר בדוגמה הבאה:
Kotlin
class ContactsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { ... override fun onCreate(savedInstanceState: Bundle?) { // Always call the super method first super.onCreate(savedInstanceState) ... // Initializes the loader loaderManager.initLoader(0, null, this)
Java
public class ContactsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { ... // Called just before the Fragment displays its UI @Override public void onCreate(Bundle savedInstanceState) { // Always call the super method first super.onCreate(savedInstanceState); ... // Initializes the loader getLoaderManager().initLoader(0, null, this);
הטמעה ב-onCreateLoader()
מטמיעים את השיטה onCreateLoader()
, שנקראת על ידי מסגרת הטעינה מיד אחרי שמפעילים את initLoader()
.
ב-onCreateLoader()
, מגדירים את הדפוס של מחרוזת החיפוש. כדי להפוך מחרוזת לדפוס, מוסיפים תווים של '%' (אחוז) כדי לייצג רצף של אפס תווים או יותר, או תווים של '_' (קו תחתון) כדי לייצג תו יחיד, או את שניהם. לדוגמה, הדפוס '%Jefferson%'
יתאים גם ל'תומס ג'פרסון' וגם ל'ג'פרסון דייוויס'.
מחזירים CursorLoader
חדש מהשיטה. ב-URI של התוכן, משתמשים ב-Contacts.CONTENT_URI
.
ה-URI הזה מתייחס לכל הטבלה, כפי שמוצג בדוגמה הבאה:
Kotlin
... override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> { /* * Makes search string into pattern and * stores it in the selection array */ selectionArgs[0] = "%$mSearchString%" // Starts the query return activity?.let { return CursorLoader( it, ContactsContract.Contacts.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, null ) } ?: throw IllegalStateException() }
Java
... @Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { /* * Makes search string into pattern and * stores it in the selection array */ selectionArgs[0] = "%" + searchString + "%"; // Starts the query return new CursorLoader( getActivity(), ContactsContract.Contacts.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, null ); }
הטמעה של onLoadFinished() ו-onLoaderReset()
מטמיעים את השיטה onLoadFinished()
. מסגרת הטעינה קוראת ל-onLoadFinished()
כשספק אנשי הקשר מחזיר את תוצאות השאילתה. בשיטה הזו, צריך להוסיף את התוצאה Cursor
ל-SimpleCursorAdapter
. הפעולה הזו תעדכן את ListView
באופן אוטומטי בתוצאות החיפוש:
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) { // Put the result Cursor in the adapter for the ListView cursorAdapter?.swapCursor(cursor) }
Java
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { // Put the result Cursor in the adapter for the ListView cursorAdapter.swapCursor(cursor); }
השיטה onLoaderReset()
מופעלת כשמסגרת הטעינה מזהה שהתוצאה Cursor
מכילה נתונים לא עדכניים. מוחקים את
ההפניה SimpleCursorAdapter
לקובץ
Cursor
הקיים. אם לא תעשו זאת, מסגרת הטעינה לא תוכל למחזר את Cursor
, וכתוצאה מכך תהיה דליפת זיכרון. לדוגמה:
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { // Delete the reference to the existing Cursor cursorAdapter?.swapCursor(null) }
Java
@Override public void onLoaderReset(Loader<Cursor> loader) { // Delete the reference to the existing Cursor cursorAdapter.swapCursor(null); }
עכשיו יש לכם את החלקים העיקריים של אפליקציה שמתאימה מחרוזת חיפוש לשמות של אנשי קשר ומחזירה את התוצאה ב-ListView
. המשתמש יכול ללחוץ על שם של איש קשר כדי לבחור אותו.
הפעולה הזו מפעילה מאזין, שבו אפשר להמשיך לעבוד עם נתוני איש הקשר. לדוגמה, ניתן לאחזר את הפרטים של איש הקשר. כדי ללמוד איך עושים את זה, ממשיכים לשיעור הבא, אחזור פרטים של איש קשר.
מידע נוסף על ממשקי משתמש לחיפוש זמין במדריך API יצירת ממשק חיפוש.
בשאר הקטעים של השיעור הזה נסביר דרכים נוספות לאיתור אנשי קשר ב-Contacts Provider.
התאמת איש קשר לפי סוג ספציפי של נתונים
השיטה הזו מאפשרת לציין את סוג הנתונים שרוצים להתאים. אחזור לפי שם הוא דוגמה ספציפית לשאילתה מסוג כזה, אבל אפשר לעשות זאת גם לגבי כל אחד מסוגי פרטי הפרטים שמשויכים לאיש קשר. לדוגמה, אפשר לאחזר אנשי קשר שיש להם מיקוד ספציפי. במקרה הזה, מחרוזת החיפוש צריכה להתאים לנתונים שמאוחסנים בשורת המיקוד.
כדי להטמיע את סוג האחזור הזה, קודם כול מטמיעים את הקוד הבא, כפי שמפורט בקטעים הקודמים:
- מבקשים הרשאה לקרוא את הספק.
- הגדרת פריטים ופריסות של ListView.
- הגדרת מקטע (Fragment) שמציג את רשימת אנשי הקשר.
- להגדיר משתנים גלובליים.
- אתחול המקטע (Fragment).
- מגדירים את CursorAdapter ל-ListView.
- הגדרת האזנה לאנשי הקשר שנבחרה.
-
מגדירים ערכי קבועים לאינדקסים של העמודות של הסמן.
אמנם אתם מאחזרים נתונים מטבלה אחרת, אבל הסדר של העמודות בתצוגה הוא זהה, כך שאפשר להשתמש באותם אינדקסים עבור הסמן.
- מגדירים את השיטה onItemClick().
- מאתחלים את הטען.
- מטמיעים את onLoadFinished() ואת onLoaderReset().
בשלבים הבאים יוצג הקוד הנוסף שנדרש כדי להתאים מחרוזת חיפוש לסוג מסוים של נתוני פרטים ולהציג את התוצאות.
בחירת סוג הנתונים והטבלה
כדי לחפש סוג מסוים של נתוני פרטים, צריך לדעת את הערך המותאם אישית של סוג ה-MIME לסוג הנתונים. לכל סוג נתונים יש ערך ייחודי של סוג MIME שמוגדר על ידי הקבוע CONTENT_ITEM_TYPE
בתת-הסוג של ContactsContract.CommonDataKinds
שמשויך לסוג הנתונים.
לשכבות המשנה יש שמות שמציינים את סוג הנתונים שלהן. לדוגמה, שכבת המשנה של נתוני האימייל היא ContactsContract.CommonDataKinds.Email
, וסוג ה-MIME המותאם אישית של נתוני האימייל מוגדר על ידי הקבוע Email.CONTENT_ITEM_TYPE
.
משתמשים בטבלה ContactsContract.Data
לחיפוש. כל הקבועים שדרושים להצגה, לתנאי הבחירה ולסדר המיון מוגדרים בטבלה הזו או עוברים בירושה ממנה.
הגדרת הקרנה
כדי להגדיר הקרנה, בוחרים עמודה אחת או יותר שמוגדרת ב-ContactsContract.Data
או את הכיתות שממנה היא עוברת בירושה. הספק של אנשי הקשר מבצע איחוד משתמע בין ContactsContract.Data
לבין טבלאות אחרות לפני שהוא מחזיר שורות. לדוגמה:
Kotlin
@SuppressLint("InlinedApi") private val PROJECTION: Array<out String> = arrayOf( /* * The detail data row ID. To make a ListView work, * this column is required. */ ContactsContract.Data._ID, // The primary display name if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) ContactsContract.Data.DISPLAY_NAME_PRIMARY else ContactsContract.Data.DISPLAY_NAME, // The contact's _ID, to construct a content URI ContactsContract.Data.CONTACT_ID, // The contact's LOOKUP_KEY, to construct a content URI ContactsContract.Data.LOOKUP_KEY )
Java
@SuppressLint("InlinedApi") private static final String[] PROJECTION = { /* * The detail data row ID. To make a ListView work, * this column is required. */ ContactsContract.Data._ID, // The primary display name Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB ? ContactsContract.Data.DISPLAY_NAME_PRIMARY : ContactsContract.Data.DISPLAY_NAME, // The contact's _ID, to construct a content URI ContactsContract.Data.CONTACT_ID, // The contact's LOOKUP_KEY, to construct a content URI ContactsContract.Data.LOOKUP_KEY // A permanent link to the contact };
הגדרת קריטריונים לחיפוש
כדי לחפש מחרוזת בתוך סוג נתונים מסוים, יוצרים תנאי בחירה מהפרטים הבאים:
-
שם העמודה שמכילה את מחרוזת החיפוש. השם הזה משתנה בהתאם לסוג הנתונים, לכן צריך למצוא את המשנה של
ContactsContract.CommonDataKinds
שתואם לסוג הנתונים ואז לבחור את שם העמודה מהמשנה הזה. לדוגמה, כדי לחפש כתובות אימייל, משתמשים בעמודהEmail.ADDRESS
. - מחרוזת החיפוש עצמה, שמיוצגת בתווית '?' בתנאי הבחירה.
-
השם של העמודה שמכילה את הערך של סוג ה-MIME המותאם אישית. השם הזה תמיד הוא
Data.MIMETYPE
. -
הערך המותאם אישית של סוג ה-MIME לסוג הנתונים. כפי שמתואר למעלה, זוהי הקבועה
CONTENT_ITEM_TYPE
בתת-הסוגContactsContract.CommonDataKinds
. לדוגמה, הערך של סוג ה-MIME לנתוני אימייל הואEmail.CONTENT_ITEM_TYPE
. צריך להקיף את הערך במירכאות בודדות על ידי שרשור התו'
(מירכאות בודדות) לתחילת הקבוע ולסופו. אחרת, הספק ירצה את הערך בתור שם משתנה ולא בתור ערך מחרוזת. אין צורך להשתמש ב-placeholder לערך הזה, כי אתם משתמשים בערך קבוע ולא בערך שמשתמשים מספקים.
לדוגמה:
Kotlin
/* * Constructs search criteria from the search string * and email MIME type */ private val SELECTION: String = /* * 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. */ "${ContactsContract.Data.MIMETYPE } = '${Email.CONTENT_ITEM_TYPE}'"
Java
/* * 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. */ ContactsContract.Data.MIMETYPE + " = '" + Email.CONTENT_ITEM_TYPE + "'";
בשלב הבא, מגדירים משתנים שיכילו את ארגומנט הבחירה:
Kotlin
private var searchString: String? = null private val selectionArgs: Array<String> = arrayOf("")
Java
String searchString; String[] selectionArgs = { "" };
הטמעת onCreateLoader()
עכשיו, אחרי שציינתם את הנתונים שרוצים ואת האופן שבו אפשר למצוא אותם, מגדירים שאילתה בהטמעה של onCreateLoader()
.
הפונקציה מחזירה CursorLoader
חדש מה-method הזה, באמצעות ההיטל, ביטוי הטקסט שנבחר ומערך הבחירה כארגומנטים. כדי להשתמש ב-URI של תוכן, צריך להשתמש ב-Data.CONTENT_URI
. לדוגמה:
Kotlin
override fun onCreateLoader(id: Int, args: Bundle?): Loader<Cursor> { // OPTIONAL: Makes search string into pattern searchString = "%$mSearchString%" searchString?.also { // Puts the search string into the selection criteria selectionArgs[0] = it } // Starts the query return activity?.let { CursorLoader( it, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, null ) } ?: throw IllegalStateException() }
Java
@Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { // OPTIONAL: Makes search string into pattern searchString = "%" + searchString + "%"; // Puts the search string into the selection criteria selectionArgs[0] = searchString; // Starts the query return new CursorLoader( getActivity(), Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, null ); }
קטעי הקוד האלה הם הבסיס לחיפוש הפוך פשוט שמבוסס על סוג ספציפי של נתוני פרטים. זו השיטה הטובה ביותר להשתמש בה אם האפליקציה מתמקדת בסוג מסוים של נתונים, כמו אימיילים, ורוצים לאפשר למשתמשים לקבל את השמות שמשויכים לפריט נתונים.
התאמת איש קשר לפי כל סוג של נתונים
אחזור של איש קשר על סמך כל סוג של נתונים מחזיר אנשי קשר אם אחד מהנתונים שלהם תואם למחרוזת החיפוש, כולל שם, כתובת אימייל, כתובת למשלוח דואר, מספר טלפון וכו'. כך מתקבלת קבוצה רחבה של תוצאות חיפוש. לדוגמה, אם מחרוזת החיפוש היא 'אלון', חיפוש של כל סוג נתונים יחזיר את איש הקשר 'אלון לוי', וגם אנשי קשר שגרים ברחוב 'אלון'.
כדי ליישם את סוג האחזור הזה, קודם צריך להטמיע את הקוד הבא, כפי שמפורט בקטעים הקודמים:
- מבקשים הרשאה לקרוא את הספק.
- הגדרת פריטים ופריסות של ListView.
- הגדרת מקטע (Fragment) שמציג את רשימת אנשי הקשר.
- להגדיר משתנים גלובליים.
- אתחול המקטע (Fragment).
- מגדירים את CursorAdapter ל-ListView.
- מגדירים את המאזין של איש הקשר שנבחר.
- מגדירים הקרנה.
-
מגדירים ערכי קבועים לאינדקסים של העמודות של הסמן.
כדי לבצע אחזור מהסוג הזה, משתמשים באותה טבלה שבה השתמשתם בקטע התאמת איש קשר לפי שם ורישום התוצאות ברשימה. צריך להשתמש גם באותו אינדקס עמודות.
- מגדירים את השיטה onItemClick().
- מאתחלים את הטען.
- מטמיעים את onLoadFinished() ואת onLoaderReset().
בשלבים הבאים מופיע הקוד הנוסף שדרוש כדי להתאים מחרוזת חיפוש לכל סוג של נתונים ולהציג את התוצאות.
הסרת קריטריונים לבחירה
אין להגדיר את הקבועים SELECTION
או את המשתנה mSelectionArgs
.
לא נעשה בהן שימוש באחזור מהסוג הזה.
הטמעת onCreateLoader()
מטמיעים את השיטה onCreateLoader()
ומחזירים CursorLoader
חדש.
אין צורך להמיר את מחרוזת החיפוש לדפוס, כי ספק אנשי הקשר עושה זאת באופן אוטומטי. משתמשים ב-Contacts.CONTENT_FILTER_URI
כ-URI הבסיסי, ומצרפים אליו את מחרוזת החיפוש באמצעות קריאה ל-Uri.withAppendedPath()
. השימוש ב-URI הזה מפעיל באופן אוטומטי חיפוש של כל סוג נתונים, כמו בדוגמה הבאה:
Kotlin
override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> { /* * Appends the search string to the base URI. Always * encode search strings to ensure they're in proper * format. */ val contentUri: Uri = Uri.withAppendedPath( ContactsContract.Contacts.CONTENT_FILTER_URI, Uri.encode(searchString) ) // Starts the query return activity?.let { CursorLoader( it, contentUri, PROJECTION2, null, null, null ) } ?: throw IllegalStateException() }
Java
@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(searchString)); // Starts the query return new CursorLoader( getActivity(), contentUri, PROJECTION, null, null, null ); }
קטעי הקוד האלה הם הבסיס לאפליקציה שמבצעת חיפוש רחב של 'ספק אנשי הקשר'. הטכניקה הזו שימושית לאפליקציות שרוצות להטמיע פונקציונליות דומה למסך רשימת אנשי הקשר של אפליקציית 'אנשים'.