بازیابی فهرست مخاطبین

این درس به شما نشان می‌دهد که چگونه بااستفاده از تکنیک‌های زیر، فهرستی از مخاطبین را که داده‌هایشان با تمام یا بخشی از رشته جستجو مطابقت دارد بازیابی کنید:

مطابقت نام مخاطب
فهرستی از مخاطبین را با مطابقت دادن رشته جستجو با همه یا بخشی از داده‌های نام مخاطب بازیابی می‌کند. «ارائه‌دهنده مخاطبین» اجازه می‌دهد چند نمونه از یک نام وجود داشته باشد، بنابراین این تکنیک می‌تواند فهرستی از موارد منطبق را برگرداند.
مطابقت دادن نوع خاصی از داده‌ها، مثل شماره تلفن
فهرست مخاطبین را با مطابقت دادن رشته جستجو با نوع خاصی از جزئیات داده‌ها، مثل نشانی ایمیل، بازیابی کنید. برای مثال، این تکنیک به شما امکان می‌دهد همه مخاطبینی را که نشانی ایمیل آن‌ها با رشته جستجو مطابقت دارد فهرست کنید.
مطابقت با هر نوع داده
با مطابقت دادن رشته جستجو با هر نوع داده جزئیات، ازجمله نام، شماره تلفن، نشانی خیابان، نشانی ایمیل، و غیره، فهرست مخاطبین را بازیابی کنید. برای مثال، این تکنیک به شما امکان می‌دهد هر نوع داده‌ای را برای رشته جستجو بپذیرید و سپس مخاطبینی را که داده‌هایشان با رشته مطابقت دارد فهرست کنید.

توجه: همه مثال‌های این درس از CursorLoader برای بازیابی داده‌ها از «ارائه‌دهنده مخاطبین» استفاده می‌کنند. CursorLoader پُرسمان خود را در رشته‌ای اجرا می‌کند که از رشته میانای کاربر جدا است. این کار تضمین می‌کند که پُرسمان زمان پاسخ‌دهی واسط کاربر را کند نمی‌کند و باعث تجربه کاربری ضعیف نمی‌شود. برای اطلاعات بیشتر، کلاس آموزشی 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 از ابزارک داخلی Android ListView استفاده می‌کند 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 از ابزارک داخلی Android TextView android:text1 استفاده می‌کند.

توجه: این درس واسط کاربر برای دریافت رشته جستجو از کاربر را توصیف نمی‌کند، زیرا ممکن است بخواهید رشته را به‌طور غیرمستقیم دریافت کنید. برای مثال، می‌توانید به کاربر گزینه‌ای بدهید تا مخاطبینی را که نامشان با رشته‌ای در پیامک ورودی مطابقت دارد جستجو کند.

دو فایل چیدمانی که نوشته‌اید واسط کاربری را تعریف می‌کند که ListView را نشان می‌دهد. مرحله بعدی نوشتن کدی است که از این میانای کاربر برای نمایش فهرست مخاطبین استفاده می‌کند.

تعریف Fragment که فهرست مخاطبین را نمایش می‌دهد

برای نمایش فهرست مخاطبین، ابتدا Fragment را تعریف کنید که توسط Activity بار می‌شود. استفاده از Fragment روش انعطاف‌پذیرتری است، زیرا می‌توانید از یک Fragment برای نمایش فهرست و از Fragment دیگری برای نمایش جزئیات مخاطبی که کاربر از فهرست انتخاب می‌کند استفاده کنید. بااستفاده از این رویکرد، می‌توانید یکی از تکنیک‌های ارائه‌شده در این درس را با یکی از تکنیک‌های درس بازیابی جزئیات مخاطب ترکیب کنید.

برای آشنایی با نحوه استفاده از یک یا چند Fragment شیء از Activity، کلاس آموزشی ساختن واسط کاربر پویا با «تکه‌تکه کردن» را بخوانید.

