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

Android实现SearchView获焦从屏幕中央移动到Toolbar效果

Android SearchView 居中-顶部位移动效实现方案

核心问题说明

你当前代码的核心问题是把SearchView固定嵌套在AppBarLayout内部,AppBar的滚动/展开范围最多只能覆盖自身高度,完全无法将SearchView下移到屏幕中央的初始位置,仅靠setExpanded方法不可能实现预期效果。
实现的核心思路是:将SearchView作为CoordinatorLayout的顶层直接子View(不嵌套在AppBar或内容布局内),初始位置放在屏幕中央,监听焦点变化通过属性动画在初始位置、顶部AppBar位置之间平滑平移,全程使用同一个SearchView实例,不会出现输入状态丢失、页面切换的问题。

第一步:调整布局文件

将SearchView从AppBar内部移出,作为CoordinatorLayout的顶层子View,AppBar仅作为顶部位置锚点存在:

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/coordinatorLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:fitsSystemWindows="true">

    <!-- 顶部AppBar,可根据需求调整高度,代码会自动适配位置 -->
    <com.google.android.material.appbar.AppBarLayout
        android:id="@+id/appBar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:backgroundTint="@color/orange_lighter"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <!-- 内部无需放置SearchView,仅作为顶部位置锚点 -->
    </com.google.android.material.appbar.AppBarLayout>

    <!-- 页面内容区 -->
    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/constraintLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior">

        <!-- 此处放置RecyclerView、分类入口等其他页面控件 -->

    </androidx.constraintlayout.widget.ConstraintLayout>

    <!-- SearchView作为顶层子View,初始位置居中,方便做位移动画 -->
    <androidx.appcompat.widget.SearchView
        android:id="@+id/consultationSearchView"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        android:theme="@style/SearchViewTheme"
        app:closeIcon="@drawable/ic_cancel"
        app:searchIcon="@drawable/ic_search"
        android:layout_gravity="center"/>

</androidx.coordinatorlayout.widget.CoordinatorLayout>

第二步:实现焦点监听和平移动画

在Activity中提前计算好两个位置的坐标,焦点变化时启动平移动画,同步处理软键盘、AppBar状态:

class ExploreConsultationsActivity : BaseActivity() {

    companion object {
        const val CONSULTATION_COVER_LIST_KEY = "consultation_cover_list"
        private const val ANIM_DURATION = 300L
    }

    private val binding by lazy { ExploreConsultationsLayoutBinding.inflate(layoutInflater) }
    val viewModel by viewModel<ExploreConsultationViewModel>()
    // 记录SearchView初始居中位置的Y轴坐标
    private var searchViewInitY = 0f
    // 记录SearchView移动到顶部的目标Y轴坐标
    private var searchViewTargetY = 0f

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setupViewModel()
        setContentView(binding.root)
        setupView()
    }

    private fun setupViewModel() {
        viewModel.categoriesList = intent?.getParcelableArrayListExtra(CONSULTATION_COVER_LIST_KEY)
    }

    private fun setupView() {
        hideActionBar()
        // 等布局绘制完成后再计算坐标,避免拿到0值
        binding.consultationSearchView.post {
            searchViewInitY = binding.consultationSearchView.y
            // 计算顶部位置:AppBar底部坐标 - SearchView自身高度 - 16dp顶部边距
            searchViewTargetY = binding.appBar.bottom - binding.consultationSearchView.height - 16.dpToPx()
        }
        setupSearchView()
    }

    private fun setupSearchView() {
        binding.consultationSearchView.apply {
            setOnQueryTextListener(object : SearchView.OnQueryTextListener {
                override fun onQueryTextSubmit(query: String?) = false
                override fun onQueryTextChange(newText: String?): Boolean {
                    if (newText != null) {
                        viewModel.removeSearchViewFocus.value = false
                        viewModel.queryText = newText
                    }
                    return true
                }
            })
            setOnQueryTextFocusChangeListener { _, hasFocus ->
                isSelected = hasFocus
                if (hasFocus) {
                    // 获焦:平移到顶部,展开AppBar、弹出软键盘
                    animate()
                        .y(searchViewTargetY)
                        .setDuration(ANIM_DURATION)
                        .start()
                    binding.appBar.setExpanded(true, true)
                    val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                    imm.showSoftInput(this, InputMethodManager.SHOW_IMPLICIT)
                } else {
                    // 失焦:回到初始居中位置,收起AppBar、隐藏软键盘、清空输入
                    animate()
                        .y(searchViewInitY)
                        .setDuration(ANIM_DURATION)
                        .start()
                    binding.appBar.setExpanded(false, true)
                    val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
                    imm.hideSoftInputFromWindow(windowToken, 0)
                    setQuery("", false)
                }
            }
        }
    }

    // dp转px扩展方法
    private fun Int.dpToPx(): Float = this * resources.displayMetrics.density

    override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
        if (ev?.action == MotionEvent.ACTION_DOWN) {
            val view: View? = currentFocus
            if (view is SearchView) {
                val outRect = Rect()
                view.getGlobalVisibleRect(outRect)
                if (!outRect.contains(ev.rawX.toInt(), ev.rawY.toInt())) {
                    view.clearFocus()
                }
            }
        }
        return super.dispatchTouchEvent(ev)
    }
}

可选优化点

  • 如果需要实现更复杂的形变效果(比如SearchView尺寸变化、图标透明度切换、背景圆角变化),可以把根布局替换为MotionLayout,通过ConstraintSet定义居中、顶部两个状态的约束规则,焦点变化时调用transitionToState切换状态,动画可定制性更强
  • 动画执行过程中可以给下层内容区设置渐变透明度,强化搜索态的聚焦视觉效果
  • 如果需要适配横竖屏切换,重写onConfigurationChanged方法重新计算SearchView两个位置的Y轴坐标,避免屏幕旋转后位置错位

内容的提问来源于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.30 02:15:34