Mengubah ViewPager menjadi manajer fragmen dengan animasi gaya iOS

Banyak pengembang untuk Android menghadapi masalah penerapan animasi dan transisi ketika membuka fragmen baru. Kami diundang untuk menggunakan salah satu dari menambahkan fragmen ke wadah, meletakkannya di atas satu sama lain, atau memutar ulang (mengganti satu fragmen dengan yang lain). Putar ulang memiliki empat jenis animasi:


Hidupkan semuanya terlihat seperti ini:
.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() 

gambar


Masalah dengan replay adalah bahwa a) fragmen sebelumnya dihancurkan, b) tidak ada cara untuk mengatur tindakan untuk menutup fragmen dengan gerakan (misalnya, seperti yang diterapkan di Google Inbox).


Menambahkan fragmen ke tumpukan (add) memungkinkan Anda untuk menggunakan animasi hanya agar fragmen dibuka, bagian belakang tidak akan bergerak.


Dan semua ini, tentu saja, disertai dengan rendering yang buruk dan bingkai yang rusak.


Akibatnya, bahkan aplikasi besar seperti VKontakte atau Instagram tidak menggunakan animasi fragmen sama sekali dalam aplikasi mereka.


Setahun setengah yang lalu, Telegram memperkenalkan Telegram x (versi uji kliennya). Mereka memecahkan masalah ini seperti ini:


gambar


Di sini, animasi dari fragmen depan dan belakang diimplementasikan, serta kemampuan untuk menutup fragmen dengan gerakan.


Saya berhasil melakukan sesuatu yang serupa dan saya ingin berbagi metode saya membuka fragmen:


gambar


Jadi, buat kelas 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) } } } 

Sekarang kami memiliki Navigator kami, yang kami gunakan sebagai wadah untuk semua fragmen di Aktivitas kami:


 <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"/> 

Latar belakang hitam. Ini diperlukan untuk mensimulasikan bayangan pada fragmen yang tertutup. lebih jauh akan lebih jelas.


Sekarang kita membutuhkan adaptor untuk meletakkan fragmen:


 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`) } } 

Segera buat Transformer untuk Navigator kami:


 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 } } } } } 

Sekarang - yang paling menarik! Kami meresepkan tindakan yang diperlukan untuk membuka fragmen di Aktivitas kami:


 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() } } } 

Faktanya, itu saja. Sekarang pada setiap fragmen yang kita sebut metode dari Activiti:


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

Dan ketika menggesek ke kanan, ia sendiri akan dihapus dari tumpukan dengan bantuan pendengar OnPageChangeListener kami.


Metode ini tidak ideal, mungkin beberapa perangkap akan terbuka dalam pengembangan lebih lanjut, tetapi saya belum menemukan masalah kegunaan, mungkin pengembang yang berpengalaman akan memperbaiki saya atau memberi tahu saya sesuatu. Anda dapat melihat cara kerjanya pada contoh nyata di sini .

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


All Articles