برای کمک به شما در نوشتن پُرسمان‌ها دربرابر «ارائه‌دهنده مخاطبین»، چارچوب Android یک کلاس قرارداد به‌نام ContactsContract ارائه می‌دهد که ثابت‌ها و روش‌های مفیدی را برای دسترسی به ارائه‌دهنده تعریف می‌کند. وقتی از این کلاس استفاده می‌کنید، لازم نیست ثابت‌های خودتان را برای نشانی‌های وب محتوا، نام‌های جدول، یا ستون‌ها تعریف کنید. برای استفاده از این کلاس، عبارت زیر را اضافه کنید:

کاتلین

import android.provider.ContactsContract

جاوا

import android.provider.ContactsContract;

ازآنجایی‌که کد از CursorLoader برای بازیابی داده‌ها از ارائه‌دهنده استفاده می‌کند، باید مشخص کنید که واسط بارکننده LoaderManager.LoaderCallbacks را پیاده‌سازی می‌کند. همچنین، برای کمک به تشخیص اینکه کاربر کدام مخاطب را از فهرست نتایج جستجو انتخاب می‌کند، واسط آداپتور AdapterView.OnItemClickListener را پیاده‌سازی کنید. برای مثال:

کاتلین

...
import android.support.v4.app.Fragment
import android.support.v4.app.LoaderManager
import android.widget.AdapterView
...
class ContactsFragment :
        Fragment(),
        LoaderManager.LoaderCallbacks<Cursor>,
        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<Cursor>,
        AdapterView.OnItemClickListener {

تعریف متغیرهای سراسری

متغیرهای سراسری را که در بخش‌های دیگر کد استفاده می‌شوند تعریف کنید:

کاتلین

...
/*
 * 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

جاوا

    ...
    /*
     * 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 برنامه را روی ۱۰ یا پایین‌تر تنظیم کنید، در Android Studio هشدار Android Lint تولید می‌شود. برای خاموش کردن این هشدار، @SuppressLint("InlinedApi") را قبل‌از تعریف FROM_COLUMNS اضافه کنید.

مقداردهی اولیه Fragment

Fragment را مقداردهی اولیه کنید. سازنده عمومی خالی موردنیاز سیستم Android را اضافه کنید و واسط کاربر شیء Fragment را در روش بازخوان onCreateView() باد کنید. برای مثال:

کاتلین

    // 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)
    }

جاوا

    // 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 فعالیت ولی استفاده کنید. برای مثال:

کاتلین

    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
        }
    }

جاوا

    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 را پیاده‌سازی می‌کند تعریف کردید، همان‌طور که در بخش تعریف «تکه‌ای» که فهرست مخاطبین را نمایش می‌دهد نشان داده شده است.

برای ادامه راه‌اندازی شنونده، آن را با فراخوانی روش setOnItemClickListener() در onActivityCreated() به ListView ملزم کنید. برای مثال:

کاتلین

    fun onActivityCreated(savedInstanceState:Bundle) {
        ...
        // Set the item click listener to be the current fragment.
        contactsList.onItemClickListener = this
        ...
    }

جاوا

    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 (میانای برنامه‌سازی کاربردی نسخه ۱۱) و نسخه‌های جدیدتر، نام این ستون Contacts.DISPLAY_NAME_PRIMARY است؛ در نسخه‌های قدیمی‌تر، نام آن Contacts.DISPLAY_NAME است.

ستون Contacts._ID توسط فرایند اتصال SimpleCursorAdapter استفاده می‌شود. Contacts._ID و LOOKUP_KEY باهم استفاده می‌شوند تا شناسه منبع یکسان محتوا را برای مخاطبی که کاربر انتخاب می‌کند بسازند.

کاتلین

...
@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
)

جاوا

...
@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 تعریف کنید، زیرا نمایه‌ها با ترتیب نام ستون‌ها در طرح‌ریزی شما یکسان هستند. برای مثال:

کاتلین

// 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

جاوا

// 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;

معیارهای انتخاب را مشخص کنید

برای مشخص کردن داده‌های موردنظرتان، ترکیبی از عبارت‌های نوشتاری و متغیرها ایجاد کنید که به ارائه‌دهنده می‌گوید ستون‌های داده را جستجو کند و مقادیر را پیدا کند.

برای عبارت نوشتاری، ثابتی را تعریف کنید که ستون‌های جستجو را فهرست کند. اگرچه این عبارت می‌تواند حاوی مقادیر نیز باشد، اما روش ترجیحی این است که مقادیر را با جای‌بان «؟» نشان دهید. درطول بازیابی، جای‌بان با مقادیری از آرایه جایگزین می‌شود. استفاده از «؟» به‌عنوان جای‌بان تضمین می‌کند که مشخصات جستجو با پیوند دادن تولید شود نه با ترجمه SQL. این رویه امکان تزریق SQL مخرب را ازبین می‌برد. برای مثال:

کاتلین

// 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)

جاوا

    // 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()، کنش را برای شنونده پیاده‌سازی کنید:

کاتلین

    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.
             */
        }
    }

