يشرح هذا الدرس كيفية استرداد البيانات التفصيلية لجهة اتصال، مثل عناوين البريد الإلكتروني أو الهاتف. والأرقام وهكذا. وهي التفاصيل التي يبحث عنها المستخدمون عند استرداد جهة اتصال. يمكنك منحهم جميع تفاصيل جهة الاتصال، أو عرض تفاصيل من نوع معيّن فقط، مثل عناوين البريد الإلكتروني.
تفترض الخطوات الواردة في هذا الدرس أن لديك بالفعل
صف واحد (ContactsContract.Contacts
) لجهة اتصال اختارها المستخدم.
يوضّح درس استرداد أسماء جهات الاتصال كيفية
استرداد قائمة بجهات الاتصال.
استرداد جميع التفاصيل لجهة اتصال
لاسترداد جميع تفاصيل جهة اتصال، ابحث في جدول
ContactsContract.Data
عن أي صفوف تحتوي على
LOOKUP_KEY
جهة الاتصال. يتوفّر هذا العمود في
جدول ContactsContract.Data
، لأنّ موفِّر 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 الذي تحدّده فئة
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 ";