ดึงข้อมูลรายชื่อติดต่อ

บทเรียนนี้จะแสดงวิธีดึงข้อมูลรายชื่อติดต่อที่มีข้อมูลตรงกับสตริงการค้นหาทั้งหมดหรือบางส่วนโดยใช้เทคนิคต่อไปนี้

จับคู่ชื่อผู้ติดต่อ
เรียกดูรายชื่อรายชื่อติดต่อโดยการจับคู่สตริงการค้นหากับรายชื่อติดต่อทั้งหมดหรือบางส่วน ข้อมูลชื่อ ผู้ให้บริการรายชื่อติดต่ออนุญาตให้ใช้ชื่อเดียวกันได้หลายรายการ ดังนั้นเทคนิคนี้จึงแสดงรายการที่ตรงกันได้
จับคู่ข้อมูลบางประเภท เช่น หมายเลขโทรศัพท์
ดึงรายชื่อผู้ติดต่อโดยการจับคู่สตริงการค้นหากับประเภทรายละเอียดที่ต้องการ ข้อมูล เช่น อีเมล ตัวอย่างเช่น เทคนิคนี้ช่วยให้คุณแสดงรายการ รายชื่อติดต่อที่มีที่อยู่อีเมลตรงกับสตริงการค้นหา
จับคู่ข้อมูลประเภทใดก็ได้
ดึงข้อมูลรายชื่อติดต่อโดยการจับคู่สตริงการค้นหากับข้อมูลแบบละเอียดประเภทใดก็ได้ ซึ่งรวมถึงชื่อ หมายเลขโทรศัพท์ ที่อยู่ อีเมล และอื่นๆ ตัวอย่างเช่น เทคนิคนี้ช่วยให้คุณยอมรับข้อมูลประเภทใดก็ได้สำหรับสตริงการค้นหา จากนั้นแสดงรายชื่อรายชื่อติดต่อที่มีข้อมูลที่ตรงกับสตริง

หมายเหตุ: ตัวอย่างทั้งหมดในบทเรียนนี้ใช้ CursorLoader เพื่อดึงข้อมูลจากรายชื่อติดต่อ ผู้ให้บริการ CursorLoader จะเรียกใช้การค้นหาในเธรดที่แยกจากเธรด UI วิธีนี้ช่วยให้มั่นใจว่าการค้นหาจะไม่ทำให้ UI ช้าลง เวลาในการตอบสนองและทำให้ผู้ใช้ได้รับประสบการณ์ที่ไม่ดี ดูข้อมูลเพิ่มเติมได้ที่ชั้นเรียนการฝึกอบรม Android การโหลดข้อมูลในเบื้องหลัง

ขอสิทธิ์อ่านผู้ให้บริการ

หากต้องการค้นหาประเภทใดก็ตามของผู้ให้บริการรายชื่อติดต่อ แอปของคุณต้องมีสิทธิ์ READ_CONTACTS หากต้องการขอสิทธิ์นี้ ให้เพิ่มองค์ประกอบ <uses-permission> นี้ลงในไฟล์ Manifest เป็นองค์ประกอบย่อยของ <manifest>

    <uses-permission android:name="android.permission.READ_CONTACTS" />

จับคู่รายชื่อติดต่อตามชื่อและแสดงผลลัพธ์

เทคนิคนี้จะพยายามจับคู่สตริงการค้นหากับชื่อของรายชื่อติดต่อหรือรายชื่อติดต่อใน ติดต่อตาราง ContactsContract.Contacts ของผู้ให้บริการ โดยปกติแล้ว คุณจะต้องแสดงผลลัพธ์ใน ListView เพื่อให้ผู้ใช้เลือกรายชื่อติดต่อที่ตรงกันได้

กําหนดเลย์เอาต์ ListView และรายการ

หากต้องการแสดงผลการค้นหาใน ListView คุณต้องมีไฟล์เลย์เอาต์หลัก ที่กำหนด UI ทั้งหมด รวมถึง 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

