บทเรียนนี้แสดงวิธีเรียกข้อมูลรายละเอียดของรายชื่อติดต่อ เช่น อีเมล หมายเลขโทรศัพท์ ตัวเลข และอื่นๆ ซึ่งเป็นรายละเอียดที่ผู้ใช้ต้องการเมื่อดึงข้อมูลรายชื่อติดต่อ คุณสามารถระบุรายละเอียดทั้งหมดของผู้ติดต่อ หรือแสดงเฉพาะรายละเอียดบางประเภท เช่น อีเมล
ขั้นตอนในบทเรียนนี้ถือว่าคุณมีแถว ContactsContract.Contacts
ของรายชื่อติดต่อที่ผู้ใช้เลือกไว้แล้ว
บทเรียนการเรียกดูชื่อผู้ติดต่อจะแสดงวิธีการ
เรียกดูรายชื่อผู้ติดต่อ
เรียกดูรายละเอียดทั้งหมดของรายชื่อติดต่อ
หากต้องการเรียกดูรายละเอียดทั้งหมดของผู้ติดต่อ ให้ค้นหา
ตาราง ContactsContract.Data
สำหรับแถวใดก็ตามที่มีของ
LOOKUP_KEY
คอลัมน์นี้พร้อมใช้งานในตาราง ContactsContract.Data
เนื่องจากผู้ให้บริการรายชื่อติดต่อทำการรวมข้อมูลโดยนัยระหว่างตาราง ContactsContract.Contacts
กับตาราง ContactsContract.Data
คอลัมน์ LOOKUP_KEY
มีคำอธิบายโดยละเอียดอยู่ในบทเรียนการดึงข้อมูลชื่อผู้ติดต่อ
หมายเหตุ: การดึงข้อมูลรายละเอียดทั้งหมดของรายชื่อติดต่อจะลดประสิทธิภาพของ
เพราะจำเป็นต้องดึงข้อมูลคอลัมน์ทั้งหมดใน
ตาราง ContactsContract.Data
พิจารณาผลกระทบด้านประสิทธิภาพก่อน
คุณใช้เทคนิคนี้
ขอสิทธิ์
หากต้องการอ่านข้อมูลจากผู้ให้บริการรายชื่อติดต่อ แอปของคุณต้องมีสิทธิ์ READ_CONTACTS
หากต้องการขอสิทธิ์นี้ ให้เพิ่มองค์ประกอบย่อยต่อไปนี้ของ
<manifest>
ลงในไฟล์ Manifest
<uses-permission android:name="android.permission.READ_CONTACTS" />
ตั้งค่าการฉายภาพ
อาจมีการใช้เพียงไม่กี่คอลัมน์หรือหลายคอลัมน์ก็ได้ ทั้งนี้ขึ้นอยู่กับประเภทข้อมูลที่อยู่ในแถว นอกจากนี้ ข้อมูลจะอยู่ในคอลัมน์ต่างๆ โดยขึ้นอยู่กับประเภทข้อมูล
คุณต้องเพิ่มชื่อคอลัมน์ทั้งหมดลงในโปรเจ็กชันเพื่อให้แน่ใจว่าคุณได้รับคอลัมน์ทั้งหมดที่เป็นไปได้สำหรับประเภทข้อมูลทั้งหมดที่เป็นไปได้ เรียกข้อมูลเสมอ
Data._ID
หากคุณกำลังเชื่อมโยงผลลัพธ์
Cursor
เป็น ListView
มิฉะนั้น การเชื่อมโยง
ก็ไม่ได้ผล ดึงข้อมูล Data.MIMETYPE
ด้วย
เพื่อให้คุณระบุประเภทข้อมูลของแต่ละแถวที่ดึงข้อมูลได้ เช่น
Kotlin
private val PROJECTION: Array<out String> = arrayOf( ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 )
Java
private static final String[] PROJECTION = { ContactsContract.Data._ID, ContactsContract.Data.MIMETYPE, ContactsContract.Data.DATA1, ContactsContract.Data.DATA2, ContactsContract.Data.DATA3, ContactsContract.Data.DATA4, ContactsContract.Data.DATA5, ContactsContract.Data.DATA6, ContactsContract.Data.DATA7, ContactsContract.Data.DATA8, ContactsContract.Data.DATA9, ContactsContract.Data.DATA10, ContactsContract.Data.DATA11, ContactsContract.Data.DATA12, ContactsContract.Data.DATA13, ContactsContract.Data.DATA14, ContactsContract.Data.DATA15 };
เส้นโครงนี้เรียกคอลัมน์ทั้งหมดของแถวใน
ContactsContract.Data
ตาราง ใช้ชื่อคอลัมน์ที่กำหนดไว้ใน
ชั้นเรียน ContactsContract.Data
นอกจากนี้ คุณยังใช้ค่าคงที่ของคอลัมน์อื่นๆ ที่กำหนดไว้ในหรือรับค่าโดย
ContactsContract.Data
ชั้นเรียน อย่างไรก็ตาม ให้สังเกตว่าคอลัมน์
SYNC1
จนถึง
SYNC4
มีไว้เพื่อใช้โดยการซิงค์
อะแดปเตอร์ ข้อมูลจึงไม่มีประโยชน์
กําหนดเกณฑ์การเลือก
ระบุค่าคงที่สำหรับวลีการเลือก อาร์เรย์เพื่อเก็บอาร์กิวเมนต์การเลือก และ
เพื่อเก็บค่าการเลือก ใช้คอลัมน์ Contacts.LOOKUP_KEY
เพื่อค้นหารายชื่อติดต่อ เช่น
Kotlin
// Defines the selection clause private const val SELECTION: String = "${ContactsContract.Data.LOOKUP_KEY} = ?" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("") /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private var lookupKey: String? = null
Java
// Defines the selection clause private static final String SELECTION = Data.LOOKUP_KEY + " = ?"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" }; /* * Defines a variable to contain the selection value. Once you * have the Cursor from the Contacts table, and you've selected * the desired row, move the row's LOOKUP_KEY value into this * variable. */ private lateinit var lookupKey: String
การใช้ "?" เป็นตัวยึดตําแหน่งในนิพจน์ข้อความการเลือกช่วยให้มั่นใจได้ว่าการค้นหาที่แสดงจะสร้างขึ้นโดยการเชื่อมโยง ไม่ใช่การคอมไพล์ SQL วิธีนี้ช่วยขจัด ความเป็นไปได้ที่จะทำการแทรก SQL ที่เป็นอันตราย
กำหนดลำดับการจัดเรียง
กำหนดลําดับการจัดเรียงที่ต้องการใน Cursor
ที่แสดง หากต้องการจัดเรียงแถวทั้งหมดของประเภทข้อมูลหนึ่งๆ ไว้ด้วยกัน ให้จัดเรียงตาม Data.MIMETYPE
อาร์กิวเมนต์การค้นหานี้จะจัดกลุ่มแถวอีเมลทั้งหมดไว้ด้วยกัน จัดกลุ่มแถวโทรศัพท์ทั้งหมดไว้ด้วยกัน และอื่นๆ เช่น
Kotlin
/* * Defines a string that specifies a sort order of MIME type */ private const val SORT_ORDER = ContactsContract.Data.MIMETYPE
Java
/* * Defines a string that specifies a sort order of MIME type */ private static final String SORT_ORDER = ContactsContract.Data.MIMETYPE;
หมายเหตุ: ข้อมูลบางประเภทไม่ใช้ประเภทย่อย คุณจึงจัดเรียงประเภทย่อยไม่ได้
แต่คุณต้องวนผ่าน Cursor
ที่แสดงผลแทน
ระบุประเภทข้อมูลของแถวปัจจุบัน และจัดเก็บข้อมูลสําหรับแถวที่ใช้ประเภทย่อย เมื่ออ่านเคอร์เซอร์เสร็จแล้ว คุณสามารถจัดเรียงข้อมูลแต่ละประเภทตามประเภทย่อยและแสดงผลลัพธ์
เริ่มต้นเครื่องมือโหลด
ดึงข้อมูลจากผู้ให้บริการรายชื่อติดต่อ (และผู้ให้บริการเนื้อหาอื่นๆ ทั้งหมด) เสมอใน
เทรดพื้นหลัง ใช้เฟรมเวิร์ก Loader ที่กําหนดโดยคลาส LoaderManager
และอินเทอร์เฟซ LoaderManager.LoaderCallbacks
เพื่อดึงข้อมูลในเบื้องหลัง
เมื่อพร้อมจะเรียกข้อมูลแถวแล้ว ให้เริ่มต้นเฟรมเวิร์กของตัวโหลดโดย
กำลังโทรหา initLoader()
ส่งตัวระบุจำนวนเต็มไปยังเมธอด โดยระบบจะส่งตัวระบุนี้ไปยังเมธอด LoaderManager.LoaderCallbacks
ตัวระบุจะช่วยคุณ
ใช้ตัวโหลดหลายตัวในแอปโดยให้คุณแยกความแตกต่างได้
ข้อมูลโค้ดต่อไปนี้แสดงวิธีเริ่มต้นเฟรมเวิร์กโปรแกรมโหลด
Kotlin
// Defines a constant that identifies the loader private const val DETAILS_QUERY_ID: Int = 0 class DetailsFragment : Fragment(), LoaderManager.LoaderCallbacks<Cursor> { ... override fun onCreate(savedInstanceState: Bundle?) { ... // Initializes the loader framework loaderManager.initLoader(DETAILS_QUERY_ID, null, this)
Java
public class DetailsFragment extends Fragment implements LoaderManager.LoaderCallbacks<Cursor> { ... // Defines a constant that identifies the loader static int DETAILS_QUERY_ID = 0; ... @Override public void onCreate(Bundle savedInstanceState) { ... // Initializes the loader framework getLoaderManager().initLoader(DETAILS_QUERY_ID, null, this);
ใช้ onCreateLoader()
ใช้เมธอด onCreateLoader()
ซึ่งเฟรมเวิร์กโหลดเดอร์จะเรียกใช้ทันทีหลังจากที่คุณเรียกใช้ initLoader()
ส่งคืน
CursorLoader
จากวิธีการนี้ เนื่องจากคุณกำลังค้นหา
ตาราง ContactsContract.Data
ให้ใช้ค่าคงที่
Data.CONTENT_URI
เป็น URI เนื้อหา
เช่น
Kotlin
override fun onCreateLoader(loaderId: Int, args: Bundle?): Loader<Cursor> { // Choose the proper action mLoader = when(loaderId) { DETAILS_QUERY_ID -> { // Assigns the selection parameter selectionArgs[0] = lookupKey // Starts the query activity?.let { CursorLoader( it, ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ) } } ... } return mLoader }
Java
@Override public Loader<Cursor> onCreateLoader(int loaderId, Bundle args) { // Choose the proper action switch (loaderId) { case DETAILS_QUERY_ID: // Assigns the selection parameter selectionArgs[0] = lookupKey; // Starts the query CursorLoader mLoader = new CursorLoader( getActivity(), ContactsContract.Data.CONTENT_URI, PROJECTION, SELECTION, selectionArgs, SORT_ORDER ); }
ใช้ onLoadFinished() และ onLoaderReset()
ติดตั้งใช้งาน
onLoadFinished()
การเรียกใช้เฟรมเวิร์กของตัวโหลด
onLoadFinished()
เมื่อผู้ให้บริการรายชื่อติดต่อแสดงผลการค้นหา เช่น
Kotlin
override fun onLoadFinished(loader: Loader<Cursor>, data: Cursor) { when(loader.id) { DETAILS_QUERY_ID -> { /* * Process the resulting Cursor here. */ } ... } }
Java
@Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * Process the resulting Cursor here. */ } break; ... } }
เมธอด onLoaderReset()
จะถูกเรียกใช้เมื่อเฟรมเวิร์กของตัวโหลดตรวจพบว่าข้อมูลที่สนับสนุนผลลัพธ์
Cursor
มีการเปลี่ยนแปลง ในขั้นตอนนี้ ให้นำข้อมูลอ้างอิงที่มีอยู่ออก
เป็น Cursor
โดยตั้งค่าเป็น Null หากไม่ดำเนินการดังกล่าว เฟรมเวิร์กโปรแกรมโหลดจะไม่ทำลาย Cursor
เดิม และคุณจะเกิดการรั่วไหลของหน่วยความจำ เช่น
Kotlin
override fun onLoaderReset(loader: Loader<Cursor>) { when (loader.id) { DETAILS_QUERY_ID -> { /* * If you have current references to the Cursor, * remove them here. */ } ... } }
Java
@Override public void onLoaderReset(Loader<Cursor> loader) { switch (loader.getId()) { case DETAILS_QUERY_ID: /* * If you have current references to the Cursor, * remove them here. */ } break; }
เรียกข้อมูลรายละเอียดที่เฉพาะเจาะจงสำหรับรายชื่อติดต่อ
การดึงข้อมูลประเภทที่เฉพาะเจาะจงสำหรับรายชื่อติดต่อ เช่น อีเมลทั้งหมด จะใช้รูปแบบเดียวกับการดึงข้อมูลทั้งหมด การเปลี่ยนแปลงเพียงอย่างเดียวที่คุณต้องทำกับโค้ดที่ระบุไว้ในดึงข้อมูลรายละเอียดทั้งหมดสำหรับรายชื่อติดต่อมีดังนี้
- การคาดคะเน
-
แก้ไขการฉายภาพเพื่อเรียกคอลัมน์ที่ระบุถึง
ประเภทข้อมูล นอกจากนี้ ให้แก้ไขการฉายภาพเพื่อใช้ค่าคงที่ชื่อคอลัมน์ที่กําหนดไว้ใน
ContactsContract.CommonDataKinds
คลาสย่อยที่สอดคล้องกับประเภทข้อมูล - การเลือก
-
แก้ไขข้อความที่เลือกเพื่อค้นหาค่า
MIMETYPE
ที่เจาะจงสำหรับประเภทข้อมูลของคุณ - ลำดับการจัดเรียง
-
เนื่องจากคุณเลือกรายละเอียดเพียงประเภทเดียวเท่านั้น อย่าจัดกลุ่ม
Cursor
ที่แสดงผลตามData.MIMETYPE
คำอธิบายเกี่ยวกับการแก้ไขดังกล่าวอยู่ในส่วนต่อไปนี้
กำหนดการฉายภาพ
กำหนดคอลัมน์ที่คุณต้องการดึงข้อมูลโดยใช้ค่าคงที่ของชื่อคอลัมน์ในคลาสย่อย
ของ ContactsContract.CommonDataKinds
สำหรับประเภทข้อมูล
หากคุณวางแผนที่จะเชื่อมโยง Cursor
กับ ListView
อย่าลืมเรียกคอลัมน์ _ID
เช่น หากต้องการเรียกข้อมูลอีเมล ให้กำหนดฟิลด์
การฉายภาพต่อไปนี้
Kotlin
private val PROJECTION: Array<String> = arrayOf( ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL )
Java
private static final String[] PROJECTION = { ContactsContract.CommonDataKinds.Email._ID, ContactsContract.CommonDataKinds.Email.ADDRESS, ContactsContract.CommonDataKinds.Email.TYPE, ContactsContract.CommonDataKinds.Email.LABEL };
โปรดทราบว่าการฉายภาพนี้ใช้ชื่อคอลัมน์ที่กําหนดไว้ในคลาส ContactsContract.CommonDataKinds.Email
แทนชื่อคอลัมน์ที่กําหนดไว้ในคลาส ContactsContract.Data
การใช้ชื่อคอลัมน์เฉพาะอีเมลจะช่วยให้โค้ดอ่านง่ายขึ้น
ในการคาดการณ์ คุณยังสามารถใช้คอลัมน์อื่นๆ ที่กำหนดไว้ในฟิลด์
คลาสย่อย ContactsContract.CommonDataKinds
กําหนดเกณฑ์การเลือก
กำหนดนิพจน์ข้อความค้นหาที่จะเรียกข้อมูลแถวสำหรับรายชื่อติดต่อที่ระบุ
LOOKUP_KEY
และ
Data.MIMETYPE
ของรายละเอียดที่คุณ
ต้องการ ใส่ค่า MIMETYPE
ไว้ใน
เครื่องหมายคำพูดเดี่ยวโดยการเชื่อม "'
" (เครื่องหมายอัญประกาศเดี่ยว) เป็นอักขระเริ่มต้นและสิ้นสุด
ของค่าคงที่นี้ ไม่เช่นนั้น ผู้ให้บริการจะตีความค่าคงที่เป็นชื่อตัวแปร
มากกว่าค่าสตริง คุณไม่จำเป็นต้องใช้ตัวยึดตำแหน่งสำหรับค่านี้ เนื่องจากคุณจะ
ใช้ค่าคงที่แทนมูลค่าที่ผู้ใช้ระบุ เช่น
Kotlin
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private const val SELECTION = "${ContactsContract.Data.LOOKUP_KEY} = ? AND " + "${ContactsContract.Data.MIMETYPE} = '${Email.CONTENT_ITEM_TYPE}'" ... // Defines the array to hold the search criteria private val selectionArgs: Array<String> = arrayOf("")
Java
/* * Defines the selection clause. Search for a lookup key * and the Email MIME type */ private static final String SELECTION = Data.LOOKUP_KEY + " = ?" + " AND " + Data.MIMETYPE + " = " + "'" + Email.CONTENT_ITEM_TYPE + "'"; // Defines the array to hold the search criteria private String[] selectionArgs = { "" };
กำหนดลําดับการจัดเรียง
กำหนดลำดับการจัดเรียงสำหรับ Cursor
ที่แสดงผล เนื่องจากคุณเรียก
ประเภทข้อมูลที่เจาะจง ละเว้นการจัดเรียงใน MIMETYPE
หากประเภทข้อมูลรายละเอียดที่คุณค้นหามีประเภทย่อย ก็ให้จัดเรียงข้อมูลนั้นแทน
เช่น สำหรับข้อมูลอีเมล คุณสามารถจัดเรียงตาม
Email.TYPE
Kotlin
private const val SORT_ORDER: String = "${Email.TYPE} ASC"
Java
private static final String SORT_ORDER = Email.TYPE + " ASC ";