
在Internet上,有许多有趣的动画模板,这些模板带有精美的移动应用程序用户界面,但是实现这些界面的示例并不多。 尽管Android SDK中包含各种内置的框架和工具,但对于新手开发人员来说,要实现漂亮的用户界面(包括现成的模板)通常并不容易。
在本文中,我们将尝试实现由设计师Ivan Parfyonov为PLATES工作室开发的用户界面。
首先,创建两个片段:
RecyclerFragment
和
DetailsFragment
。
Android Transition框架?
Android Transition框架运作良好,但有一些细微差别。 首先,我们希望一切都至少可以在API 19上为我们工作,其次,我们需要同时对多个用户元素进行动画处理,其中一些仅存在于一个片段中。 因此,我们使用
ViewPropertyAnimator
手动实现过渡元素(共享元素过渡)的
ViewPropertyAnimator
。
全部按顺序
- 我们从列表中计算所选元素的最终坐标(其坐标在
DerailsFragment
),列表为RecyclerView
; - 我们保存当前坐标(
RecyclerFragment
坐标),并将它们传递给DetailsFragment
(这对于API <21的反向动画是必需的); - 从列表中创建所选项目的副本;
- 我们使选定的元素不可见(不是副本,而是元素本身);
- 将在第3步中创建的副本添加到父片段的根布局中,在本例中为
RecyclerFragment
; - 我们开始其余界面元素的动画,并将创建的副本移动到步骤1中的最终坐标;
- 动画结束后,创建一个事务并显示
DetailsFragment
; - 我们开始在
DetailsFragment
中DetailsFragment
界面元素的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"/>
BottomNavigationView
和
RecyclerView
动画
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上获得 ,而本文也可提供
英文版本 。
感谢您的关注!