从Dribbble到Android Motion



在Internet上,有许多有趣的动画模板,这些模板带有精美的移动应用程序用户界面,但是实现这些界面的示例并不多。 尽管Android SDK中包含各种内置的框架和工具,但对于新手开发人员来说,要实现漂亮的用户界面(包括现成的模板)通常并不容易。

在本文中,我们将尝试实现由设计师Ivan Parfyonov为PLATES工作室开发的用户界面。


首先,创建两个片段: RecyclerFragmentDetailsFragment


Android Transition框架?


Android Transition框架运作良好,但有一些细微差别。 首先,我们希望一切都至少可以在API 19上为我们工作,其次,我们需要同时对多个用户元素进行动画处理,其中一些仅存在于一个片段中。 因此,我们使用ViewPropertyAnimator手动实现过渡元素(共享元素过渡)的ViewPropertyAnimator

全部按顺序


  1. 我们从列表中计算所选元素的最终坐标(其坐标在DerailsFragment ),列表为RecyclerView
  2. 我们保存当前坐标( RecyclerFragment坐标),并将它们传递给DetailsFragment (这对于API <21的反向动画是必需的);
  3. 从列表中创建所选项目的副本;
  4. 我们使选定的元素不可见(不是副本,而是元素本身);
  5. 将在第3步中创建的副本添加到父片段的根布局中,在本例中为RecyclerFragment
  6. 我们开始其余界面元素的动画,并将创建的副本移动到步骤1中的最终坐标;
  7. 动画结束后,创建一个事务并显示DetailsFragment ;
  8. 我们开始在DetailsFragmentDetailsFragment界面元素的DetailsFragment

UI元素动画


为了给Toolbar制作动画Toolbar我们将在RecyclerFragment创建一个附加的View ,并将其放置在顶部屏幕的后面。 将使用ViewPropertyAnimator在“详细信息DetailsFragment (在gif中为蓝色)中的“ Toolbar容器中DetailsFragment View进行动画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 

图片

BottomNavigationViewRecyclerView动画BottomNavigationView可以使用ViewPropertyAnimator BottomNavigationView实现,这并不复杂(更改透明度和移动)。

过渡框架的一点点


简而言之,然后当android transition框架开始对过渡元素进行动画处理时,创建该过渡元素的内容的副本(类似于打印屏幕),从该副本制作一个ImageView,然后将该图片添加到叠加层中的额外叠加层被调用的片段并开始动画。

android过渡框架不太适合我们,因为 当过渡元素动画开始时,该片段中的所有其他用户界面元素都将被销毁,因此我们无法为其设置动画。 即 当我们单击RecyclerFragment的列表项以打开DetailsFragment并开始过渡动画时, RecyclerFragment所有其他接口元素RecyclerFragment破坏而没有动画。

为了获得所需的结果,我们将手动创建一个从列表中选择的元素的副本,将其添加到叠加层,然后进行动画处理。 但是这里出现一个小问题, 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启动必要的用户界面元素的DetailsFragment

一起运行




与原始图片不太一样,但是看起来很相似。

例子


源代码可在GitHub上获得 ,而本文也可提供英文版本

感谢您的关注!

Source: https://habr.com/ru/post/zh-CN423235/


All Articles