หมายเหตุ: บทเรียนนี้ไม่ได้อธิบาย UI สำหรับการรับสตริงค้นหาจาก เพราะคุณอาจต้องได้รับสตริงทางอ้อม เช่น คุณสามารถให้ผู้ใช้ ตัวเลือกสำหรับค้นหารายชื่อติดต่อที่ชื่อตรงกับสตริงในข้อความขาเข้า

ไฟล์เลย์เอาต์ 2 ไฟล์ที่คุณเขียนจะกำหนดอินเทอร์เฟซผู้ใช้ที่แสดง ListView ขั้นตอนถัดไปคือการเขียนโค้ดที่ใช้ UI นี้เพื่อแสดงรายชื่อติดต่อ

กำหนด Fregment ที่แสดงรายการรายชื่อติดต่อ

หากต้องการแสดงรายชื่อรายชื่อติดต่อ ให้เริ่มด้วยการกำหนด Fragment โหลดโดย Activity การใช้ Fragmentเป็นเทคนิคที่ยืดหยุ่นกว่าเนื่องจากคุณใช้ Fragment 1 ชิ้นเพื่อแสดงรายการและอีก 1 วินาที Fragmentเพื่อแสดงรายละเอียดของรายชื่อติดต่อที่ผู้ใช้ เลือกจากรายการ เมื่อใช้แนวทางนี้ คุณสามารถรวมเทคนิคใดเทคนิคหนึ่งจากบทเรียนนี้เข้ากับเทคนิคจากบทเรียนดึงข้อมูลรายละเอียดสำหรับรายชื่อติดต่อ

หากต้องการดูวิธีใช้ออบเจ็กต์ Fragment อย่างน้อย 1 รายการจาก Activity โปรดอ่านชั้นเรียนการฝึกอบรม สร้าง UI แบบไดนามิกด้วย Framgnet

เฟรมเวิร์ก Android มีคลาสสัญญาที่เรียกว่า ContactsContract ซึ่งจะกำหนดค่าคงที่และเมธอดที่มีประโยชน์สำหรับการเข้าถึงผู้ให้บริการเพื่อช่วยให้คุณเขียนการค้นหากับ Contacts Provider ได้ เมื่อใช้คลาสนี้ คุณไม่จําเป็นต้องกําหนดค่าคงที่ของคุณเองสําหรับ 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 เพิ่มคอนสตรัคเตอร์แบบว่างและแบบสาธารณะที่ระบบ Android ต้องการ และขยาย UI ของออบเจ็กต์ 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);
    }

ตั้งค่า Listener รายชื่อติดต่อที่เลือก

เมื่อแสดงผลการค้นหา คุณมักจะต้องอนุญาตให้ผู้ใช้เลือกรายชื่อติดต่อรายการเดียวเพื่อดำเนินการต่อ ตัวอย่างเช่น เมื่อผู้ใช้คลิกรายชื่อติดต่อ คุณสามารถแสดงที่อยู่ของรายชื่อติดต่อบนแผนที่ ในการเปิดใช้ฟีเจอร์นี้ ก่อนอื่นคุณต้องกำหนด Fragment เป็นผู้ฟังการคลิกด้วยการระบุว่าคลาส ใช้ AdapterView.OnItemClickListener ดังที่แสดงในส่วน กำหนดส่วนย่อยที่แสดงรายชื่อติดต่อ

หากต้องการตั้งค่า 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 ตอนนี้คุณต้องใช้วิธีการที่จำเป็นแล้ว onItemClick() ซึ่ง จัดการกิจกรรมการคลิก โปรดดูรายละเอียดในส่วนถัดไป

กำหนดการฉายภาพ

