You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 17:48:14