تحويل ViewPager إلى مدير تجزئة به رسوم متحركة على نمط iOS

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


نعيش كل شيء يبدو مثل هذا:
.beginTransaction() .setCustomAnimations( R.anim.enter_from_left, //   2 R.anim.exit_to_right, //   1 R.anim.enter_from_right, //   1 R.anim.exit_to_left) //   2 .replace(R.id.container, myFragment) .commit() 

صورة


مشكلة الإعادة هي أن أ) تم تدمير الجزء السابق ، ب) لا توجد طريقة لتعيين إجراء لإغلاق الجزء بإيماءة (على سبيل المثال ، كما هو مطبق في Google Inbox).


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


وكل هذا ، بطبيعة الحال ، يرافقه إطارات ضعيفة الأداء ومكسورة.


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


قبل عام ونصف ، قدمت Telegram Telegram x (نسخة تجريبية من عميلها). لقد حلوا هذه المشكلة مثل هذا:


صورة


هنا ، يتم تنفيذ الرسوم المتحركة للشظايا الأمامية والخلفية ، وكذلك القدرة على إغلاق الشظايا بإيماءة.


تمكنت من القيام بشيء مشابه وأرغب في مشاركة طريقة فتح الأجزاء:


صورة


لذلك ، قم بإنشاء فئة NavigatorViewPager:


 class NavigatorViewPager : ViewPager { init { init() } constructor(context: Context) : super(context) constructor(context: Context, attributeSet: AttributeSet) : super(context, attributeSet) override fun canScrollHorizontally(direction: Int): Boolean { return false } //  private fun init() { // PageTransformer       setPageTransformer(false, NavigatorPageTransformer()) //   overScrollMode = View.OVER_SCROLL_NEVER //       , //    setDurationScroll(300) } //      fun setDurationScroll(millis: Int) { try { val viewpager = ViewPager::class.java val scroller = viewpager.getDeclaredField("mScroller") scroller.isAccessible = true scroller.set(this, OwnScroller(context, millis)) } catch (e: Exception) { e.printStackTrace() } } //      DecelerateInterpolator() inner class OwnScroller(context: Context, durationScroll: Int) : Scroller(context, DecelerateInterpolator(1.5f)) { private var durationScrollMillis = 1 init { this.durationScrollMillis = durationScroll } override fun startScroll(startX: Int, startY: Int, dx: Int, dy: Int, duration: Int) { super.startScroll(startX, startY, dx, dy, durationScrollMillis) } } } 

الآن لدينا Navigator ، والذي نستخدمه كحاوية لجميع الأجزاء في نشاطنا:


 <info.yamm.project2.navigator.NavigatorViewPager xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/navigator_view_pager" android:layout_width="match_parent" android:layout_height="match_parent" android:background="@color/black" android:fitsSystemWindows="false" tools:context=".activities.MainActivity"/> 

خلفية مجموعة سوداء. هذا ضروري لمحاكاة الظل على الجزء المغلق. كذلك سيكون أكثر وضوحا.


نحتاج الآن إلى محول نضع فيه الأجزاء:


 class NavigatorAdapter(val fragmentManager: FragmentManager) : FragmentStatePagerAdapter(fragmentManager) { // ArrayList      private val listOfFragments: ArrayList<BaseFragment> = ArrayList() //  fun addFragment(fragment: BaseFragment) { listOfFragments.add(fragment) notifyDataSetChanged() } //   fun removeLastFragment() { listOfFragments.removeAt(listOfFragments.size - 1) notifyDataSetChanged() } //     fun getFragmentsCount(): Int { return listOfFragments.size } override fun getItemPosition(`object`: Any): Int { val index = listOfFragments.indexOf(`object`) //        return if (index == -1) PagerAdapter.POSITION_NONE else index } override fun getItem(position: Int): Fragment? { return listOfFragments[position] } override fun getCount(): Int { return listOfFragments.size } override fun destroyItem(container: ViewGroup, position: Int, `object`: Any) { super.destroyItem(container, position, `object`) } } 

إنشاء محول على الفور لمتصفحنا:


 class NavigatorPageTransformer : ViewPager.PageTransformer { override fun transformPage(view: View, position: Float) { // PageTransformer       view.apply { val pageWidth = width when { //        position <= -1 -> { //   INVISIBLE       //    visibility = View.INVISIBLE } // ,          position > 0 && position <= 1 -> { alpha = 1f visibility = View.VISIBLE translationX = 0f } //         // (    ,     NavigatorViewPager?) position <= 0 -> { alpha = 1.0F - Math.abs(position) / 2 translationX = -pageWidth * position / 1.3F visibility = View.VISIBLE } //    ,     else -> { visibility = View.INVISIBLE } } } } } 

الآن - الأكثر إثارة للاهتمام! نصف الإجراءات اللازمة لفتح الأجزاء في نشاطنا:


 class MainActivity : BaseActivity() { private lateinit var navigatorAdapter: NavigatorAdapter private lateinit var navigatorViewPager: NavigatorViewPager private lateinit var mainFragment: MainFragment override fun onCreate(savedInstanceState: Bundle?) { setTheme(info.yamm.project2.R.style.AppTheme) //   navigatorViewPager = findViewById<NavigatorViewPager>(info.yamm.project2.R.id.navigator_view_pager) //   (    BottomNavigationView   ) mainFragment = MainFragment() //  navigatorAdapter = NavigatorAdapter(supportFragmentManager) //       addFragment(mainFragment) //     navigatorViewPager.adapter = navigatorAdapter //      // :   FragmentStatePagerAdapter,   //     . // FragmentPagerAdapter   ,         // ,    . // ,     INVISIBLE  PageTransformer //     ,        //   .    , //       navigatorViewPager.offscreenPageLimit = 30 var canRemoveFragment: Boolean = false //         var sumPositionAndPositionOffset = 0.0f //   navigatorViewPager.addOnPageChangeListener(object : OnPageChangeListener { //     ,     override fun onPageScrollStateChanged(state: Int) { if (state == 0 && canRemoveFragment) { while ((navigatorAdapter.getFragmentsCount() - 1) > navigatorViewPager.currentItem) { navigatorAdapter.removeLastFragment() } } } //       //        override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) { canRemoveFragment = position + positionOffset < sumPositionAndPositionOffset sumPositionAndPositionOffset = position + positionOffset } override fun onPageSelected(position: Int) { } }) } //             fun addFragment(fragment: BaseFragment) { navigatorAdapter.addFragment(fragment) navigatorViewPager.currentItem = navigatorViewPager.currentItem + 1 } //    "" override fun onBackPressed() { if (navigatorAdapter.getFragmentsCount() > 1) { navigatorViewPager.setCurrentItem(navigatorViewPager.currentItem - 1, true) } else { finish() } } } 

هذا ، في الواقع ، هو كل شيء. الآن في أي جزء نسميه الطريقة من Activiti:


 (activity as MainActivity).addFragment(ConversationFragment()) 

وعند التمرير إلى اليمين ، سيتم إزالته هو نفسه من المكدس بمساعدة مستمع OnPageChangeListener الخاص بنا.


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

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


All Articles