กําหนดค่าคงที่ซึ่งมีคอลัมน์ที่คุณต้องการแสดงผลจากคําค้นหา แต่ละรายการใน ListView จะแสดงชื่อที่แสดงของรายชื่อติดต่อ ซึ่งมีรูปแบบหลักของชื่อผู้ติดต่อ ใน Android 3.0 (API เวอร์ชัน 11) ขึ้นไป ชื่อของคอลัมน์นี้คือ Contacts.DISPLAY_NAME_PRIMARY ส่วนในเวอร์ชันก่อนหน้าชื่อของคอลัมน์นี้คือ Contacts.DISPLAY_NAME

กระบวนการเชื่อมโยง SimpleCursorAdapter ใช้คอลัมน์ Contacts._ID ระบบจะใช้ 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;

ระบุเกณฑ์การเลือก

หากต้องการระบุข้อมูลที่ต้องการ ให้สร้างชุดค่าผสมของนิพจน์ข้อความและตัวแปร ซึ่งบอกผู้ให้บริการเกี่ยวกับคอลัมน์ข้อมูลที่จะค้นหาและค่าที่จะค้นหา

สําหรับนิพจน์ข้อความ ให้กําหนดค่าคงที่ซึ่งแสดงรายการคอลัมน์การค้นหา แม้ว่านิพจน์นี้จะมีค่าด้วย แต่แนวทางปฏิบัติแนะนำคือให้แสดงค่าด้วยตัวยึดตําแหน่ง "?" ระหว่างการดึงข้อมูล ตัวยึดตำแหน่งจะถูกแทนที่ด้วยค่าจาก อาร์เรย์ การใช้ "?" เป็นตัวยึดตําแหน่งช่วยให้มั่นใจว่าระบบจะสร้างข้อกําหนดการค้นหาโดยการเชื่อมโยง ไม่ใช่โดยการคอมไพล์ 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()

ในส่วนก่อนหน้า คุณตั้งค่า Listener การคลิกรายการสำหรับ ListView ตอนนี้ให้ใช้การดำเนินการสําหรับ Listener โดยกําหนดเมธอดดังนี้ 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(), ซึ่งจะเรียกโดยเฟรมเวิร์กตัวโหลดทันทีหลังจากที่คุณเรียก initLoader()

ใน onCreateLoader() ให้ตั้งค่ารูปแบบสตริงการค้นหา หากต้องการทำให้สตริงเป็นรูปแบบ ให้แทรก "%" (เปอร์เซ็นต์) อักขระเพื่อแสดงลำดับของอักขระศูนย์ตัวขึ้นไป หรือ "_" (ขีดล่าง) อักขระเพื่อแสดงอักขระตัวเดียว หรือทั้งสองตัว ตัวอย่างเช่น รูปแบบ "%Jefferson%" จะตรงกับทั้ง "โทมัส เจฟเฟอร์สัน" และ "Jefferson Davis"

แสดงผล CursorLoader ใหม่จากเมธอด สำหรับเนื้อหา URI ให้ใช้ Contacts.CONTENT_URI URI นี้ใช้อ้างอิงทั้งตาราง ดังที่ปรากฏในตัวอย่างต่อไปนี้

Kotlin

    ...
    override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> {
        /*
         * Makes search string into pattern and
         * stores it in the selection array
         */
        selectionArgs[0] = "%$mSearchString%"
        // Starts the query
        return activity?.let {
            return CursorLoader(
                    it,
                    ContactsContract.Contacts.CONTENT_URI,
                    PROJECTION,
                    SELECTION,
                    selectionArgs,
                    null
            )
        } ?: throw IllegalStateException()
    }

Java

    ...
    @Override
    public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) {
        /*
         * Makes search string into pattern and
         * stores it in the selection array
         */
        selectionArgs[0] = "%" + searchString + "%";
        // Starts the query
        return new CursorLoader(
                getActivity(),
                ContactsContract.Contacts.CONTENT_URI,
                PROJECTION,
                SELECTION,
                selectionArgs,
                null
        );
    }

