使用iOS风格的动画将ViewPager变成片段管理器

许多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() 

图片


重播的问题是:a)先前的片段已被破坏,b)无法设置动作以手势关闭片段(例如,在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) } } } 

现在我们有了导航器,我们将其用作活动中所有片段的容器:


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

立即为我们的导航器创建一个Transformer:


 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/zh-CN453166/


All Articles