تطوير Android. قليلا عن العمل السريع مع القوائم

مرحبا بالجميع! مشاركاتي هي رغبة في المساعدة في بعض عناصر Android. إذا كنت مطورًا ولم تقم بعد بتشكيل خوارزمية لإنشاء قوائم لنفسك ، فقد تجد أنه من المفيد قراءة هذه المادة. في الأساس ، أود أن أقدم حلولًا جاهزة للتطوير ، تكشف في سياق القصة عن بعض الأفكار حول كيفية وصولهم إليهم .

في هذه المقالة:

  • نقوم بتشكيل العديد من الفئات والواجهات الأساسية للعمل مع RecyclerView و RecyclerView.Adapter
  • توصيل مكتبة واحدة من Android Jetpack (اختياري ، أولاً بدونها)
  • لتطوير أسرع - خيار القالب في نهاية المقالة ؛)

الدخول


حسنا اذن! لقد نسي الجميع بالفعل عن ListView ويكتبون بأمان على RecyclerView ( Rv ). تلك الأوقات عندما طبقنا نمط ViewHolder بأنفسنا غرقت في طي النسيان. توفر لنا Rv مجموعة من الفصول الجاهزة لتنفيذ القوائم ومجموعة كبيرة إلى حد ما من LayoutManagers لعرضها. في الواقع ، بالنظر إلى العديد من الشاشات ، يمكنك تخيل معظمها كقائمة - على وجه التحديد نظرًا لقدرة كل عنصر على تنفيذ ViewHolder الخاص به. لقد قيل لنا قصة التطوير بمزيد من التفصيل على Google I / O.

ولكن ، هناك دائمًا بضع "استثناءات" .. تقترح الإجابات القياسية لـ Stackoverflow حلولًا عامة تؤدي إلى نسخ اللصق ، خاصة في المكان الذي يتم فيه تنفيذ المحول.

في الوقت الحالي ، يبلغ Rv بالفعل ثلاث سنوات. هناك الكثير من المعلومات حولها ، وهناك الكثير من المكتبات التي لديها حلول جاهزة ، ولكن ماذا لو كنت لا تحتاج إلى جميع الوظائف ، أو إذا كنت تتسلق للنظر إلى رمز شخص آخر - وترى هناك أن الرعب القديم ليس ما تريد رؤيته ، أو ليس ما تخيلت؟ على مدار هذه السنوات الثلاث ، وافق Android أخيرًا رسميًا على Kotlin = تحسنت قراءة الرمز ، وتم نشر الكثير من المقالات المثيرة للاهتمام على Rv والتي تكشف تمامًا عن قدراته.

الغرض من ذلك هو جمع الأساس من أفضل ممارسات دراجتك ، إطار عمل للعمل مع قوائم للتطبيقات الجديدة. يمكن استكمال هذا الإطار بمنطق من التطبيق إلى التطبيق ، وذلك باستخدام ما تحتاجه وتجاهل غير ضروري. أعتقد أن هذا النهج أفضل بكثير من مكتبة شخص آخر - لديك في فصولك الفرصة لفهم كيفية عمل كل شيء والتحكم في الحالات التي تحتاجها دون الارتباط بقرار شخص آخر.

دعونا نفكر منطقيا ومن البداية


سيتم تحديد ما يجب أن يفعله المكون من خلال الواجهة ، وليس الفئة ، ولكن في النهاية سنغلق منطق التنفيذ الملموس على الفصل الذي سينفذ وينفذ هذه الواجهة. ولكن ، إذا اتضح أنه مع تطبيق الواجهة ، يتم إنشاء لصق نسخ - يمكننا إخفاؤها خلف فئة مجردة ، وبعدها - فئة ترث من الملخص. سأعرض تنفيذي للواجهات الأساسية ، ولكن هدفي هو أن يحاول المطور التفكير في نفس الاتجاه. مرة أخرى - الخطة هي: مجموعة من الواجهات -> فئة مجردة تأخذ لصق النسخ (إذا لزم الأمر) -> وفئة معينة بالفعل مع رمز فريد . يمكنك تنفيذ واجهات مختلفة.