ติดตั้งใช้งาน onLoadFinished() และ onLoaderreset()

ใช้เมธอด onLoadFinished() การเรียกใช้เฟรมเวิร์กของตัวโหลด onLoadFinished() เมื่อผู้ให้บริการรายชื่อติดต่อแสดงผลการค้นหา ด้วยวิธีนี้ ให้ใส่ ผลลัพธ์ Cursor ใน SimpleCursorAdapter ซึ่งจะอัปเดต ListView ด้วยผลการค้นหาโดยอัตโนมัติ

Kotlin

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

Java

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

เมธอด onLoaderReset() จะถูกเรียกใช้เมื่อเฟรมเวิร์กของตัวโหลดตรวจพบว่า ผลลัพธ์ Cursor มีข้อมูลเก่า ลบการอ้างอิง SimpleCursorAdapter ไปยัง Cursor ที่มีอยู่ หากคุณไม่ทำเช่นนี้ เฟรมเวิร์กตัวโหลดจะไม่ รีไซเคิล Cursor ซึ่งทำให้หน่วยความจำรั่วไหล เช่น

Kotlin

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

Java

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        // Delete the reference to the existing Cursor
        cursorAdapter.swapCursor(null);

    }

ตอนนี้คุณมีข้อมูลสำคัญของแอปที่ตรงกับสตริงการค้นหากับชื่อผู้ติดต่อและการคืนสินค้าแล้ว ผลลัพธ์เป็น ListView ผู้ใช้คลิกชื่อรายชื่อติดต่อเพื่อเลือกได้ การดำเนินการนี้จะทริกเกอร์ Listener ซึ่งคุณสามารถทำงานกับข้อมูลของรายชื่อติดต่อเพิ่มเติมได้ เช่น คุณสามารถเรียกดูรายละเอียดของรายชื่อติดต่อ หากต้องการดูวิธีดำเนินการ ให้ไปที่บทเรียนถัดไป ซึ่งก็คือเรียกดูรายละเอียดสำหรับรายชื่อติดต่อ

หากต้องการดูข้อมูลเพิ่มเติมเกี่ยวกับอินเทอร์เฟซผู้ใช้การค้นหา โปรดอ่านคู่มือ API สร้างอินเทอร์เฟซการค้นหา

ส่วนที่เหลือในบทนี้จะแสดงวิธีอื่นๆ ในการค้นหารายชื่อติดต่อในผู้ให้บริการรายชื่อติดต่อ

จับคู่รายชื่อติดต่อตามข้อมูลบางประเภท

เทคนิคนี้ช่วยให้คุณระบุประเภทข้อมูลที่ต้องการจับคู่ได้ การดึงข้อมูลตามชื่อเป็นตัวอย่างที่เฉพาะเจาะจงของการค้นหาประเภทนี้ แต่คุณยังดึงข้อมูลรายละเอียดประเภทใดก็ได้ที่เชื่อมโยงกับรายชื่อติดต่อ เช่น คุณสามารถเรียกข้อมูลรายชื่อติดต่อที่มีรหัสไปรษณีย์ที่เฉพาะเจาะจงได้ ในกรณีนี้ สตริงการค้นหาต้องตรงกับข้อมูลที่จัดเก็บไว้ในแถวรหัสไปรษณีย์

