如何在Android Studio(Kotlin)中实现UC浏览器同款搜索栏?
单Toolbar实现UC浏览器风格搜索栏交互逻辑
一、优化后的XML布局(仅一个Toolbar)
将所有需要的控件整合到单个Toolbar中,通过控制控件可见性、图标和文本变化实现交互,避免切换整个Toolbar:
<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:padding="16dp" tools:context=".fragment.HomeFragment"> <androidx.appcompat.widget.Toolbar android:id="@+id/search_toolbar" android:layout_width="match_parent" android:layout_height="50dp" android:background="@drawable/custom_design" android:elevation="6dp" android:minHeight="?attr/actionBarSize" android:theme="?attr/actionBarTheme" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent"> <LinearLayout android:id="@+id/search_container" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <!-- 搜索引擎图标按钮 --> <ImageButton android:id="@+id/engine_icon_btn" android:layout_width="46dp" android:layout_height="46dp" android:layout_marginBottom="10dp" android:layout_weight="1" android:background="?attr/selectableItemBackground" android:contentDescription="@string/search_engine_icon" android:src="@drawable/google" tools:ignore="TouchTargetSizeCheck" /> <!-- 初始状态的提示文本 --> <TextView android:id="@+id/search_hint_tv" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="8" android:background="@android:color/transparent" android:clickable="true" android:focusable="true" android:gravity="center_vertical" android:paddingStart="8dp" android:paddingEnd="8dp" android:text="@string/search_or_enter_url" android:textColor="#494747" android:textSize="18sp" /> <!-- 输入状态的EditText --> <EditText android:id="@+id/search_input_et" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="8" android:background="@android:color/transparent" android:hint="@string/search_or_enter_url" android:imeOptions="actionSearch" android:inputType="text" android:paddingStart="8dp" android:paddingEnd="8dp" android:singleLine="true" android:textColorHint="#494747" android:visibility="gone" tools:ignore="VisualLintTextFieldSize" /> <!-- 麦克风/清除按钮(复用同一个控件) --> <ImageButton android:id="@+id/mic_clear_btn" android:layout_width="46dp" android:layout_height="46dp" android:layout_marginBottom="10dp" android:layout_weight="1" android:background="?attr/selectableItemBackground" android:contentDescription="@string/microphone" android:src="@drawable/baseline_mic_24" tools:ignore="TouchTargetSizeCheck" /> <!-- 分割线(仅输入状态显示) --> <View android:id="@+id/divider_view" android:layout_width="2dp" android:layout_height="40dp" android:layout_gravity="center_vertical" android:background="?android:attr/listDivider" android:visibility="gone" /> <!-- 取消/搜索按钮(复用同一个控件) --> <TextView android:id="@+id/cancel_search_tv" android:layout_width="46dp" android:layout_height="match_parent" android:layout_weight="1" android:clickable="true" android:focusable="true" android:gravity="center" android:text="@string/cancel" android:textColor="#434141" android:visibility="gone" /> </LinearLayout> </androidx.appcompat.widget.Toolbar> </androidx.constraintlayout.widget.ConstraintLayout>
二、Kotlin逻辑实现状态切换
在HomeFragment中处理所有交互逻辑,通过状态枚举管理不同场景下的控件变化:
import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.ImageButton import android.widget.TextView import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import android.text.Editable import android.text.TextWatcher import android.app.AlertDialog class HomeFragment : Fragment() { private lateinit var searchHintTv: TextView private lateinit var searchInputEt: EditText private lateinit var micClearBtn: ImageButton private lateinit var cancelSearchTv: TextView private lateinit var dividerView: View private lateinit var engineIconBtn: ImageButton private enum class SearchState { INITIAL, INPUT, INPUT_WITH_TEXT } private var currentState = SearchState.INITIAL override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { val view = inflater.inflate(R.layout.fragment_home, container, false) initViews(view) setupListeners() return view } private fun initViews(view: View) { searchHintTv = view.findViewById(R.id.search_hint_tv) searchInputEt = view.findViewById(R.id.search_input_et) micClearBtn = view.findViewById(R.id.mic_clear_btn) cancelSearchTv = view.findViewById(R.id.cancel_search_tv) dividerView = view.findViewById(R.id.divider_view) engineIconBtn = view.findViewById(R.id.engine_icon_btn) } private fun setupListeners() { // 初始状态点击提示文本切换到输入状态 searchHintTv.setOnClickListener { switchToState(SearchState.INPUT) searchInputEt.requestFocus() // 弹出软键盘 val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.showSoftInput(searchInputEt, InputMethodManager.SHOW_IMPLICIT) } // 文本输入监听,动态切换按钮状态 searchInputEt.addTextChangedListener(object : TextWatcher { override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {} override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { currentState = if (!s.isNullOrEmpty()) SearchState.INPUT_WITH_TEXT else SearchState.INPUT updateStateUI() } override fun afterTextChanged(s: Editable?) {} }) // 麦克风/清除按钮点击事件 micClearBtn.setOnClickListener { when (currentState) { SearchState.INITIAL, SearchState.INPUT -> performVoiceSearch() SearchState.INPUT_WITH_TEXT -> searchInputEt.setText("") } } // 取消/搜索按钮点击事件 cancelSearchTv.setOnClickListener { when (currentState) { SearchState.INPUT -> { switchToState(SearchState.INITIAL) // 隐藏软键盘 val imm = requireContext().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager imm.hideSoftInputFromWindow(searchInputEt.windowToken, 0) } SearchState.INPUT_WITH_TEXT -> performSearch(searchInputEt.text.toString()) } } // 搜索引擎图标点击弹出选择列表 engineIconBtn.setOnClickListener { showEngineSelectionDialog() } } // 切换状态并更新UI private fun switchToState(state: SearchState) { currentState = state updateStateUI() } // 根据当前状态更新控件显示 private fun updateStateUI() { when (currentState) { SearchState.INITIAL -> { searchHintTv.visibility = View.VISIBLE searchInputEt.visibility = View.GONE micClearBtn.setImageResource(R.drawable.baseline_mic_24) micClearBtn.contentDescription = getString(R.string.microphone) dividerView.visibility = View.GONE cancelSearchTv.visibility = View.GONE view?.findViewById<Toolbar>(R.id.search_toolbar)?.setBackgroundResource(R.drawable.custom_design) } SearchState.INPUT -> { searchHintTv.visibility = View.GONE searchInputEt.visibility = View.VISIBLE micClearBtn.setImageResource(R.drawable.baseline_mic_24) micClearBtn.contentDescription = getString(R.string.microphone) dividerView.visibility = View.VISIBLE cancelSearchTv.visibility = View.VISIBLE cancelSearchTv.text = getString(R.string.cancel) view?.findViewById<Toolbar>(R.id.search_toolbar)?.setBackgroundColor(resources.getColor(R.color.white)) } SearchState.INPUT_WITH_TEXT -> { searchHintTv.visibility = View.GONE searchInputEt.visibility = View.VISIBLE micClearBtn.setImageResource(R.drawable.baseline_cancel_24) micClearBtn.contentDescription = getString(R.string.clear_text) dividerView.visibility = View.VISIBLE cancelSearchTv.visibility = View.VISIBLE cancelSearchTv.text = getString(R.string.search) view?.findViewById<Toolbar>(R.id.search_toolbar)?.setBackgroundColor(resources.getColor(R.color.white)) } } } // 语音搜索实现(自行补充) private fun performVoiceSearch() { // 此处添加语音搜索逻辑 } // 搜索逻辑实现(自行补充) private fun performSearch(query: String) { // 此处添加搜索跳转逻辑,比如打开WebView } // 搜索引擎选择对话框 private fun showEngineSelectionDialog() { val engines = arrayOf("Google", "Bing", "百度", "搜狗") AlertDialog.Builder(requireContext()) .setTitle("选择搜索引擎") .setItems(engines) { _, which -> when (which) { 0 -> engineIconBtn.setImageResource(R.drawable.google) 1 -> engineIconBtn.setImageResource(R.drawable.bing) 2 -> engineIconBtn.setImageResource(R.drawable.baidu) 3 -> engineIconBtn.setImageResource(R.drawable.sogou) } } .show() } }
三、核心优化点
- 控件复用:麦克风/清除、取消/搜索按钮均复用同一控件,仅切换图标和文本,避免冗余布局
- 状态枚举管理:用
SearchState明确区分三种交互状态,逻辑清晰易维护 - 视觉同步:随状态切换Toolbar背景样式,匹配UC浏览器的视觉效果
- 用户体验优化:自动控制软键盘显示/隐藏,减少操作步骤
内容的提问来源于stack exchange,提问作者Arshad Siddiqui
相关产品推荐
相关产品推荐