ما الذي يمكن أن يفعله المحول بالقائمة؟ من الأسهل الحصول على إجابة هذا السؤال عند إلقاء نظرة على بعض الأمثلة. يمكنك إلقاء نظرة على RecyclerView.Adapter ، ستجد بعض النصائح. إذا كنت تفكر قليلاً ، يمكنك أن تتخيل شيئًا مثل هذه الطرق:

IBaseListAdapter
interface IBaseListAdapter<T> { fun add(newItem: T) fun add(newItems: ArrayList<T>?) fun addAtPosition(pos : Int, newItem : T) fun remove(position: Int) fun clearAll() } 

* خلال تنفيذ المشاريع ، وجدت عدة طرق أخرى سأحذفها هنا ، على سبيل المثال getItemByPos (الوظيفة: Int) ، أو حتى القائمة الفرعية (startIndex: Int ، endIndex: Int). أكرر: يجب أن تشاهد بنفسك ما تحتاجه من المشروع وتضمين الوظائف في الواجهة. ليس من الصعب عندما تعلم أن كل شيء يحدث في فصل واحد. سيسمح لك الزهد في هذه المسألة بالتخلص من المنطق غير الضروري ، مما يقلل من إمكانية قراءة الشفرة ، لأن تنفيذ معين يأخذ المزيد من الخطوط.

انتبه إلى عام T. بشكل عام ، يعمل المحول مع أي كائن في القائمة (العنصر) ، لذلك لا يوجد توضيح هنا - لم نختار منهجنا بعد. وفي هذه المقالة سيكون هناك اثنان على الأقل ، تبدو الواجهة الأولى كما يلي:

 interface IBaseListItem { fun getLayoutId(): Int } 

حسنًا ، نعم ، يبدو الأمر منطقيًا - نحن نتحدث عن عنصر قائمة ، لذلك يجب أن يكون لكل عنصر نوعًا من التخطيط ، ويمكنك الرجوع إليه باستخدام layoutId. على الأرجح ، لن يحتاج المطور المبتدئ إلى أي شيء على الأرجح ، ما لم نتبع بالطبع مناهج أكثر تقدمًا . إذا كانت لديك خبرة كافية في مجال التنمية ، يمكنك بالتأكيد إنشاء مندوب أو غلاف ، ولكن هل يستحق ذلك مع مشروع صغير - وحتى تجربة تطوير أقل؟ جميع روابطي في مكان ما على YouTube مفيدة جدًا إذا لم يكن لديك وقت الآن - فقط تذكرها واقرأها ، لأن المنهج أبسط هنا - أعتقد أنه مع العمل القياسي مع Rv ، بناءً على الوثائق الرسمية ، ما هو معروض أعلاه ليس ضمني.

حان الوقت لدمج IBaseListAdapter مع الواجهات ، وستكون الفئة التالية مجردة:

SimpleListAdapter
 abstract class SimpleListAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>(), IBaseListAdapter<IBaseListItem> { protected val items: ArrayList<IBaseListItem> = ArrayList() override fun getItemCount() = items.size override fun getItemViewType(position: Int) = items[position].layoutId protected fun inflateByViewType(context: Context?, viewType: Int, parent: ViewGroup) = LayoutInflater.from(context).inflate(viewType, parent, false) override fun add(newItem: IBaseListItem) { items.add(newItem) notifyDataSetChanged() } override fun add(newItems: ArrayList<IBaseListItem>?) { for (newItem in newItems ?: return) { items.add(newItem) notifyDataSetChanged() } } override fun addAtPosition(pos: Int, newItem: IBaseListItem) { items.add(pos, newItem) notifyDataSetChanged() } override fun clearAll() { items.clear() notifyDataSetChanged() } override fun remove(position: Int) { items.removeAt(position) notifyDataSetChanged() } } 

* ملاحظة: انتبه إلى وظيفة getItemViewType (الموضع: Int) الملغاة . نحن بحاجة إلى نوع من المفتاح int ، والذي سيفهم به Rv أي ViewHolder يناسبنا. Val layoutId لبندنا مفيد جدًا لهذا ، مثل يعمل نظام Android في كل مرة على مساعدة معرف التنسيقات بشكل فريد ، وكل القيم أكبر من الصفر - سنستخدم هذا لاحقًا " لتضخيم " itemView لمشاهدينا في طريقة inflateByViewType () (السطر التالي).