หากต้องการใช้การดึงข้อมูลประเภทนี้ ให้ติดตั้งใช้งานโค้ดต่อไปนี้ก่อนตามที่ระบุไว้ในส่วนก่อนหน้า

  • ขอสิทธิ์อ่านผู้ให้บริการ
  • กำหนดเลย์เอาต์ ListView และรายการ
  • กำหนด Fregment ที่แสดงรายการรายชื่อติดต่อ
  • กําหนดตัวแปรส่วนกลาง
  • เริ่มต้น Fragment
  • ตั้งค่า CursorAdapter สำหรับ ListView
  • ตั้งค่า Listener ของรายชื่อติดต่อที่เลือก
  • ระบุค่าคงที่สำหรับดัชนีคอลัมน์เคอร์เซอร์

    แม้ว่าคุณจะดึงข้อมูลจากตารางอื่น แต่ลำดับของคอลัมน์ใน การฉายภาพเหมือนกัน คุณจึงใช้ดัชนีเดียวกันสำหรับเคอร์เซอร์ได้

  • กำหนดเมธอด onItemClick()
  • เริ่มต้นตัวโหลด
  • ใช้ onLoadFinished() และ onLoaderReset()

ขั้นตอนต่อไปนี้จะแสดงรหัสเพิ่มเติมที่คุณต้องจับคู่สตริงการค้นหา ข้อมูลรายละเอียดบางประเภทแล้วแสดงผล

เลือกประเภทข้อมูลและตาราง

หากต้องการค้นหาข้อมูลรายละเอียดประเภทใดประเภทหนึ่ง คุณต้องทราบค่าประเภท MIME ที่กําหนดเองสําหรับประเภทข้อมูลนั้น ข้อมูลแต่ละประเภทมีประเภท MIME ที่ไม่ซ้ำกัน ค่าที่กำหนดโดย CONTENT_ITEM_TYPE คงที่ในคลาสย่อยของ ContactsContract.CommonDataKindsที่เชื่อมโยงกับประเภทข้อมูล ซับคลาสจะมีชื่อที่ระบุประเภทข้อมูล เช่น ซับคลาสสําหรับข้อมูลอีเมลคือ ContactsContract.CommonDataKinds.Email และประเภท MIME ที่กําหนดเองสําหรับข้อมูลอีเมลจะกําหนดโดยค่าคงที่ Email.CONTENT_ITEM_TYPE

ใช้ตาราง ContactsContract.Data สำหรับการค้นหา ตารางนี้จะกำหนดหรือรับค่าคงที่ทั้งหมดที่จำเป็นสำหรับการแสดงผล คำสั่งการเลือก และลําดับการจัดเรียง

กำหนดการฉายภาพ

หากต้องการกําหนดโปรเจ็กชัน ให้เลือกคอลัมน์อย่างน้อย 1 คอลัมน์ที่กําหนดไว้ใน 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 Street" ด้วย

หากต้องการใช้การดึงข้อมูลประเภทนี้ ให้ติดตั้งใช้งานโค้ดต่อไปนี้ก่อนตามที่ระบุไว้ในส่วนก่อนหน้า

  • ขอสิทธิ์อ่านผู้ให้บริการ
  • กำหนดเลย์เอาต์ ListView และรายการ
  • กำหนด Fregment ที่แสดงรายการรายชื่อติดต่อ
  • กําหนดตัวแปรส่วนกลาง
  • เริ่มต้น Fragment
  • ตั้งค่า CursorAdapter สำหรับ ListView
  • ตั้งค่า Listener รายชื่อติดต่อที่เลือก
  • กำหนดการฉายภาพ
  • ระบุค่าคงที่สำหรับดัชนีคอลัมน์เคอร์เซอร์

    สำหรับการดึงข้อมูลประเภทนี้ คุณจะใช้ตารางเดียวกับที่ใช้ในส่วนจับคู่รายชื่อติดต่อตามชื่อและแสดงผลลัพธ์ ใช้ดัชนีคอลัมน์เดียวกันด้วย

  • กำหนดเมธอด 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
        );
    }

ข้อมูลโค้ดเหล่านี้เป็นพื้นฐานของแอปที่ทำการค้นหาอย่างกว้างๆ ของผู้ให้บริการ Contacts เทคนิคนี้มีประโยชน์สำหรับแอปที่ต้องการใช้ฟังก์ชันที่คล้ายกับ หน้าจอรายชื่อติดต่อของแอป People