Android如何实现居中SearchView获焦后移至AppBar的Chrome式效果
Android实现类Chrome搜索栏交互方案
核心实现选型
直接用MotionLayout做单SearchView的约束位移动画是最优方案,比手写属性动画适配成本低、状态一致性好,不会出现焦点错乱、输入内容丢失的问题,完全可以实现初始居中、获焦自动上移到AppBar的交互。
实现步骤
- 配置双状态约束
在MotionScene中定义两个约束集:- 初始状态:SearchView设置水平、垂直双向居中约束,AppBar可展示默认标题
- 聚焦状态:SearchView约束绑定到AppBar内部,匹配顶部搜索栏的边距和位置,可同步配置页面其他内容的渐隐、位移动画,对齐Chrome的过渡效果
- 绑定焦点触发逻辑
给SearchView设置焦点变化监听:获焦时触发动画切到顶部状态,失焦且输入内容为空时切回初始居中状态 - 动效参数对齐原生体验
动画时长设置为280ms左右,插值器用FastOutSlowInInterpolator,和系统Material动效节奏一致;同步处理输入法的弹出/收起,避免动效和输入法不同步。
核心代码参考
布局文件(根节点为MotionLayout)
<androidx.constraintlayout.motion.widget.MotionLayout android:id="@+id/motionContainer" android:layout_width="match_parent" android:layout_height="match_parent" app:layoutDescription="@xml/search_bar_motion_scene"> <com.google.android.material.appbar.AppBarLayout android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" app:layout_constraintTop_toTopOf="parent" android:background="@android:color/transparent"/> <androidx.appcompat.widget.SearchView android:id="@+id/searchView" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginHorizontal="16dp" app:defaultQueryHint="搜索内容"/> <!-- 页面其余业务内容放在此处 --> </androidx.constraintlayout.motion.widget.MotionLayout>
MotionScene配置(res/xml/search_bar_motion_scene.xml)
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android" xmlns:motion="http://schemas.android.com/apk/res-auto"> <!-- 初始居中状态 --> <ConstraintSet android:id="@+id/start"> <Constraint android:id="@+id/searchView" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginHorizontal="16dp" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="parent" motion:layout_constraintBottom_toBottomOf="parent"/> <Constraint android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:alpha="1" motion:layout_constraintTop_toTopOf="parent"/> </ConstraintSet> <!-- 获焦后顶部状态 --> <ConstraintSet android:id="@+id/end"> <Constraint android:id="@+id/searchView" android:layout_width="match_parent" android:layout_height="48dp" android:layout_marginHorizontal="16dp" motion:layout_constraintStart_toStartOf="parent" motion:layout_constraintEnd_toEndOf="parent" motion:layout_constraintTop_toTopOf="@id/appBar" motion:layout_constraintBottom_toBottomOf="@id/appBar"/> <Constraint android:id="@+id/appBar" android:layout_width="match_parent" android:layout_height="?attr/actionBarSize" android:alpha="1" android:background="?attr/colorPrimary" motion:layout_constraintTop_toTopOf="parent"/> </ConstraintSet> <Transition motion:constraintSetStart="@id/start" motion:constraintSetEnd="@id/end" motion:duration="280" motion:motionInterpolator="fastOutSlowIn"/> </MotionScene>
焦点监听逻辑(Kotlin)
binding.searchView.setOnQueryTextFocusChangeListener { _, hasFocus -> if (hasFocus) { binding.motionContainer.transitionToEnd() binding.searchView.post { ViewCompat.getWindowInsetsController(binding.searchView) ?.show(WindowInsetsCompat.Type.ime()) } } else { if (binding.searchView.query.isNullOrBlank()) { binding.motionContainer.transitionToStart() } ViewCompat.getWindowInsetsController(binding.searchView) ?.hide(WindowInsetsCompat.Type.ime()) } }
优化提示
不要用两个SearchView做显隐切换实现该效果,会导致输入状态丢失、焦点不同步的bug。如果需要更贴近Chrome的效果,可以在Transition中追加页面主内容的Y轴位移、透明度渐变,以及AppBar背景色的过渡动画,整体流畅度会更高。如果项目用Jetpack Compose实现,逻辑完全一致,用
AnimatedConstraintLayout配合状态监听即可实现相同效果。
内容的提问来源于stack exchange,提问作者Compose Learner
相关产品推荐
相关产品推荐