قم بإنشاء قائمة


خذ على سبيل المثال شاشة الإعدادات. يقدم لنا Android نسخته الخاصة ، ولكن ماذا لو كان التصميم يحتاج إلى شيء أكثر تعقيدًا؟ أفضل ملء هذه الشاشة كقائمة. هنا سوف تعطى مثل هذه الحالة:



نرى عنصرين مختلفين في القائمة ، لذلك فإن SimpleListAdapter و Rv مثاليان هنا!

دعنا نبدأ! يمكنك البدء بتخطيطات التخطيط لـ item'ov:

item_info.xml ؛ item_switch.xml
 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="56dp"> <TextView android:id="@+id/tv_info_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="28dp" android:textColor="@color/black" android:textSize="20sp" tools:text="Balance" /> <TextView android:id="@+id/tv_info_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|end" android:layout_marginEnd="48dp" tools:text="1000 $" /> </FrameLayout> <!----> <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="56dp"> <TextView android:id="@+id/tv_switch_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="28dp" android:textColor="@color/black" android:textSize="20sp" tools:text="Send notifications" /> <Switch android:id="@+id/tv_switch_value" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|end" android:layout_marginEnd="48dp" tools:checked="true" /> </FrameLayout> 

بعد ذلك ، نحدد الفئات نفسها ، التي نريد أن نمررها داخل القيم التي تتفاعل مع القائمة: الأول هو الرأس وبعض القيم التي جاءت من الخارج (سيكون لدينا كعب ، حول الطلبات في وقت آخر) ، والثاني هو الرأس والمتغير المنطقي التي من خلالها يجب علينا القيام بعمل. للتمييز بين عناصر التبديل ، يكون معرف الكيانات من الخادم مناسبًا ، إذا لم تكن موجودة ، فيمكننا إنشائها بأنفسنا أثناء التهيئة.

InfoItem.kt و SwitchItem.kt
 class InfoItem(val title: String, val value: String): IBaseListItem { override val layoutId = R.layout.item_info } class SwitchItem( val id: Int, val title: String, val actionOnReceive: (itemId: Int, userChoice: Boolean) -> Unit ) : IBaseListItem { override val layoutId = R.layout.item_switch } 

في تطبيق بسيط ، سيحتاج كل عنصر أيضًا إلى ViewHolder:

InfoViewHolder.kt و SwitchViewHolder.kt
 class InfoViewHolder.kt(view: View) : RecyclerView.ViewHolder(view) { val tvTitle = view.tv_info_title val tvValue = view.tv_info_value } class SwitchViewHolder.kt(view: View) : RecyclerView.ViewHolder(view) { val tvTitle = view.tv_switch_title val tvValue = view.tv_switch_value } 

حسنًا ، الجزء الأكثر إثارة للاهتمام هو التنفيذ الملموس لـ SimpleListAdapter'a:

SettingsListAdapter.kt
 class SettingsListAdapter : SimpleListAdapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { val context = parent.context return when (viewType) { R.layout.item_info -> InfoHolder(inflateByViewType(context, viewType, parent)) R.layout.item_switch -> SwitchHolder(inflateByViewType(context, viewType, parent)) else -> throw IllegalStateException("There is no match with current layoutId") } } override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { when (holder) { is InfoHolder -> { val infoItem = items[position] as InfoItem holder.tvTitle.text = infoItem.title holder.tvValue.text = infoItem.value } is SwitchHolder -> { val switchItem = items[position] as SwitchItem holder.tvTitle.text = switchItem.title holder.tvValue.setOnCheckedChangeListener { _, isChecked -> switchItem.actionOnReceive.invoke(switchItem.id, isChecked) } } else -> throw IllegalStateException("There is no match with current holder instance") } } } 

* ملاحظة: لا تنس أنه تحت غطاء طريقة inflateByViewType (السياق ، viewType ، الأصل): viewType = layoutId.

جميع المكونات جاهزة! الآن ، يبقى رمز النشاط ويمكنك تشغيل البرنامج:

