2

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>
Iurii Mg
  • 63
  • 7
  • The examples were just showing how the exact same API applies to activity based menus and fragment based menus - you don't implement them both. – ianhanniballake Oct 02 '22 at 15:35
  • This is already answered here : https://stackoverflow.com/a/73080607/14778218 – Raj Oct 02 '22 at 15:37
  • Thank you so much, @ianhanniballake, it all worked out! I thought I had to change the code in both files, but it turned out to be much easier. – Iurii Mg Oct 02 '22 at 16:02
  • 1
    Does this answer your question? ['setHasOptionsMenu(Boolean): Unit' is deprecated. Deprecated in Java](https://stackoverflow.com/questions/71917856/sethasoptionsmenuboolean-unit-is-deprecated-deprecated-in-java) – Dr Mido Oct 03 '22 at 01:55
  • Yes, this question contains the same code from the android website. I just didn't know that it doesn't need to be embedded in both Activity and Fragment, it's enough to do it in the fragment. And I easily changed the code to fit my menu, I had no problem with that. – Iurii Mg Oct 03 '22 at 15:41

1 Answers1

1

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)
Vivek Gupta
  • 747
  • 4
  • 13