在我的工作项目中,我们需要在几个Fragments
中使用SearchView
。搜索栏本身必须放置在Toolbar
中,并通过单击Toolbar
(see the image)末尾的搜索按钮打开。因此,我通过以下方式实现了这样的行为:
- 添加只包含搜索项的 * menu.xml * 菜单文件;
- 添加只包含
androidx.appcompat.widget.SearchView
的 layout_search.xml 布局文件作为根视图; - 在我的片段中实现
MenuProvider
接口。
然后,当我试图测试它是否按预期工作时,我发现**onMenuItemSelected()
方法中编写的所有内容都从未被调用**。我的意思是,当单击工具栏中的搜索按钮时,搜索栏会像预期的那样出现在工具栏中,但系统不会调用我的侦听器来处理菜单项选择。
我只是不明白为什么菜单项选择回调没有被调用。我尝试了另一种方法来处理工具栏的菜单和它的项选择(例如,将工具栏放在我的片段中,膨胀菜单,然后用所需的处理程序设置binding.toolbar.setOnMenuItemClickListener
),但它也导致了同样的结局-项选择回调根本不起作用。
***我希望有人曾经遇到过同样的问题,并找到了解决方案,因为我已经浪费了3天时间试图让项目选择回调工作,现在我感到被难住了。
为了重现这个问题,我创建了一个带有底部导航栏模板的新项目,添加了资源,并在自动生成的片段中实现了MenuProvider
接口。为了测试项目选择回调是否被调用,我使用了logger,我的日志消息都没有出现在logcat中。代码示例如下:
HomeFragment.kt
class HomeFragment : Fragment(), MenuProvider {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
private val queryConsumer: (String) -> Unit = { Log.i("HOME_FRAGMENT", "CONSUMED QUERY: $it") }
private var searchView: SearchView? = null
private val onBackPressed: OnBackPressedCallback = object : OnBackPressedCallback(false) {
override fun handleOnBackPressed() {
clearSearch()
closeSearch()
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requireActivity().onBackPressedDispatcher.addCallback(onBackPressed)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val homeViewModel =
ViewModelProvider(this)[HomeViewModel::class.java]
_binding = FragmentHomeBinding.inflate(inflater, container, false)
val root: View = binding.root
val textView: TextView = binding.textHome
homeViewModel.text.observe(viewLifecycleOwner) {
textView.text = it
}
return root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val menuHost = requireActivity() as MenuHost
menuHost.addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.search_menu, menu)
}
override fun onMenuItemSelected(menuItem: MenuItem): Boolean = when (menuItem.itemId) {
R.id.search_menu_item -> {
Log.i("HOME_FRAGMENT", "NOW IN SEARCH MENU ITEM BRANCH")
searchView = menuItem.actionView as? SearchView
searchView?.run {
queryHint = "hint"
maxWidth = Int.MAX_VALUE
setOnClickListener {
onSearchOpened()
}
setOnCloseListener {
onSearchClosed()
queryConsumer.invoke("")
return@setOnCloseListener false
}
setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String): Boolean {
queryConsumer.invoke(query)
return true
}
override fun onQueryTextChange(newText: String): Boolean {
queryConsumer.invoke(newText)
return true
}
})
}
true
}
else -> {
Log.i("HOME_FRAGMENT", "NOW IN ELSE BRANCH")
false
}
}
fun clearSearch() {
searchView?.setQuery("", true)
}
fun closeSearch() {
searchView?.isIconified = true
}
fun onSearchClosed() {
onBackPressed.isEnabled = false
}
fun onSearchOpened() {
onBackPressed.isEnabled = true
}
}
布局_搜索.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.SearchView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/search"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</androidx.appcompat.widget.SearchView>
搜索菜单.xml
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
>
<item
android:id="@+id/search_menu_item"
android:contentDescription="@string/search_menu_title"
android:icon="?android:attr/actionModeWebSearchDrawable"
android:title="@string/search_menu_title"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always" />
</menu>
MainActivity.kt
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navView: BottomNavigationView = binding.navView
val navController = findNavController(R.id.nav_host_fragment_activity_main)
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
val appBarConfiguration = AppBarConfiguration(
setOf(
R.id.navigation_home, R.id.navigation_dashboard, R.id.navigation_notifications
)
)
setupActionBarWithNavController(navController, appBarConfiguration)
navView.setupWithNavController(navController)
}
}
1条答案
按热度按时间jyztefdp1#
问题隐藏在菜单 .xml 文件中...我只是随机地(从绝望中)将属性
app:showAsAction
的值从always
更改为ifRoom|collapseActionView
,所有回调(来自MenuProvider
的onMenuItemSelected
和来自工具栏的setOnMenuItemClickListener
)都开始工作!