Activity_settings.xml
 <?xml version="1.0" encoding="utf-8"?> <FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <android.support.v7.widget.RecyclerView android:id="@+id/rView" android:layout_width="match_parent" android:layout_height="match_parent" /> </FrameLayout> 

SettingsActivity.kt
 class SettingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) val adapter = SettingsListAdapter() rView.layoutManager = LinearLayoutManager(this) rView.adapter = adapter adapter.add(InfoItem("User Name", "Leo Allford")) adapter.add(InfoItem("Balance", "350 $")) adapter.add(InfoItem("Tariff", "Business")) adapter.add(SwitchItem(1, "Send Notifications") { itemId, userChoice -> onCheck(itemId, userChoice) }) adapter.add(SwitchItem(2, "Send News on Email") { itemId, userChoice -> onCheck(itemId, userChoice) }) } private fun onCheck(itemId: Int, userChoice: Boolean) { when (itemId) { 1 -> Toast.makeText(this, "Notification now set as $userChoice", Toast.LENGTH_SHORT).show() 2 -> Toast.makeText(this, "Send news now set as $userChoice", Toast.LENGTH_SHORT).show() } } } 

ونتيجة لذلك ، عند إنشاء القائمة ، فإن كل العمل يأتي على النحو التالي:

1. حساب عدد التخطيطات المختلفة للعناصر

2. اختيار أسمائهم . استخدم القاعدة: شيء Item.kt ، item_ شيء. xml ، شيء ViewHolder.kt

3. نكتب محول لهذه الفئات. من حيث المبدأ ، إذا كنت لا تتظاهر بالتحسين ، فعندئذ يكفي محول واحد مشترك. ولكن في المشاريع الكبيرة ، ما زلت أفعل القليل على الشاشات ، لأنه في الحالة الأولى تنمو طريقة onBindViewHolder () حتمًا (تتأثر قابلية قراءة التعليمات البرمجية) في المحول الخاص بك (في حالتنا ، فهي SettingsListAdapter ) + سيتعين على البرنامج الانتقال في كل مرة ، لكل عنصر ، تعمل على هذه الطريقة + طريقة onCreateViewHolder ()

4. قم بتشغيل الكود واستمتع!

Jetpack


حتى تلك اللحظة ، استخدمنا نهج ربط البيانات القياسي من Item.kt إلى item_layout.xml . ولكن يمكننا توحيد طريقة onBindViewHolder () ، وتركها في الحد الأدنى ، ونقل المنطق إلى العنصر والتخطيط.

دعنا نذهب إلى صفحة Android JetPack الرسمية:



دعونا ننتبه إلى علامة التبويب الأولى في قسم الهندسة المعمارية. يعد Android Databinding موضوعًا واسعًا جدًا ، وأود أن أتحدث عنه بمزيد من التفصيل في مقالات أخرى ، ولكن في الوقت الحالي سنستخدمه فقط كجزء من المقالة الحالية - سنجعل Item.kt - متغيرًا لـ item.xml (أو يمكنك تسميته نموذج عرض للتخطيط).

في وقت كتابة هذا التقرير ، كان من الممكن ربط Databinding على النحو التالي:

 android { compileSdkVersion 27 defaultConfig {...} buildTypes {...} dataBinding { enabled = true } dependencies { kapt "com.android.databinding:compiler:3.1.3" //... } } 

دعنا نذهب من خلال الطبقات الأساسية مرة أخرى. تكمل واجهة العنصر العنصر السابق:

 interface IBaseItemVm: IBaseListItem { val brVariableId: Int } 

أيضا ، سوف نقوم بتوسيع ViewHolder ، وبالتالي ، يتم الاتصال بنا باستخدام databyding. سنمر ViewDataBinding إليها ، وبعد ذلك سوف ننسى بأمان إنشاء تخطيط وربط البيانات

 class VmViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root) 

يتم استخدام نفس النهج هنا ، ولكن في Kotlin يبدو أقصر بكثير ، أليس كذلك؟ =)

