ما هو صامت developer.android.com حول RecyclerView؟

مسألة دورة حياة تطبيق Android أو جزء من تطبيق Android مهم للغاية لممارسة Android (مطور Android). لماذا؟ لأن ترتيب تنفيذ عمليات الاسترجاعات لجميع الطرق المتعلقة بحالة دورة الحياة ( onCreate () و onStart () ، وما إلى ذلك) يتم تشفيره بشكل ثابت وسوف يؤدي تطبيقه غير الصحيح إلى عدم قابلية التطبيق للتشغيل. ما علاقة دورة الحياة بها؟ - سيسأل habretchit اليقظة. بعد كل شيء ، يبدو أن العنوان ليس عنه؟ أجيب: هناك شيء مشترك بين دورة حياة النشاط وعمل RecyclerView - هناك أمر صعب لتنفيذ طرق رد الاتصال عند استخدام هذه الأداة ، وبالتالي ، الحاجة إلى تطبيقه بشكل صحيح .

إذا لم يتم ذلك ، يمكن أن تتصرف القوائم بطريقة غامضة للغاية.

محول الأدنى ل RecyclerView


على سبيل المثال. يوجد محول القائمة هذا مع الحد الأدنى القياسي للحشو:

الإدراج 1


public class RvCustomAdapter extends RecyclerView.Adapter<RvCustomAdapter.CustomViewHolder> { private final Frag1 frag; private final LayoutInflater lInflater; private ArrayList<JSONDataSet> dataSet; ... ... ... public RvCustomAdapter(final Frag1 fragment) { this.frag = fragment; this.lInflater = (LayoutInflater) fragment.getContext() .getSystemService(Context.LAYOUT_INFLATER_SERVICE); this.dataSet = new ArrayList<>(); } ... ... ... @Override public CustomViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { //    View view = lInflater.inflate(R.layout.recycler_view_data_item, parent, false); /** *      *  (size, margins, paddings  .) */ RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) view.getLayoutParams(); params.height = RecyclerView.LayoutParams.WRAP_CONTENT; view.setLayoutParams(params); return new CustomViewHolder(view); } //    view ( layout manager-) @Override public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) { holder.showData(position); } @Override public int getItemCount() { return dataSet.size(); } /** *  view holder-      *     *        */ class CustomViewHolder extends RecyclerView.ViewHolder { ... ... ... @BindView(R.id.ll_Data) LinearLayout ll_Data; @BindView(R.id.cb_Data) CheckBox cb_Data; ... ... ... private JSONDataSet cur; CustomViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } /** * ,      *       . */ ... ... ... } 

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

قائمة 2


  void showData(final int position) { cur = dataSet.get(position); cb_Data.setChecked(cur.isChecked()); ... ... ... cb_Data.setOnCheckedChangeListener(cb_DataOnCheckedChangeListener); ll_Data.setOnClickListener(ll_DataOnClickListener); } private OnClickListener ll_DataOnClickListener = new OnClickListener() { @Override public void onClick(View view) { cur.setChecked(!cur.isChecked()); cb_Data.setChecked(cur.isChecked()); } }; private OnCheckedChangeListener cb_DataOnCheckedChangeListener = new OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean checked) { cur.setChecked(checked); compoundButton.setChecked(checked); setItemsColor(checked); if (checked) { if (...) { (frag).addSelectedItemsCounter(cur); } else { cur.setChecked(!checked); compoundButton.setChecked(!checked); setItemsColor(!checked); if (...) { createPrimaryDialog(); } else { createSecondaryDialog(); } } } else { (frag).remSelectedItemsCounter(cur); } } }; 

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

اتضح شيء مثل هذا:


في الشكل 1 - القائمة التي تم إنشاؤها. في الشكل 2 - عنصر القائمة المحدد. في الشكل 3 - حوار لإبلاغه بانتهاك الشرط عند وضع علامة على العنصر التالي.

للحصول على النتيجة باستخدام مدير تخطيط قائمة الشكل 1 ( LayoutManager ) ، يتم تنفيذ الترتيب التالي لاستدعاء الوظائف الضرورية:

خوارزمية 1


  1. Rv_Adapter.getItemCount () - يتحقق من عدد العناصر في المجموعة ؛
  2. Rv_Adapter.onAttachedToRecyclerView () - المحول متصل بالقطعة ؛
  3. حتى يتم ملء مساحة القائمة بعناصر القائمة ، يتم تنفيذ الخطوات التالية من الخوارزمية 2 للقائمة:

