אחזור רשימה של אנשי קשר

במדריך הזה תלמדו איך לאחזר רשימה של אנשי קשר שהנתונים שלהם תואמים לכל מחרוזת החיפוש או לחלק ממנה, באמצעות השיטות הבאות:

התאמה לשמות של אנשי קשר
אחזור רשימה של אנשי קשר על ידי התאמת מחרוזת החיפוש לכל איש הקשר או לחלק ממנו נתוני שמות. ספק אנשי הקשר מאפשר מספר מופעים של אותו שם, כך יכולה להחזיר רשימה של התאמות.
התאמה לסוג נתונים ספציפי, כמו מספר טלפון
אחזור רשימת אנשי קשר על ידי התאמת מחרוזת החיפוש לסוג מסוים של פרטי נתונים, כמו כתובת אימייל. לדוגמה, הטכניקה הזו מאפשרת לכם לקבל רשימה של כל אנשי הקשר שכתובת האימייל שלהם תואמת למחרוזת החיפוש.
התאמה לכל סוג של נתונים
מאחזרים רשימה של אנשי קשר על ידי התאמת מחרוזת החיפוש לסוג כלשהו של נתוני פרטים, כולל שם, מספר טלפון, רחוב, כתובת אימייל וכן הלאה. לדוגמה, הטכניקה הזו מאפשרת לקבל כל סוג של נתונים עבור מחרוזת חיפוש, ולאחר מכן להציג את אנשי הקשר שהנתונים שלהם תואמים למחרוזת.

הערה: בכל הדוגמאות במדריך הזה נעשה שימוש ב-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 שנטען על ידי Activity. השימוש ב-Fragment הוא טכניקה גמישה יותר, כי אפשר להשתמש ב-Fragment אחד כדי להציג את הרשימה וב-Fragment שני כדי להציג את הפרטים של איש הקשר שהמשתמש בוחר מהרשימה. בעזרת הגישה הזו, תוכלו לשלב אחת מהשיטות שמפורטות במדריך הזה עם אחת מהשיטות שמפורטות במדריך אחזור פרטים של איש קשר.

כדי ללמוד איך להשתמש באובייקט Fragment אחד או יותר מ Activity, לקריאת שיעור האימון פיתוח ממשק משתמש דינמי באמצעות Fragments.

כדי לעזור לך לכתוב שאילתות מול ספק אנשי הקשר, המסגרת של Android מספקת שנקרא 'ContactsContract', שמגדיר קבועים ושיטות לגישה לספק. כשמשתמשים בכיתה הזו, לא צריך להגדיר קבועים משלכם למזהי URI של תוכן, לשמות של טבלאות או לעמודות. כדי להשתמש בכיתה הזו, לכלול את ההצהרה הבאה:

Kotlin

import android.provider.ContactsContract

Java

import android.provider.ContactsContract;

מכיוון שהקוד משתמש ב-CursorLoader כדי לאחזר נתונים יש לציין שהוא מיישם את ממשק ה-Loader 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 ממשק משתמש בשיטת הקריאה החוזרת 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, כפי שמתואר בקטע הגדרת קטע (Fragment) שמוצגת בו רשימת אנשי הקשר.

כדי להמשיך בהגדרת המאזין, צריך לקשר אותו ל-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, עכשיו צריך להטמיע את השיטה הנדרשת 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 כדי לאחזר נתונים, עליכם לאתחל את שרשור הרקע ומשתנים אחרים ששולטים באחזור האסינכרוני. ביצוע האתחול תוך 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(), קוראים ל-framework של טעינה מיד אחרי שקוראים initLoader().

ב-onCreateLoader(), להגדיר את תבנית מחרוזת החיפוש. כדי להפוך מחרוזת לדפוס, מוסיפים תווים של '%' (אחוז) כדי לייצג רצף של אפס תווים או יותר, או תווים של '_' (קו תחתון) כדי לייצג תו יחיד, או את שניהם. לדוגמה, הדפוס '%Jefferson%' יתאים גם ל-"תומס ג'פרסון" וגם "ג'פרסון דייוויס".

החזרת CursorLoader חדש מה-method. ב-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() . ה-framework של טעינה 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.
  • הגדרת האזנה לאנשי הקשר שנבחרה.
  • מגדירים ערכי קבועים לאינדקסים של העמודות Cursor.

    אמנם אתם מאחזרים נתונים מטבלה אחרת, אבל הסדר של העמודות בתצוגה הוא זהה, כך שאפשר להשתמש באותם אינדקסים עבור הסמן.

  • מגדירים את השיטה 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.
  • הגדרת האזנה לאנשי הקשר שנבחרה.
  • מגדירים הקרנה.
  • מגדירים ערכי קבועים לאינדקסים של העמודות Cursor.

    לאחזור מהסוג הזה משתמשים באותה טבלה שבה השתמשתם בקטע התאמה לאיש קשר לפי שם ופירוט התוצאות. צריך להשתמש גם באותו אינדקס עמודות.

  • מגדירים את השיטה 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
        );
    }

קטעי הקוד האלה הם הבסיס לאפליקציה שמבצעת חיפוש רחב של ספק אנשי הקשר. הטכניקה הזו שימושית לאפליקציות שרוצות להטמיע פונקציונליות דומה למסך רשימת אנשי הקשר של אפליקציית 'אנשים'.