VmListAdapter
 class VmListAdapter : RecyclerView.Adapter<VmViewHolder>(), IBaseListAdapter<IBaseItemVm> { private var mItems = ArrayList<IBaseItemVm>() override fun getItemCount() = mItems.size override fun getItemViewType(position: Int) = mItems[position].layoutId override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VmViewHolder { val inflater = LayoutInflater.from(parent.context) val viewDataBinding = DataBindingUtil.inflate<ViewDataBinding>(inflater!!, viewType, parent, false) return VmViewHolder(viewDataBinding) } override fun onBindViewHolder(holder: VmViewHolder, position: Int) { holder.binding.setVariable(mItems[position].brVariableId, mItems[position]) holder.binding.executePendingBindings() } override fun add(newItem: IBaseItemVm) { mItems.add(newItem) notifyItemInserted(mItems.lastIndex) } override fun add(newItems: ArrayList<IBaseItemVm>?) { val oldSize = mItems.size mItems.addAll(newItems!!) notifyItemRangeInserted(oldSize, newItems.size) } override fun clearAll() { mItems.clear() notifyDataSetChanged() } override fun getItemId(position: Int): Long { val pos = mItems.size - position return super.getItemId(pos) } override fun addAtPosition(pos: Int, newItem: IBaseItemVm) { mItems.add(pos, newItem) notifyItemInserted(pos) } override fun remove(position: Int) { mItems.removeAt(position) notifyItemRemoved(position) } } 

انتبه بشكل عام إلى الأساليب onCreateViewHolder () و onBindViewHolder () . الفكرة هي أنها لم تعد تنمو. المجموع ، تحصل على محول واحد لأي شاشة ، مع أي عناصر قائمة.

البنود لدينا:

InfoItem.kt و SwitchItem.kt
 class InfoItem(val title: String, val value: String) : IBaseItemVm { override val brVariableId = BR.vmInfo override val layoutId = R.layout.item_info } // class SwitchItem( val id: Int, val title: String, private val actionOnReceive: (itemId: Int, userChoice: Boolean) -> Unit ) : IBaseItemVm { override val brVariableId = BR.vmSwitch override val layoutId = R.layout.item_switch val listener = CompoundButton.OnCheckedChangeListener { _, isChecked -> actionOnReceive.invoke(id, isChecked) } } 

هنا يمكنك أن ترى أين ذهب منطق طريقة onBindViewHolder () . أخذ Android Databinding على عاتقه - الآن يتم دعم أي من تصميمنا بنموذج العرض الخاص به ، وسوف يعالج بهدوء كل منطق النقرات والرسوم المتحركة والاستعلامات وأشياء أخرى. ماذا جئت. ستقوم محولات الربط بعمل جيد في هذا الأمر - مما يتيح لك ربط العرض بالبيانات من أي نوع. أيضا ، يمكن تحسين الاتصال بفضل databyding ثنائي الاتجاه . ربما سيومض في أي من المقالات التالية ، في هذا المثال يمكن جعل كل شيء أبسط. محول محول واحد يكفي بالنسبة لنا:

 @BindingAdapter("switchListener") fun setSwitchListener(sw: Switch, listener: CompoundButton.OnCheckedChangeListener) { sw.setOnCheckedChangeListener(listener) } 

بعد ذلك ، نربط قيمنا المتغيرة بعنصرنا داخل xml:

item_info.xml ؛ item_switch.xml
 <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="com.lfkekpoint.adapters.adapters.presentation.modules.bindableItemsSettings.InfoItem" /> <variable name="vmInfo" type="InfoItem" /> </data> <FrameLayout android:layout_width="match_parent" android:layout_height="56dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="28dp" android:text="@{vmInfo.title}" android:textColor="@color/black" android:textSize="20sp" tools:text="Balance" /> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|end" android:layout_marginEnd="48dp" android:text="@{vmInfo.value}" tools:text="1000 $" /> </FrameLayout> </layout> <!----> <?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <import type="com.lfkekpoint.adapters.adapters.presentation.modules.bindableItemsSettings.SwitchItem" /> <variable name="vmSwitch" type="SwitchItem" /> </data> <FrameLayout android:layout_width="match_parent" android:layout_height="56dp"> <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginStart="28dp" android:text="@{vmSwitch.title}" android:textColor="@color/black" android:textSize="20sp" tools:text="Send notifications" /> <Switch android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical|end" android:layout_marginEnd="48dp" app:switchListener="@{vmSwitch.listener}" tools:checked="true" /> </FrameLayout> </layout> 

