I have a fragment with toolbar, I need to handle navigation by the click on options menu item. At first i thought the problem is navigation, but then I supposed that it just not clickable. I tried to use Toast to see if it's working and it's not. Layout screenshot
CocktailListFragment
class CocktailListFragment : Fragment() {
private val viewModel: CocktailListViewModel by lazy {
ViewModelProvider(this).get(CocktailListViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = CocktailListFragmentBinding.inflate(inflater)
...
setHasOptionsMenu(true)
return binding.root
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.filter_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.filter -> {
Toast.makeText(context, "Filter clicked", Toast.LENGTH_LONG).show()
true
} else -> super.onOptionsItemSelected(item)
}
}
}
Can this problem be connected with layout?
filter_list.xml
<layout 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">
<data>
<variable
name="cocktail"
type="com.example.coctaildb.cocktaillist.CocktailListViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
style="#style/Widget.AppCompat.Toolbar"
android:background="#android:color/white"
app:menu="#menu/filter_menu"
app:title="#string/drinks"
android:elevation="20dp"
app:titleTextColor="#android:color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
... />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Here my menu file:
<menu xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:id="#+id/filter"
android:icon="#drawable/ic_filter_icon"
android:title="#string/filters"
app:showAsAction="always" />
</menu>
Try to handle your click events with the OnMenuItemClickListener method:
class CocktailListFragment : Fragment() {
private val viewModel: CocktailListViewModel by lazy {
ViewModelProvider(this).get(CocktailListViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val binding = CocktailListFragmentBinding.inflate(inflater)
...
binding.root.toolbar.setOnMenuItemClickListener { item ->
when (item.itemId) {
R.id.filter -> {
Toast.makeText(context, "Filter clicked", Toast.LENGTH_LONG).show()
true
}
}
false
}
return binding.root
}
}
Related
I have an app that uses NavigationComponents among with ViewPager 2.
I'd like to use ViewPager for switching between fragments. I've made my ActivityMain as FragmentContainerView, and I'd like to have ViewPager implemented in one of my fragments.
The problem is, the ViewPager doesn't work at all. It doesn't change fragments, don't know why. What should I change in the code?
ActivityMain
<?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"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="#navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>
First fragment
class BlankFragment : Fragment() {
private var mPag: ViewPager2? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_blank, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mPag = view.findViewById(R.id.pager123)
val adapter = PagerAdapter(this)
val list = mutableListOf<Fragment>()
list.add(BlankFragment())
list.add(BlankFragment2())
mPag?.adapter = adapter
}
}
.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BlankFragment"
android:orientation="vertical">
<TextView
android:id="#+id/test123"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="111111111" />
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/pager123"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#a1a1"/>
</LinearLayout>
Second fragment
class BlankFragment2 : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_blank2, container, false)
}
}
.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".BlankFragment2">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="222222222222222" />
</FrameLayout>
Nav graph
<?xml version="1.0" encoding="utf-8"?>
<navigation 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:id="#+id/nav_graph"
app:startDestination="#id/blankFragment">
<fragment
android:id="#+id/blankFragment"
android:name="com.example.myapplication.BlankFragment"
android:label="fragment_blank"
tools:layout="#layout/fragment_blank" />
<fragment
android:id="#+id/blankFragment2"
android:name="com.example.myapplication.BlankFragment2"
android:label="fragment_blank2"
tools:layout="#layout/fragment_blank2" />
</navigation>
Pager adapter
class PagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
val mFragments = mutableListOf<Fragment>()
override fun getItemCount(): Int {
return mFragments.size
}
override fun createFragment(position: Int): Fragment {
when (position){
0 -> return BlankFragment()
1 -> return BlankFragment2()
}
return mFragments[position]
}
}
What you are essentially missing is what I tried to explain in the previous question.
You need to use Navigation component and ViewPager2 as two separate entities.
There is also issue with your PagerAdapter. Fragment adapters are not similar to other Adapters you might have experience with(mFragments).
They shouldn't hold a reference to those fragments. There are two main Fragment adapters(more here).
class PagerAdapter(fragment: Fragment) : FragmentStateAdapter(fragment) {
override fun getItemCount(): Int = 2
override fun createFragment(position: Int): Fragment {
return when (position){
0 -> BlankFragment1()
1 -> BlankFragment2()
else -> throw IllegalArgumentException("Out of fragments, will depend on getItemCount")
}
}
}
You need to have Fragments that are solely in the Navigation component(e.g. FirstFragment & SecondFragment) and Fragments that belong to the ViewPager2(e.g. BlankFragment1 & BlankFragment2)
<?xml version="1.0" encoding="utf-8"?>
<navigation android:id="#+id/nav_graph"
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"
app:startDestination="#id/FirstFragment">
<fragment
android:id="#+id/FirstFragment"
android:name="com.example.myapplication.FirstFragment"
android:label="#string/first_fragment_label"
tools:layout="#layout/fragment_first" >
<action
android:id="#+id/action_FirstFragment_to_SecondFragment"
app:destination="#id/SecondFragment" />
</fragment>
<fragment
android:id="#+id/SecondFragment"
android:name="com.example.myapplication.SecondFragment"
android:label="#string/second_fragment_label"
tools:layout="#layout/fragment_second" />
</navigation>
Fragments for ViewPager2:
class BlankFragment1: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_blank_1, container, false)
}
}
class BlankFragment2: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_blank_2, container, false)
}
}
The FirstFragment is going to host the ViewPager2 that will allow you to swipe between BlankFragment1 and BlankFragment2.
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_first, container, false)
view.findViewById<ViewPager2>(R.id.vp2)?.let { viewPager2 ->
val pagerAdapter = PagerAdapter(this)
viewPager2.adapter = pagerAdapter
}
return view
}
Now the only thing you need to do in any of the fragments to use Navigation component is to simply findNavControler and navigate to destination you want.
class BlankFragment1: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view = inflater.inflate(R.layout.fragment_blank_1, container, false)
view.findViewById<MaterialButton>(R.id.blank_1_button).setOnClickListener {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
}
return view
}
}
I am using bottom navigation. I want to open a dialog window by selecting the option menu after switching to another fragment screen through the bottom menu.
But I don't see the option menu..
I wrote it by referring to other sample code, but I do not know the cause
WorkoutListFragment.kt
class WorkoutListFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.fragment_workout_list, container,false)
return view
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater.inflate(R.menu.record_item_add, menu)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
val dialog = BodyPartDialogFragment()
dialog.show(activity!!.supportFragmentManager, "BodyPartDialog")
return true
}
}
ADDED
val toolbar: Toolbar = view.findViewById(R.id.toolbar)
(activity as AppCompatActivity).setSupportActionBar(toolbar)
WorkouListFragment.xml
<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"
tools:context=".fragment.WorkoutListFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/coordinator"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:elevation="0dp"
android:theme="#style/Theme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:title="LIST"
app:titleTextColor="#color/black"
app:titleMarginStart="30dp"
android:paddingRight="30dp"
android:theme="#style/Theme.PopupOverlay"/>
</com.google.android.material.appbar.AppBarLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
I'm trying to implement a searchView in a Fragment, using Material UI components but I can't seem to get any output when setting the listener, so I'm guessing I have missed a step in the setup process.
The problem
So when I load my app and start typing in the search view, I expect to see some output in the logcat of Android Studio, but nothing seems to be shown.
What I've done
Created an XML layout with Material UI toolbar.
...
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fitsSystemWindows="true"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#000000"
app:menu="#menu/menu_search"
app:navigationIcon="#drawable/ic_arrow_back" />
</com.google.android.material.appbar.AppBarLayout>
...
Created a menu item (menu_search.xml)
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_search"
android:icon="#drawable/ic_search_24dp"
android:title="Menu Search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="always" />
In my Fragment
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.menu_search, menu)
val search = menu.findItem(R.id.action_search)
val searchView = search.actionView as SearchView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
Timber.d("onQueryTextSubmit")
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
Timber.d("onQueryTextChange $newText")
return false
}
})
super.onCreateOptionsMenu(menu, inflater)
}
Other things that might be relevant
I'm not overriding onOptionsItemSelected in my fragment.
I'm using data-binding.
Any help would be greatly appreciated, thanks.
Try to instantiate your menu in the onCreateView method of the fragment:
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View?
{
val view = inflater.inflate(<your-fragment-layout>, container, false)
view.findViewById<MaterialToolbar>(R.id.toolbar).apply {
val searchView = menu.findItem(R.id.action_search).actionView
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
Timber.d("onQueryTextSubmit")
return false
}
override fun onQueryTextChange(newText: String?): Boolean {
Timber.d("onQueryTextChange $newText")
return false
}
})
}
...
return view
}
I am trying to add a SearchView to my custom Toolbar.
I've tried adding the SearchView directly to the Toolbar with toolbar.addView(searchView) but it didn't change anything, I am getting a blank Toolbar no matter what I try.
What am I doing wrong? this should be simple...
Toolbar:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:elevation="4dp"
android:theme="#style/ThemeOverlay.AppCompat.Light"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light">
</androidx.appcompat.widget.Toolbar>
Search Menu:
<?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/action_search"
android:icon="#drawable/ic_search"
android:title="Search"
app:actionViewClass="androidx.appcompat.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
</menu>
Fragment:
lateinit var toolbar: Toolbar
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
view = ConstraintLayout(this.context)
view.layoutParams = ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.MATCH_PARENT, ConstraintLayout.LayoutParams.MATCH_PARENT)
toolbar = inflater.inflate(R.layout.toolbar, container, false) as Toolbar
setHasOptionsMenu(true)
return view
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
// Inflate the options menu from XML
inflater?.inflate(R.menu.search_menu, menu)
val searchItem = menu?.findItem(R.id.action_search)
val searchView = searchItem?.actionView as SearchView
searchView.imeOptions = EditorInfo.IME_ACTION_DONE
searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(p0: String?): Boolean {
return false
}
override fun onQueryTextChange(p0: String?): Boolean {
//adapter.getFilter().filter(newText);
return false
}
})
toolbar.addView(searchView)
}
Solved it with adding setSupportActionBar(toolbar) in onCreateView()
I am working on app that uses Navigation pattern.
Now I have an Activity and few fragments.
Each Fragment has a unique functionality so I created a separate toolbar for each fragment: collapsing, usual or no toolbar at all.
I used the following example: https://android.jlelse.eu/instagram-style-navigation-using-navigation-component-854037cf1389
It works pretty well, but now I am taking an issue if I want to set some menu items in the toolbar.
For example, I would like to add a settings menu to the toolbar of the Profile Fragment
I tried setting a menu item, but it simply never appears and I think the method doesn't get called at all..
Is it possible to solve this issue without changing the whole architecture logic?
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
binding = DataBindingUtil.inflate(inflater, R.layout.my_layout, container, false)
setHasOptionsMenu(true)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.toolbar.apply {
setupWithNavController(findNavController())
}
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
inflater.inflate(R.menu.main_menu, menu)
super.onCreateOptionsMenu(menu, inflater)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
if(item.itemId == R.id.action_get_info)
Toast.makeText(context!!, "test message", Toast.LENGTH_SHORT).show()
return super.onOptionsItemSelected(item)
}
<?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/action_get_info"
android:icon="#drawable/ic_setting"
android:title="Settings"
app:showAsAction="always" />
</menu>
<layout 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"
tools:context="com.example.MyFragment">
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:fitsSystemWindows="true"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/white">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
style="#style/ToolbarAppearance"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize">
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_test_image" />
</RelativeLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>