جاوا

    @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() انجام دهید، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

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)

جاوا

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 جدیدی را از روش برگردانید. برای نشانی وب محتوا، از Contacts.CONTENT_URI استفاده کنید. این URI به کل جدول اشاره دارد، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

    ...
    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()
    }

جاوا

    ...
    @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 را با نتایج جستجو به‌روز می‌کند:

کاتلین

    override fun onLoadFinished(loader: Loader<Cursor>, cursor: Cursor) {
        // Put the result Cursor in the adapter for the ListView
        cursorAdapter?.swapCursor(cursor)
    }

جاوا

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        // Put the result Cursor in the adapter for the ListView
        cursorAdapter.swapCursor(cursor);
    }

وقتی چارچوب بارکننده تشخیص دهد که نتیجه Cursor حاوی داده‌های قدیمی است، روش onLoaderReset() فراخوانده می‌شود. مرجع SimpleCursorAdapter به Cursor موجود را حذف کنید. اگر این کار را نکنید، چارچوب بارکننده Cursor را بازیافت نخواهد کرد، که باعث نشت حافظه می‌شود. برای مثال:

کاتلین

    override fun onLoaderReset(loader: Loader<Cursor>) {
        // Delete the reference to the existing Cursor
        cursorAdapter?.swapCursor(null)
    }

جاوا

    @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() را تعریف کنید.
  • مقداردهی اولیه بارکننده.
  • ‫onLoadFinished() و onLoaderReset() را پیاده‌سازی کنید.

مراحل زیر کد اضافی موردنیاز برای مطابقت دادن رشته جستجو با نوع خاصی از داده‌های جزئیات و نمایش نتایج را نشان می‌دهد.

نوع داده و جدول را انتخاب کنید

برای جستجوی نوع خاصی از داده‌های جزئی، باید مقدار نوع MIME سفارشی را برای نوع داده بدانید. هر نوع داده دارای یک مقدار نوع MIME منحصربه‌فرد است که توسط یک ثابت CONTENT_ITEM_TYPE در زیرکلاس ContactsContract.CommonDataKinds مرتبط با نوع داده تعریف شده است. زیرکلاس‌ها نام‌هایی دارند که نوع داده آن‌ها را نشان می‌دهد؛ برای مثال، زیرکلاس داده‌های ایمیل ContactsContract.CommonDataKinds.Email است و نوع MIME سفارشی برای داده‌های ایمیل با ثابت Email.CONTENT_ITEM_TYPE تعریف می‌شود.

از جدول ContactsContract.Data برای جستجو استفاده کنید. همه ثابت‌هایی که برای عبارت انتخاب، ترتیب مرتب‌سازی، و طرح‌ریزی‌تان نیاز دارید در این جدول تعریف شده‌اند یا از آن به‌ارث برده شده‌اند.

تعریف کردن طرح

برای تعریف کردن یک طرح، یک یا چند ستون تعریف‌شده در ContactsContract.Data یا کلاس‌هایی را که از آن ارث می‌برد انتخاب کنید. «ارائه‌دهنده مخاطبین» قبل‌از برگرداندن ردیف‌ها، پیوستن ضمنی بین ContactsContract.Data و جدول‌های دیگر انجام می‌دهد. برای مثال:

کاتلین

@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
)