التطبيق: switchListener = "@ {vmSwitch.listener}" - في هذا السطر استخدمنا BindingAdapter


* ملاحظة: لأسباب معقولة ، قد يبدو للبعض أننا نكتب المزيد من التعليمات البرمجية في XML - ولكن هذه مسألة معرفة مكتبة Android Databinding. إنه يكمل التخطيط ، ويقرأ بسرعة ، ومن حيث المبدأ ، يزيل الجزء الأكبر بالضبط من الصفيحة المرجانية. أعتقد أن Google ستطور هذه المكتبة جيدًا ، لأنها الأولى في علامة التبويب الهندسة المعمارية في Android Jetpack. حاول تغيير MVP إلى MVVM في مشروعين - وقد يفاجأ الكثيرون.

حسنًا إذن! .. آه ، الرمز في SettingsActivity:

SettingsActivity.kt
... لم يتغير إلا إذا تغير المحول! =) ولكن لكي لا تقفز فوق المقالة:

 class SettingsActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_settings) val adapter = BaseVmListAdapter() rView.layoutManager = LinearLayoutManager(this) rView.adapter = adapter adapter.add(InfoItem("User Name", "Leo Allford")) adapter.add(InfoItem("Balance", "350 $")) adapter.add(InfoItem("Tariff", "Business")) adapter.add(SwitchItem(1, "Send Notifications") { itemId, userChoice -> onCheck(itemId, userChoice) }) adapter.add(SwitchItem(2, "Send News on Email") { itemId, userChoice -> onCheck(itemId, userChoice) }) } private fun onCheck(itemId: Int, userChoice: Boolean) { when (itemId) { 1 -> Toast.makeText(this, "Notification now set as $userChoice", Toast.LENGTH_SHORT).show() 2 -> Toast.makeText(this, "Send news now set as $userChoice", Toast.LENGTH_SHORT).show() } } } 

الملخص


لقد حصلنا على خوارزمية لبناء قوائم وأدوات للعمل معهم. في حالتي (غالبًا ما أستخدم Databinding دائمًا) ، يأتي كل التحضير لتهيئة الفئات الأساسية في المجلدات ، وتخطيط العناصر في .xml ثم الربط بالمتغيرات في .kt.

تسريع التنمية
للعمل بشكل أسرع ، استخدمت النماذج من Apache لـ Android Studio - وكتبت نماذجي مع عرض صغير لكيفية عمل كل شيء. آمل حقًا أن يكون شخص ما في متناول اليد. يرجى ملاحظة أنه عند العمل ، تحتاج إلى استدعاء القالب من مجلد جذر المشروع - يتم ذلك لأن معلمة applicationId للمشروع يمكن أن تقع عليك إذا قمت بتغييرها في Gradle. لكن لا يمكن خداع packageName بهذه السهولة ، وهو ما استخدمته. يمكن قراءة اللغة المتاحة حول القالب على الروابط أدناه

المراجع / الإعلام


1. تطوير Android الحديث: Android Jetpack و Kotlin والمزيد (Google I / O 2018 ، 40 م.) - دليل موجز لما هو عليه اليوم ، سيتضح من هنا أيضًا بشكل عام كيف تم تطوير RecyclerView ؛

2. Droidcon NYC 2016 - Radical RecyclerView ، 36 م. - تقرير مفصل عن RecyclerView من ليزا Wray ؛

3. إنشاء قائمة باستخدام RecyclerView - الوثائق الرسمية

4. واجهات مقابل الطبقات

5. Android IDE Format Template ، Total Templating ، FreeMarker manual - نهج ملائم ، والذي سيساعد في إطار هذه المقالة على إنشاء الملفات الضرورية بسرعة للعمل مع القوائم

6. كود للمقال (هناك أسماء فئات مختلفة قليلا ، كن حذرا) ، قوالب للعمل والفيديو ، وكيفية العمل مع القوالب

7. النسخة الإنجليزية من المقال

Source: https://habr.com/ru/post/ar424129/


All Articles