الخوارزمية 2


  1. Rv_Adapter.onCreateViewHolder () - لكل عنصر من عناصر المجموعة ، يتم إنشاء معالج خاص به ؛
  2. CustomViewHolder () - يتم تنفيذ مُنشئ المعالج ؛
  3. Rv_Adapter.onBindViewHolder () - لكل مثيل ، يبدأ منشئ العرض ؛
  4. Rv_Adapter.onViewAttachedToWindow () - طريقة العرض التي تم إنشاؤها متصلة بالنافذة ؛

كل شيء رائع! إن لم يكن لـ "لكن". بدلا من ذلك ، ولكن!

المشكلة


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

استكشاف الأخطاء وإصلاحها


والسبب هو أنه عند كتابة رمز المحول ، فإننا لم نأخذ في الاعتبار ترتيب الأداء لوظائف الاتصال المدرجة هنا وهنا عند التمرير. وهو هكذا:

الخوارزمية 3


  1. عند الاختباء في الخارج رؤية كل عنصر من عناصر القائمة للمثيل المرتبط للمعالج ، يتم تنفيذ طريقة Rv_Adapter.onViewDetachedFromWindow () ، والتي تفصل طريقة الإختباء من النافذة ؛
  2. عندما يظهر كل عنصر جديد في القائمة ( itemView ) من خارج نطاق الرؤية ، يتم تنفيذ الخوارزمية 2 لمثيل المعالج المرتبط ؛

لكن هذا ليس كل شيء باستخدام الإعدادات "الافتراضية" لمدير العلامات ، لا يبقى كل عنصر قائمة غير متصل بالنافذة طويلًا في قائمة الانتظار للوصول السريع. بمجرد وجود 2 منهم هناك ، يتم نقلهم من قبل المدير إلى قائمة انتظار الحالات التي تم التخلص منها ، والتي يتم تمييزها باستدعاء طريقة Rv_Adapter.onViewRecycled () لكل عنصر قائمة مستخدم والعكس بالعكس.

لذلك ، تبدو الخوارزمية 3 كما يلي:

الخوارزمية 3 '


 //   :  - true,  - false: bool direction; if(direction){ /** *          *      * (  directDetachedViews) */ Rv_Adapter.onViewDetachedFromWindow(holder); /** *        *   ,  max */ if(directDetachedViews.size() > max) { /** *       (holder) *      *      * (  directRecycleredHolders) */ Rv_Adapter.onViewRecycled(holder); } /** *     * (visiblePos)   ,  */ if(visiblePos < Rv_Adapter.getItemCount()) { /** *       *    (  reverseDetachedViews) *   (itemView),   *   (  visiblePos), */ if(reverseDetachedViews.content(itemView)){ /** *        *  (  reverseRecycleredHolders) *   holder,    *  ,  visiblePos,    */ Rv_Adapter.onCreateViewHolder(itemView) -> { holder = CustomViewHolder(itemView); }; } else { /** *  -       *   (reverseRecycleredHolders) */ holder = reverseRecycleredHolders.getHolder(visiblePos); } /** *        *   */ Rv_Adapter.onBindViewHolder(holder, visiblePos); } else { /** *  -      *     (reverseDetachedViews) */ holder = reverseDetachedViews.getHolder(visiblePos) } //     Rv_Adapter.onViewAttachedToWindow(holder); } else { ... ... ... ... ... } 


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

الخلاصة والتوصية


لتجنب تكرار العمليات في طريقة onBindViewHolder (الحامل ، visualPos) عند التمرير في القائمة حسب عدد المواضع التي تزيد عن الحد الأقصى ، من الضروري:

  1. استكمل عناصر المجموعة بحقل مع علامة على ازدحام العرض المرتبط في قائمة انتظار المعالجات المستخدمة ، على سبيل المثال ، المعاد تدويره ؛
  2. إدراج تعليمات حول كيفية تعيين هذه العلامة في طريقة onViewRecycled (حامل) ، على سبيل المثال .... setRecycled (true) ؛
  3. أدخل في طريقة onBindViewHolder (الحامل ، visualPos) فحصًا لهذه العلامة ، على سبيل المثال إذا (! Handler.cur.isRecycled ()) ... ؛
  4. قم بإدراج تعليمات طريقة onViewAttachedToWindow (حامل) لإزالة هذا العرض ، على سبيل المثال .... setRecycled (false) ؛

على سبيل المثال ، مثل هذا:

قائمة 3


  @Override public void onViewRecycled(@NonNull CustomViewHolder holder) { super.onViewRecycled(holder); holder.cur.setRecycled(true); } @Override public void onBindViewHolder(@NonNull CustomViewHolder holder, int position) { if (!holder.cur.isRecycled()){ ... ... ... } } @Override public void onViewAttachedToWindow(@NonNull CustomViewHolder holder) { super.onViewAttachedToWindow(holder); holder.cur.setRecycled(false); } 

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


All Articles