How now to create Option menu in Fragment (setHasOptionMenu is deprecated)

1.5k Views Asked by At

When trying to write setHasOptionsMenu(true) in onCreate and override fun onCreateOptionsMenu as usual, Android Studio crosses out these functions saying that they are deprecated.

I looked at what they suggest

https://developer.android.com/jetpack/androidx/releases/activity?authuser=5#1.4.0-alpha01

and it turns out that they are asking to insert some new functions in Activity (MainActivity.kt) and some in Fragment (DogListFragment.kt). But in my app, all menu customization was done only in Fragment, so Activity can't do that. Activity simply doesn't have access to the RecyclerView, which is in the layout (fragment_god_list.xml) that belongs to Fragment. Activity only has androidx.fragment.app.FragmentContainerView in its activity_main.xml

Does anyone know how this can be done in Fragment without having to do anything with the menus in Activity?

GitHub project: https://github.com/theMagusDev/DogglersApp

MainActivity.kt:

package com.example.dogglers

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.navigation.NavController
import androidx.navigation.fragment.NavHostFragment
import androidx.navigation.ui.setupActionBarWithNavController
import com.example.dogglers.databinding.ActivityMainBinding

private lateinit var navController: NavController

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        // Setup view binding
        val binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        // Setup navController
        val navHostFragment = supportFragmentManager
            .findFragmentById(R.id.nav_host_fragment) as NavHostFragment
        navController = navHostFragment.navController

        setupActionBarWithNavController(navController)
    }

    override fun onSupportNavigateUp(): Boolean {
        return navController.navigateUp() || super.onSupportNavigateUp()
    }
}

DogListFragment.kt:

package com.example.dogglers

import android.os.Bundle
import android.view.*
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.example.dogglers.adapter.DogCardAdapter
import com.example.dogglers.const.Layout
import com.example.dogglers.databinding.FragmentDogListBinding

class DogListFragment : Fragment() {
    private var _binding: FragmentDogListBinding? = null

    // This property is only valid between onCreateView and
    // onDestroyView.
    private val binding get() = _binding!!

    private lateinit var recyclerView: RecyclerView
    private var layoutType = Layout.VERTICAL

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setHasOptionsMenu(true)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentDogListBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        recyclerView = binding.verticalRecyclerView
        setUpAdapter()
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
        inflater.inflate(R.menu.layout_manu, menu)

        val layoutButton = menu.findItem(R.id.action_switch_layout)

        // Calls code to set the icon
        setIcon(layoutButton)
    }

    override fun onOptionsItemSelected(item: MenuItem): Boolean {
        when (item.itemId) {
            R.id.action_switch_layout -> {
                layoutType = when (layoutType) {
                    Layout.VERTICAL -> Layout.HORIZONTAL
                    Layout.HORIZONTAL -> Layout.GRID
                    else -> Layout.VERTICAL
                }
                setUpAdapter()

                return true
            }
            //  Otherwise, do nothing and use the core event handling

            // when clauses require that all possible paths be accounted for explicitly,
            //  for instance both the true and false cases if the value is a Boolean,
            //  or an else to catch all unhandled cases.

            else -> return super.onOptionsItemSelected(item)
        }
    }

    fun setUpAdapter() {
        recyclerView.adapter = when(layoutType){
            Layout.VERTICAL -> {
                recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.VERTICAL, false)
                DogCardAdapter(
                    context,
                    Layout.VERTICAL
                )
            }
            Layout.HORIZONTAL -> {
                recyclerView.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
                DogCardAdapter(
                    context,
                    Layout.HORIZONTAL
                )
            }
            else -> {
                recyclerView.layoutManager = GridLayoutManager(context, 2, RecyclerView.VERTICAL, false)
                DogCardAdapter(
                    context,
                    Layout.GRID
                )
            }
        }
    }

    private fun setIcon(menuItem: MenuItem?) {
        if (menuItem == null)
            return
        menuItem.icon = when(layoutType) {
            Layout.VERTICAL -> ContextCompat.getDrawable(this.requireContext(), R.drawable.ic_vertical_layout)
            Layout.HORIZONTAL -> ContextCompat.getDrawable(this.requireContext(), R.drawable.ic_horizontal_layout)
            else -> ContextCompat.getDrawable(this.requireContext(), R.drawable.ic_grid_layout)
        }
    }
}

ActivityMain.xml:

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
    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/nav_host_fragment"
        android:name="androidx.navigation.fragment.NavHostFragment"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:defaultNavHost="true"
        app:navGraph="@navigation/nav_graph"/>

</FrameLayout>

FragmentDogList:

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout 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.recyclerview.widget.RecyclerView
        android:id="@+id/vertical_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        android:orientation="vertical"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:listitem="@layout/vertical_list_item"/>

</FrameLayout>
2

There are 2 best solutions below

0
Vivek Gupta On

When you call setUpActionBarWithNavController() method , you are setting up toolbar inside the activity. Your Fragment is inside this activity. Your fragment has this actionBar too. To use Menu provider inside fragment, you need to call below method inside of onViewCreated() method of fragment.

requireActivity().addMenuProvider(this, viewLifecycleOwner, Lifecycle.State.RESUMED)

Also, You need to make your fragment implement MenuProvider Interface

class DogListFragment : Fragment(),MenuProvider {...

IDE will ask you to implement its provider method i.e onCreateMenu and onMenuItemSelected Inside OnCreateMenu, use menu Inflator to inflate menu layout example:-

 menuInflater.inflate(R.menu.search_menu,menu)
0
Rizwan Ahmed On

No one have been worked to me. Only bellow process worked. I think this cause is I implemented menu from xml file

  1. Added this part in xml

    <com.google.android.material.appbar.MaterialToolbar android:id="@+id/materialToolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/toolbar" app:layout_constraintTop_toTopOf="parent" app:title="Video on frame" app:menu="@menu/menu_home_frame" app:navigationIcon="@drawable/ic_menu" app:titleTextColor="@color/white" />

  2. from Fragment onCreatedView

     binding.materialToolbar.addMenuProvider(
         object : MenuProvider{
             override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
                 ///No need to implement. Al
             }
    
             override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
    
                 return when (menuItem.itemId) {
                     R.id.btn_booster -> {
                         startBoosterActivity()
                         true
                     }
    
                     R.id.btn_premium -> {
                         startActivity(PremiumActivity::class.java)
                         true
                     }
    
                     R.id.btn_search -> {
                         startActivity(SearchActivity::class.java)
                         true
                     }
    
                     else -> false
                 }
             }
                              }, viewLifecycleOwner, Lifecycle.State.RESUMED
     )