جاوا

    @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 است. مقدار را با الحاق نویسه «'» (گیومه تکی) به ابتدا و انتهای ثابت، در گیومه تکی قرار دهید؛ درغیراین‌صورت، ارائه‌دهنده مقدار را به‌عنوان نام متغیر تفسیر می‌کند نه به‌عنوان مقدار رشته. برای این مقدار نیازی به استفاده از جای‌بان نیست، زیرا به‌جای مقدار ارائه‌شده توسط کاربر از مقدار ثابت استفاده می‌کنید.

برای مثال:

کاتلین

/*
 * 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}'"

جاوا

    /*
     * 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 + "'";

سپس، متغیرهایی را برای دربرگرفتن آرگومان انتخاب تعریف کنید:

کاتلین

    private var searchString: String? = null
    private val selectionArgs: Array<String> = arrayOf("")

جاوا

    String searchString;
    String[] selectionArgs = { "" };

پیاده‌سازی onCreateLoader()

اکنون که داده‌های موردنظرتان و نحوه پیدا کردن آن را مشخص کرده‌اید، پُرسمانی را در پیاده‌سازی onCreateLoader() تعریف کنید. بااستفاده از طرح‌ریزی، عبارت نوشتاری انتخاب، و آرایه انتخاب به‌عنوان آرگومان، CursorLoader جدیدی را از این روش برگردانید. برای شناسه منبع یکسان محتوا، از Data.CONTENT_URI استفاده کنید. برای مثال:

کاتلین

    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()
    }

جاوا

@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 Street» زندگی می‌کنند برمی‌گرداند.

برای پیاده‌سازی این نوع بازیابی، ابتدا کد زیر را پیاده‌سازی کنید، همان‌طور که در بخش‌های قبلی فهرست شده است:

  • درخواست اجازه برای خواندن ارائه‌دهنده.
  • ‫ListView و چیدمان‌های مورد را تعریف کنید.
  • ‫Fragment را که فهرست مخاطبین را نمایش می‌دهد تعریف کنید.
  • متغیرهای سراسری را تعریف کنید.
  • مقداردهی اولیه Fragment.
  • ‫CursorAdapter را برای ListView تنظیم کنید.
  • شنودگر مخاطب انتخاب‌شده را تنظیم کنید.
  • پیش‌بینی را تعریف کنید.
  • ثابت‌ها را برای نمایه‌های ستون «مکان‌نما» تعریف کنید.

    برای این نوع بازیابی، از همان جدولی استفاده می‌کنید که در بخش مطابقت دادن مخاطب براساس نام و فهرست کردن نتایج استفاده کردید. از همان نمایه‌های ستون نیز استفاده کنید.

  • متد onItemClick() را تعریف کنید.
  • مقداردهی اولیه بارکننده.
  • onLoadFinished()‎ و onLoaderReset()‎ را پیاده‌سازی کنید.

مراحل زیر کد اضافه‌ای را که برای مطابقت رشته جستجو با هر نوع داده و نمایش نتایج نیاز دارید نشان می‌دهد.

برداشتن معیارهای انتخاب

ثابت‌های SELECTION یا متغیر mSelectionArgs را تعریف نکنید. این‌ها در این نوع بازیابی استفاده نمی‌شوند.

پیاده‌سازی onCreateLoader()‎

روش onCreateLoader() را پیاده‌سازی کنید و CursorLoader جدیدی را برگردانید. نیازی نیست رشته جستجو را به الگو تبدیل کنید، زیرا «ارائه‌دهنده مخاطبین» این کار را به‌طور خودکار انجام می‌دهد. از Contacts.CONTENT_FILTER_URI به‌عنوان نشانی وب پایه استفاده کنید و رشته جستجوی خود را با فراخوانی Uri.withAppendedPath() به آن اضافه کنید. استفاده از این URI به‌طور خودکار جستجو برای هر نوع داده‌ای را راه‌اندازی می‌کند، همان‌طور که در مثال زیر نشان داده شده است:

کاتلین

    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()
    }

جاوا

    @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
        );
    }

این تکه‌کدها پایه برنامه‌ای هستند که جستجوی گسترده‌ای در «ارائه‌دهنده مخاطبین» انجام می‌دهد. این تکنیک برای برنامه‌هایی مفید است که می‌خواهند عملکردی مشابه با صفحه فهرست مخاطبین برنامه «افراد» را پیاده‌سازی کنند.