「这是我参与2022首次更文挑战的第18天,活动详情查看:2022首次更文挑战」

之前的页面太死板,浏览器肯定要支持多页面,本篇文章写一个TransitionManagelementser过渡动画的使用 提升用户体验感

elementui果图

多页面的过度动画

新建一个WebTabManagerFragment类放入RecyclerViewhttp协议 WebTabManageelement是什么意思rFragment.xml

<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto">
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#FF242424">
        <FrameLayout
            android:id="@+id/list_container"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:clipToPadding="false"
            app:layout_behavior="@string/appbar_scrolling_view_behavior"
            app:layout_constraintTop_toTopOf="parent" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

动态创建一个RecyclerView

GridLayout的RecyclerView

private val ALBUM_RECYCLER_VIEW_ID = ViewCompat.generateViewId()
private fun createRecyclerView(listTypeGrid: Boolean): RecyclerView {
    val context = requireContext()
    //动态创建
    val recyclerView = RecyclerView(context)
    recyclerView.id = ALBUM_RECYCLER_VIEW_ID
    recyclerView.layoutParams =
        FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.MATCH_PARENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
        )
    val verticalPadding = 8
    recyclerView.setPadding(0, verticalPadding, 0, verticalPadding)
    recyclerView.clipToPadding = false
    recyclerView.layoutManager = GridLayoutManager(context, GRID_SPAN_COUNT)
    return recyclerView
}

在ReappreciatecyclerView的Aelement酒店dapter写个点击接口用来传条目的点击事件


interface Listener {
    fun onClicked(view: View, data: HomeTabEntity)
}

在点击事件下把点击的View传出来

这个view要用来当过度动画的开始标记

container.setOnClickListener {
    listener.onClicked(
        container,
        data
    )
}

实现接口的抽象方法

override fun onClicked(view: View, data: HomeTabEntity) {
}

使过渡来动画应appointment用在RecyclerViews 的移除和添加


val recyclerView = createRecyclerView(listTypeGrid)
// 恢复 RecyclerView 的滚动位置)
if (listState != null) {
    recyclerView.layoutManager!!.onRestoreInstanceState(listState)
    listState = null
}
transition.addTarget(recyclerView)
val currentRecyclerView = listContainer.getChildAt(0)
if (currentRecyclerView != null) {
    transition.addTarget(currentRecyclerView)
}
TransitionManager.beginDelayedTransition(listContainer, transition)

插入7条假数据

val albums: List<HomeTabEntity?> = ArrayList<HomeTabEntity>(
    Arrays.asList(
        HomeTabEntity(1L, "baidu1", "time"),
        HomeTabEntity(2L, "baidu2", "time"),
        HomeTabEntity(3L, "baidu3", "time"),
        HomeTabEntity(4L, "baidu4", "time"),
        HomeTabEntity(5L, "baidu5", "time"),
        HomeTabEntity(6L, "baidu6", "time"),
        HomeTabEntity(7L, "baidu7", "time"),
    )
)
adapter.submitList(albums)

Fragment与 Fappearragment的替换

使用 Hold 过渡使该片段在过渡到专辑详细信息屏幕的 MaterialContainerTransform 下方可见。
如果没有 Hold,该 FraElementgment 将在其容器approach被新 Frapproachagment 替换后立即消失

将根视图添加为保留的目标,以便将整个视图层次结构作为
一个视图,elementanimation而不是每个子视图。有助于在过渡期间保持阴影。

val fragment = HomeFragment.newInstance(data.title!!)
val transform = MaterialContainerTransform(requireContext(),  /* entering= */true)
transform.duration = 666//持续时间/毫秒
fragment.sharedElementEnterTransition = transform
val hold = Hold()
hold.addTarget(R.id.container)//布局标签
hold.duration = transform.duration
//退出的过度动画
exitTransition = hold

拿到父布http协议局的fragment管理器打开搜索页面

parentFragmentManager
    .beginTransaction()
    .addSharedElement(view, ViewCompat.getTransitionName(view)!!)
    .replace(R.id.fragment_container, fragment,HomeFragment.TAG)
    .addToBackStack(MainFragment.TAG)
    .commit()

在HomeFragment搜索页面的构造函数里面新增一个参数

companion object {
    const val TAG = "HomeFragment"
    private const val KEY_TRANSITION_NAME = "key_ttansition_name"  
    fun newInstance(
        transitionName:String,   
    ): HomeFragment {
        return HomeFragment().apply {
            arguments = bundleOf(
                KEY_TRANSITION_NAME to transitionName,
            )
        }
    }
}

完整代码

private fun startFragment(
    hometab: HomeTabEntity,
    view: View
) {
    val fragment = HomeFragment.newInstance(hometab.title!!, hometab.id!!)
    val transform = MaterialContainerTransform(requireContext(),  /* entering= */true)
    transform.duration = 666
    fragment.sharedElementEnterTransition = transform
    val hold = Hold()
    hold.addTarget(R.id.container)
    hold.duration = transform.duration
    exitTransition = hold
    parentFragmentManager
        .beginTransaction()
        .addSharedElement(view, ViewCompat.getTransitionName(view)!!)
        .replace(R.id.fragment_container, fragment, MainFragment.TAG)
        .addToBackStack(MainFragment.TAG)
        .commit()
}

拿到参数之后在onViewCreated下为共享元素转换设置与要转换的开始布appearance局匹配的转换名称

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    arguments?.let { args ->
        transitionName = args.getString(KEY_TRANSITION_NAME)
    }
    if (transitionName != null) {
        val transitionNameView :ViewGroup = view.findViewById(R.id.fragment_container)
        ViewCompat.setTransitionName(transitionNameView, transitionName)
    }
}

最后点击接口的回调下使用

override fun onClicked(view: View, hometab: HomeTabEntity) {
    startFragment(hometab, view)
}