من المراوغة إلى Android Motion



على الإنترنت ، هناك العديد من قوالب الرسوم المتحركة المثيرة للاهتمام ذات واجهات المستخدم الجميلة لتطبيقات الهاتف المحمول ، ولكن لا توجد أمثلة كثيرة على تنفيذ هذه الواجهات. على الرغم من وفرة العديد من الأطر والأدوات المدمجة في Android SDK ، إلا أنه ليس من السهل على المطور المبتدئ تنفيذ واجهة مستخدم جميلة ، بما في ذلك قالب جاهز.

في هذه المقالة ، سنحاول تنفيذ واجهة المستخدم التي طورها المصمم Ivan Parfyonov لاستوديو PLATES.


أولاً ، قم بإنشاء جزأين: RecyclerFragment و DetailsFragment .


إطار انتقال Android؟


يعمل إطار Android Transition بشكل جيد ، ولكن هناك بعض الفروق الدقيقة. أولاً ، نريد أن يعمل كل شيء لنا على الأقل على API 19 ، وثانيًا ، نحتاج إلى تحريك العديد من عناصر المستخدم في نفس الوقت وبعضها موجود في جزء واحد فقط. لذلك ، نقوم بتنفيذ الرسوم المتحركة لعنصر الانتقال (انتقال العنصر المشترك) يدويًا باستخدام ViewPropertyAnimator .

كل ذلك بالترتيب


  1. نحسب الإحداثيات النهائية للعنصر المحدد من القائمة (إحداثياته ​​في DerailsFragment ) ، والقائمة هي RecyclerView ؛
  2. نقوم بحفظ الإحداثيات الحالية (الإحداثيات في RecyclerFragment ) وتمريرها إلى DetailsFragment (هذا ضروري للرسوم المتحركة العكسية مع API <21) ؛
  3. إنشاء نسخة من العنصر المحدد من القائمة ؛
  4. نجعل العنصر المحدد غير مرئي (ليس نسخة ، ولكن العنصر نفسه) ؛
  5. أضف النسخة التي تم إنشاؤها في الخطوة 3 إلى التخطيط الجذر للجزء الأصلي ، في حالتنا هذه هي RecyclerFragment ؛
  6. نبدأ الرسوم المتحركة لعناصر الواجهة المتبقية وننقل النسخة التي تم إنشاؤها إلى الإحداثيات النهائية من الخطوة 1 ؛
  7. عندما تنتهي الرسوم المتحركة ، قم بإنشاء معاملة وإظهار DetailsFragment ؛
  8. نبدأ الرسوم المتحركة لعناصر الواجهة في DetailsFragment .

عناصر واجهة المستخدم للرسوم المتحركة


لتحريك Toolbar RecyclerFragment View إضافية في RecyclerFragment خلف الشاشة في الأعلى. سيتم View هذا View المتحرك في حاوية Toolbar في DetailsFragment (اللون الأزرق على gif) باستخدام ViewPropertyAnimator .

 <View android:id="@+id/details_toolbar_helper" android:layout_width="wrap_content" android:layout_height="@dimen/details_toolbar_container_height" android:background="@color/colorPrimary" app:layout_constraintTop_toTopOf="parent"/> // In RecyclerFragment details_toolbar_helper.translationY = -details_toolbar_helper.height 

الصورة

BottomNavigationView تنفيذ الرسوم المتحركة لـ BottomNavigationView و RecyclerView أيضًا باستخدام ViewPropertyAnimator ، لا شيء معقد (تغيير الشفافية والتحرك).

قليلا من إطار الانتقال


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

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

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

ولكن بالنسبة لـ RecyclerView لا يعمل هذا ، لا يتم حذف العنصر المحدد من RecyclerView بعد إضافته إلى طبقة التراكب.

إليك ما يحدث عندما نضيف العنصر المحدد إلى طبقة التراكب:



ونحن بحاجة إلى هذا:



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

 fun View.copyViewImage(): View { val copy = ImageView(context) val bitmap = drawToBitmap() copy.setImageBitmap(bitmap) //  pre-Lollipop   ,   card view  ,      card view return (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { CardView(context).apply { cardElevation = resources.getDimension(R.dimen.card_elevation) radius = resources.getDimension(R.dimen.card_corner_radius) addView(copy) } } else { copy }).apply { layoutParams = this@copyViewImage.layoutParams layoutParams.height = this@copyViewImage.height layoutParams.width = this@copyViewImage.width x = this@copyViewImage.x y = this@copyViewImage.y } } 


لماذا تقوم بإنشاء نسخة إذا كان بإمكانك فقط تحريك العنصر المحدد مباشرة من القائمة؟

لأن RecyclerView نفسها ستكون أيضًا متحركة ، وبالتالي ، جميع عناصرها ، بما في ذلك العنصر المحدد ، الذي نريد تحريكه بشكل منفصل.

بعد ذلك ، أضف نسخة إلى ترميز الجذر وابدأ الرسوم المتحركة.

 override fun onClick(view: View) { val fragmentTransaction = initFragmentTransaction(view) val copy = view.createCopyView() root.addView(copy) view.visibility = View.INVISIBLE startAnimation(copy, fragmentTransaction) } 


وهذا ما حصلنا عليه:



خط النهاية


تظهر الرسوم المتحركة gif أعلاه في RecyclerFragment ، وبعد اكتمالها نحتاج إلى إظهار DetailsFragment .

 .withEndAction { fragmentTransaction?.commitAllowingStateLoss() } 

لماذا نستخدم commitAllowingStateLoss ؟

إذا لم تستخدمه وفي وقت الرسوم المتحركة سيكون هناك ، على سبيل المثال ، تغيير في اتجاه الشاشة ، فسوف نحصل على IllegalStateExeption . هنا هو مكتوب بشكل جيد عن ذلك.

بعد ذلك ، نبدأ الرسوم المتحركة لعناصر واجهة المستخدم الضرورية في DetailsFragment .

قم بتشغيلها معًا




ليست تمامًا مثل النسخة الأصلية ، لكنها تبدو متشابهة.

أمثلة


كود المصدر متاح على GitHub ، والمقالة متاحة أيضًا باللغة الإنجليزية .

شكرا لكم على اهتمامكم!

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


All Articles