בשיעור הזה נסביר איך לאחזר נתונים מפורטים של איש קשר, כמו כתובות אימייל, מספרי טלפון וכו'. אלה הפרטים שהמשתמשים מחפשים כשהם מאחזרים איש קשר. אתם יכולים לתת להם את כל הפרטים של איש הקשר, או להציג רק פרטים מסוג מסוים, כמו כתובות אימייל.
השלבים בשיעור הזה מבוססים על ההנחה שכבר יש לכם שורה של ContactsContract.Contacts לאיש קשר שהמשתמש בחר.
בשיעור אחזור שמות של אנשי קשר מוסבר איך לאחזר רשימה של אנשי קשר.
אחזור כל הפרטים של איש קשר
כדי לאחזר את כל הפרטים של איש קשר, מחפשים בטבלה ContactsContract.Data שורות שמכילות את LOOKUP_KEY של איש הקשר. העמודה הזו זמינה בטבלה ContactsContract.Data, כי ספק אנשי הקשר מבצע איחוד משתמע בין הטבלה ContactsContract.Contacts לבין הטבלה ContactsContract.Data. הסבר מפורט יותר על העמודה LOOKUP_KEY מופיע בשיעור אחזור שמות של אנשי קשר.
הערה: אחזור כל הפרטים של איש קשר מפחית את הביצועים של המכשיר, כי הוא צריך לאחזר את כל העמודות בטבלה ContactsContract.Data. לפני שמשתמשים בשיטה הזו, חשוב להביא בחשבון את ההשפעה על הביצועים.
שליחת בקשה להרשאות
כדי לקרוא נתונים מניהול אנשי הקשר, לאפליקציה צריכה להיות ההרשאה READ_CONTACTS.
כדי לבקש את ההרשאה הזו, מוסיפים את רכיב הצאצא הבא של
<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
תמיד צריך לבצע אחזור מ-Contacts Provider (ומכל ספקי התוכן האחרים) בשרשור ברקע. משתמשים במסגרת 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()
מטמיעים את ה-method
onLoadFinished(). מסגרת ה-loader קוראת ל- 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() מופעלת כשמסגרת ה-loader מזהה שהנתונים שעליהם מבוססת התוצאה Cursor השתנו. בשלב הזה, מסירים את כל ההפניות הקיימות אל Cursor על ידי הגדרתן כ-null. אם לא תעשו את זה, ה-framework של ה-loader לא ישמיד את ה-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 ";