如何防止横竖屏切换后SearchView自动获取焦点?
这个问题我之前也遇到过,横竖屏切换时SearchView自动抢焦点确实挺烦的,尤其是用户之前正在其他输入框操作的时候。本质原因是横竖屏切换会触发Activity重建(或配置变化回调),系统会自动恢复SearchView的展开状态和焦点。下面给你几个可行的解决方案:
方法一:保存状态,手动控制焦点恢复
这种方法遵循Android生命周期规范,通过保存SearchView的展开状态和之前的焦点视图,在重建后手动调整焦点:
步骤1:保存关键状态
在Activity中重写onSaveInstanceState,记录SearchView是否展开以及当前的焦点视图:
private var isSearchExpanded = false private var previousFocusView: View? = null override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) // 保存SearchView的展开状态 outState.putBoolean("isSearchExpanded", searchItem?.isActionViewExpanded ?: false) // 保存当前的焦点视图ID outState.putInt("previousFocusId", previousFocusView?.id ?: View.NO_ID) }
步骤2:恢复状态并调整焦点
在onCreate方法中恢复状态,展开SearchView后手动清除它的焦点,同时让之前的视图重新获焦:
override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val searchItem = menu.findItem(R.id.action_search) val searchView = searchItem.actionView as SearchView if (savedInstanceState != null) { isSearchExpanded = savedInstanceState.getBoolean("isSearchExpanded") val previousFocusId = savedInstanceState.getInt("previousFocusId") if (isSearchExpanded) { // 展开SearchView,但不自动获焦 searchItem.expandActionView() // 清除SearchView的焦点 searchView.clearFocus() // 恢复之前的焦点视图 if (previousFocusId != View.NO_ID) { findViewById<View>(previousFocusId)?.requestFocus() } } } // 监听SearchView的展开/折叠事件,实时更新状态 searchItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { override fun onMenuItemActionExpand(item: MenuItem): Boolean { isSearchExpanded = true return true } override fun onMenuItemActionCollapse(item: MenuItem): Boolean { isSearchExpanded = false return true } }) // 全局监听焦点变化,记录当前焦点视图 window.decorView.viewTreeObserver.addOnGlobalFocusChangeListener { oldFocus, newFocus -> previousFocusView = oldFocus ?: newFocus } }
方法二:拦截SearchView的焦点请求
通过给SearchView设置焦点监听器,在检测到是配置变化导致的焦点获取时,直接清除焦点:
步骤1:标记配置变化
在onConfigurationChanged中设置标志,同时记录之前的焦点视图:
private var isConfigurationChange = false private var previousFocusView: View? = null override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) isConfigurationChange = true previousFocusView = currentFocus }
步骤2:拦截焦点获取
给SearchView设置OnFocusChangeListener,当检测到是配置变化导致的焦点获取时,清除焦点并恢复之前的视图:
val searchView = searchItem.actionView as SearchView searchView.setOnFocusChangeListener { v, hasFocus -> if (hasFocus && isConfigurationChange) { // 清除SearchView的焦点 v.clearFocus() // 恢复之前的焦点视图 previousFocusView?.requestFocus() // 重置标志,避免影响正常的焦点操作 isConfigurationChange = false } }
方法三:阻止Activity重建(简化处理)
如果你不想让Activity在横竖屏切换时重建,可以在AndroidManifest.xml中给Activity添加configChanges属性,系统会调用onConfigurationChanged而不是重建Activity:
步骤1:修改Manifest配置
<activity android:name=".YourActivity" android:configChanges="orientation|screenSize|smallestScreenSize"> <!-- 其他属性 --> </activity>
步骤2:在回调中处理焦点
重写onConfigurationChanged,直接检查SearchView的状态并调整焦点:
override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) val searchView = findViewById<SearchView>(R.id.search_view) val previousFocus = currentFocus // 如果SearchView展开且获取了焦点,就清除它的焦点,恢复之前的视图 if (searchView.isIconified.not() && searchView.hasFocus()) { searchView.clearFocus() previousFocus?.requestFocus() } }
这种方法的优点是不需要处理状态保存和恢复,适合界面逻辑不复杂的场景。
内容的提问来源于stack exchange,提问作者Surendar Dharavath

