בשיעור הזה נסביר איך לאחזר רשימה של אנשי קשר שהנתונים שלהם תואמים לכל מחרוזת החיפוש או לחלק ממנה, באמצעות השיטות הבאות:
- התאמת שמות של אנשי קשר
- שליפת רשימה של אנשי קשר על ידי התאמת מחרוזת החיפוש לכל הנתונים של שם איש הקשר או לחלק מהם. ניהול אנשי הקשר מאפשר כמה מופעים של אותו שם, כך שהשיטה הזו יכולה להחזיר רשימה של התאמות.
- התאמה לסוג נתונים ספציפי, כמו מספר טלפון
- אחזור רשימה של אנשי קשר על ידי התאמת מחרוזת החיפוש לסוג מסוים של נתונים כמו כתובת אימייל. לדוגמה, בעזרת הטכניקה הזו אפשר להציג רשימה של כל אנשי הקשר שכתובת האימייל שלהם תואמת למחרוזת החיפוש.
- התאמה לכל סוג של נתונים
- אחזור רשימה של אנשי קשר על ידי התאמת מחרוזת החיפוש לכל סוג של נתון פרטים, כולל שם, מספר טלפון, כתובת, כתובת אימייל וכו'. לדוגמה, הטכניקה הזו מאפשרת לקבל כל סוג של נתונים למחרוזת חיפוש, ואז להציג רשימה של אנשי הקשר שהנתונים שלהם תואמים למחרוזת.
הערה: כל הדוגמאות בשיעור הזה משתמשות ב-CursorLoader כדי לאחזר נתונים מספק אנשי הקשר. CursorLoader מריץ את השאילתה שלו בשרשור נפרד משרשור ה-UI. כך אפשר לוודא שהשאילתה לא תאט את זמני התגובה של ממשק המשתמש ותגרום לחוויית משתמש גרועה. מידע נוסף זמין בשיעור ההדרכה של Android בנושא
טעינת נתונים ברקע.
בקשת הרשאה לקרוא את הספק
כדי לבצע חיפוש מכל סוג שהוא בניהול אנשי הקשר, לאפליקציה שלכם צריכה להיות הרשאת
READ_CONTACTS.
כדי לבקש את זה, מוסיפים את הרכיב <uses-permission> לקובץ המניפסט כרכיב צאצא של <manifest>:
<uses-permission android:name="android.permission.READ_CONTACTS" />
התאמה של איש קשר לפי שם ורשימת התוצאות
הטכניקה הזו מנסה להתאים מחרוזת חיפוש לשם של איש קשר או אנשי קשר בטבלה ContactsContract.Contacts של ניהול אנשי הקשר. בדרך כלל רוצים להציג את התוצאות בListView, כדי לאפשר למשתמש לבחור מבין אנשי הקשר התואמים.
הגדרת 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.
הערה: בשיעור הזה לא מתואר ממשק המשתמש לקבלת מחרוזת חיפוש מהמשתמש, כי יכול להיות שתרצו לקבל את המחרוזת באופן עקיף. לדוגמה, אפשר לתת למשתמש אפשרות לחפש אנשי קשר שהשם שלהם תואם למחרוזת בהודעת SMS נכנסת.
שני קובצי הפריסה שכתבתם מגדירים ממשק משתמש שמוצג בו ListView. השלב הבא הוא לכתוב קוד שמשתמש בממשק המשתמש הזה כדי להציג רשימה של אנשי קשר.
הגדרת Fragment שמציג את רשימת אנשי הקשר
כדי להציג את רשימת אנשי הקשר, קודם צריך להגדיר Fragment
שנטען על ידי Activity. שימוש ב-Fragment הוא טכניקה גמישה יותר, כי אפשר להשתמש ב-Fragment אחד כדי להציג את הרשימה וב-Fragment שני כדי להציג את הפרטים של איש קשר שהמשתמש בוחר מהרשימה. באמצעות הגישה הזו, אפשר לשלב בין אחת מהטכניקות שמוצגות בשיעור הזה לבין אחת מהטכניקות שמוצגות בשיעור
אחזור פרטים של איש קשר.
כדי ללמוד איך להשתמש באובייקט אחד או יותר של Fragment מ-Activity, קראו את שיעור ההדרכה
בניית ממשק משתמש דינמי באמצעות מקטעים.
כדי לעזור לכם לכתוב שאילתות לניהול אנשי הקשר, מסגרת Android מספקת מחלקה של ערכים קבועים (contract class) שנקראת 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 או לערך נמוך יותר יוצרת אזהרת Android Lint ב-Android Studio. כדי להשבית את האזהרה הזו, צריך להוסיף את ההערה @SuppressLint("InlinedApi") לפני ההגדרה של FROM_COLUMNS.
הפעלת ה-Fragment
מפעילים את Fragment. מוסיפים את ה-constructor הריק והציבורי שנדרש על ידי מערכת Android, ויוצרים את ממשק המשתמש של האובייקט Fragment בשיטת קריאה חוזרת 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. משתמשים ב-Context של
פעילות האב כשמתקשרים אל setAdapter().
לדוגמה:
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 הנוכחי כרכיב click listener על ידי ציון שהמחלקה מטמיעה את AdapterView.OnItemClickListener, כפי שמוצג בקטע הגדרת Fragment שמציג את רשימת אנשי הקשר.
כדי להמשיך בהגדרת ה-listener, צריך לקשר אותו ל-ListView על ידי קריאה למתודה 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 מבטיח שמפרט החיפוש ייווצר על ידי איגוד ולא על ידי קומפילציה של 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()
בקטע הקודם הגדרתם את רכיב click listener עבור ListView.
עכשיו מטמיעים את הפעולה של המאזין על ידי הגדרת ה-method 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. */ }
מאתחלים את ה-loader
מכיוון שאתם משתמשים ב-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()
מטמיעים את ה-method
onCreateLoader(),
שמופעל על ידי framework הטעינה מיד אחרי שמפעילים את
initLoader().
ב-onCreateLoader(),
מגדירים את התבנית של מחרוזת החיפוש. כדי להפוך מחרוזת לתבנית, מוסיפים את התו '%' כדי לייצג רצף של אפס תווים או יותר, או את התו '_' כדי לייצג תו בודד, או את שניהם. לדוגמה, התבנית "%Jefferson%"
תתאים גם ל- "Thomas Jefferson" וגם ל- "Jefferson Davis".
מחזירים CursorLoader חדש מהשיטה. בשדה Content 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()
מטמיעים את ה-method
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() מופעלת כשמסגרת ה-loader מזהה שהתוצאה 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 בנושא יצירת ממשק חיפוש.
החלקים הבאים בשיעור הזה מציגים דרכים אחרות למציאת אנשי קשר בספק אנשי הקשר.
התאמה של איש קשר לפי סוג נתונים ספציפי
השיטה הזו מאפשרת לכם לציין את סוג הנתונים שאתם רוצים להתאים. אחזור לפי שם הוא דוגמה ספציפית לסוג כזה של שאילתה, אבל אפשר לעשות את זה גם לכל אחד מסוגי הנתונים המפורטים שמשויכים לאיש קשר. לדוגמה, אתם יכולים לאחזר אנשי קשר שיש להם מיקוד ספציפי. במקרה כזה, מחרוזת החיפוש צריכה להתאים לנתונים שמאוחסנים בשורת מיקוד.
כדי להטמיע את סוג האחזור הזה, קודם מטמיעים את הקוד הבא, כמו שמופיע בקטעים הקודמים:
- בקשת הרשאה לקריאת הספק.
- מגדירים את הפריסות של ListView ושל הפריטים.
- מגדירים Fragment שמציג את רשימת אנשי הקשר.
- הגדרת משתנים גלובליים.
- מפעילים את ה-Fragment.
- מגדירים את CursorAdapter עבור ListView.
- הגדרת המאזין לאיש הקשר שנבחר.
-
מגדירים קבועים לאינדקסים של עמודות הסמן.
למרות שמאחזרים נתונים מטבלה אחרת, סדר העמודות בהקרנה זהה, ולכן אפשר להשתמש באותם אינדקסים עבור הסמן.
- מגדירים את השיטה onItemClick().
- מאתחלים את ה-loader.
- מטמיעים את 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. תוחמים את הערך במירכאות בודדות על ידי שרשור של התו "'" (מירכאות בודדות) בתחילת הקבוע ובסופו. אחרת, הספק מפרש את הערך כשם של משתנה ולא כערך של מחרוזת. אין צורך להשתמש בפלייסהולדר לערך הזה, כי אתם משתמשים בקבוע ולא בערך שהמשתמש מספק.
לדוגמה:
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 חדש מהשיטה הזו, ומשתמשים בהיטל, בביטוי הטקסט של הבחירה ובמערך הבחירה כארגומנטים. ל-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 ); }
קטעי הקוד האלה הם הבסיס לחיפוש הפוך בסיסי שמבוסס על סוג ספציפי של נתונים מפורטים. זו השיטה הכי טובה אם האפליקציה מתמקדת בסוג מסוים של נתונים, כמו כתובות אימייל, ואתם רוצים לאפשר למשתמשים לקבל את השמות שמשויכים לנתון מסוים.
התאמת איש קשר לפי כל סוג של נתונים
אם מאחזרים איש קשר על סמך כל סוג של נתונים, אנשי הקשר יוחזרו אם אחד מהנתונים שלהם תואם למחרוזת החיפוש, כולל שם, כתובת אימייל, כתובת למשלוח דואר, מספר טלפון וכו'. כתוצאה מכך, מתקבלת קבוצה רחבה של תוצאות חיפוש. לדוגמה, אם מחפשים את המחרוזת 'Doe', חיפוש של כל סוג נתונים יחזיר את איש הקשר 'John Doe', וגם אנשי קשר שגרים ברחוב Doe.
כדי להטמיע סוג כזה של אחזור, קודם מטמיעים את הקוד הבא, כמו שמופיע בקטעים הקודמים:
- בקשת הרשאה לקריאת הספק.
- מגדירים את הפריסות של ListView ושל הפריטים.
- מגדירים Fragment שמציג את רשימת אנשי הקשר.
- הגדרת משתנים גלובליים.
- מפעילים את ה-Fragment.
- מגדירים את CursorAdapter עבור ListView.
- מגדירים את מאזין אנשי הקשר שנבחרו.
- הגדרת הקרנה.
-
מגדירים קבועים לאינדקסים של העמודות של הסמן.
כדי לאחזר את המידע הזה, משתמשים באותה טבלה שבה השתמשתם בקטע התאמה של איש קשר לפי שם ורישום התוצאות. צריך להשתמש גם באותם אינדקסים של עמודות.
- מגדירים את השיטה onItemClick().
- מאתחלים את ה-loader.
- מטמיעים את 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 ); }
קטעי הקוד האלה הם הבסיס לאפליקציה שמבצעת חיפוש רחב של ניהול אנשי הקשר. השיטה הזו שימושית לאפליקציות שרוצות להטמיע פונקציונליות שדומה למסך רשימת אנשי הקשר של אפליקציית "אנשי קשר".