Kotlin - Call fragment method from MainActivity - android

I have two fragments and a toolbar with a menu. I want to be able to switch fragments when a menu item is selected by calling the method in the fragment that does this. Problem is, I get this error when I try running the app with the code below. java.lang.IllegalStateException: Fragment FirstFragment{5ce330b (f5c2d362-d3fb-4346-9971-dd7be653e609)} not associated with a fragment manager.
MainActivity.kt
class MainActivity : AppCompatActivity() {
lateinit var adapter: Adapter
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.toolbar))
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> {
FirstFragment().settings()
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
FirstFragment.kt
fun settings() {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
}
How can I implement a fragment manager so I can call this method via MenuItem and not get an exception? Thanks in advance!

Thanks for the help! I solved the issue by creating the MenuItem in my first fragment like so:
lateinit var second: MenuItem
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
second = menu.add("second fragment")
second.setOnMenuItemClickListener() {
settings()
true
}
}
fun settings() {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
}

Related

How to avoid reinflating menu in fragments with AppBarLayout?

I inflate my SearchView into my AppBarLayout and MaterialToolbar in MainActivity. When I search items in ListFragment and open DetailFragment for an item then get back my AppBarLayout inflates again and I see my list is cleared. I want it to save query and show my previous state.
In ListFragment I do:
override fun onCreateOptionsMenu(menu: Menu, menuInflater: MenuInflater) {
menuInflater.inflate(R.menu.menu_main, menu)
}
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
setupSearchView(menu)
}
private fun setupSearchView(menu: Menu) {
userSearchView = menu.findItem(R.id.search).actionView as SearchView
userSearchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener {
override fun onQueryTextSubmit(query: String?): Boolean {
query?.let(viewModel::trySearching)
return true
}
override fun onQueryTextChange(newText: String?): Boolean {
newText?.let(viewModel::trySearching)
return true
}
})
}
In DetailFragment I do:
override fun onPrepareOptionsMenu(menu: Menu) {
super.onPrepareOptionsMenu(menu)
menu.clear()
}
For navigation I use AAC nav component

Must onCreateOptionsMenu become implemented of each activity?

When I have an app with multiple activities and like to have an options-menu all-over: Do I have to override the onCreateOptionsMenu-method in each activity?
Or can that become centralized?
You can prevent that boilerplate code creating a common Activity which all of your Activities inherit.
abstract class CommonSaveActivity : Activity() {
#CallSuper
override fun onCreateOptionsMenu(menu: Menu): Boolean {
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.common_save_action_menu, menu)
return true
}
#CallSuper
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.saveAction -> {
// TODO: handle common save action
true
}
else -> super.onOptionsItemSelected(item)
}
}
}
class ActivityA : CommonSaveActivity()
class ActivityB : CommonSaveActivity()
class ActivityC : CommonSaveActivity() {
// You can have the common save menu AND your
// activity specific menu at the same time in UI.
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// NOTE: if you call super.onCreateOptionsMenu(menu) AFTER
// menuInflater.infalte(..) line, then the order of menu
// items will be different.
super.onCreateOptionsMenu(menu)
menuInflater.inflate(R.menu.activity_c_menu, menu)
return true
}

Show menu actions only in certain fragments - NavigationComponents

I have a navigation host activity that inflates 2 fragments, one the main host fragment and the event fragments, I need to show in the event fragment a menu action , but this menu action is also shown in the main fragment which I dont want to show
How do I show this menu only in the event fragment ?
MainActivity
class MainActivity: AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navController = Navigation.findNavController(this, R.id.nav_host_fragment)
setupActionBar(navController)
}
private fun setupActionBar(navController: NavController) {
NavigationUI.setupActionBarWithNavController(this, navController)
}
override fun onSupportNavigateUp(): Boolean {
return Navigation.findNavController(this, R.id.nav_host_fragment).navigateUp()
|| super.onSupportNavigateUp()
}
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
MenuInflater(this).inflate(R.menu.menu, menu)
return super.onCreateOptionsMenu(menu)
}
override fun onOptionsItemSelected(item: MenuItem?): Boolean {
NavigationUI.onNavDestinationSelected(item!!, Navigation.findNavController(this, R.id.nav_host_fragment))
return super.onOptionsItemSelected(item)
}
Menu
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/eventsFragment"
app:showAsAction="ifRoom|always"
android:title="¿ Where to buy ?" />
</menu>
I need to show this menu only in the eventsFragment
EventFragment
class EventsFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_event, container, false)
}
But instead of just showing it just in the EventFragment it also shows it in the first fragment that MainActivity as hosts inflates
How can I just have this menu in my EventsFragment?
In MainActivity menuItem setVisible(false):
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
MenuInflater(this).inflate(R.menu.menu, menu)
val menuItem : MenuItem = menu.findItem(R.id.eventsFragment);
if (menuItem!= null)
menuItem.setVisible(false);
return super.onCreateOptionsMenu(menu)
}
In EventsFragment setvisibility of menuitem to true as shown below:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onPrepareOptionsMenu(menu: Menu) {
val menuItem = menu.findItem(R.id.eventsFragment)
if (menuItem != null)
menuItem.isVisible = true
}
You could try considering Inflating the menu from inside the fragment. ie. fragments onCreate() method and destroying it from onDestroy() function of the fragment

gridView.setMultiChoiceModeListener NOT working in android app

My code as is follows:
class MySitesActivity : AppCompatActivity() {
val REQUEST_CODE = 3
private val TAG = "MySitesActivity"
lateinit var gridView: GridView
lateinit var siteAdapter:BaseAdapter
lateinit var sites:ArrayList<Site>
lateinit var actionBarObject:ActionBar
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_my_sites)
setSupportActionBar(findViewById(R.id.my_toolbar))
sites = ArrayList(db.appDao().getAllSites()) //From Database
gridView = findViewById<View>(R.id.gridview) as GridView
siteAdapter = SitesAdapter(this#MySitesActivity, sites)
gridView.adapter = siteAdapter
gridView.choiceMode = GridView.CHOICE_MODE_MULTIPLE // CAN DO IN XML
actionBarObject = supportActionBar!!
actionBarObject.setDisplayHomeAsUpEnabled(true)
Log.d(TAG, "* Setting MultiChoiceModeListener *")
gridView.setMultiChoiceModeListener(object : AbsListView.MultiChoiceModeListener {
override fun onItemCheckedStateChanged(mode: ActionMode, position: Int,
id: Long, checked: Boolean) {
// Here you can do something when items are selected/de-selected,
// such as update the title in the CAB
}
override fun onActionItemClicked(mode: ActionMode, item: MenuItem): Boolean {
// Respond to clicks on the actions in the CAB
return false
}
override fun onCreateActionMode(mode: ActionMode, menu: Menu): Boolean {
// Inflate the menu for the CAB
Log.d(TAG,"Inflating menu")
mode.menuInflater.inflate(R.menu.delete_menu, menu)
return true
}
override fun onDestroyActionMode(mode: ActionMode) {
// Here you can make any necessary updates to the activity when
// the CAB is removed. By default, selected items are deselected/unchecked.
}
override fun onPrepareActionMode(mode: ActionMode, menu: Menu): Boolean {
// Here you can perform updates to the CAB due to
// an invalidate() request
return false
}
})
}
}
For brevity, I've removed the databse functions. The onCreate Action Mode never gets called and the Log statement never spits out the data.
My SiteAdapter Class extends the Baseadapter and the view returned is fine and it shows fine. I am not adding any listener or anything like that in my siteadapter class. Its just that on long click nothing happens. Any ideas as what could I be doing wrong? Thanks
Ok, So I've chosen the wrong mode.
GridView.CHOICE_MODE_MULTIPLE instead of GridView.CHOICE_MODE_MULTIPLE_MODAL
This change solve the INITIAL problem and created a new one which I think I should post it on a seperate thread.

setSupportActionBar inside of a Fragment

I have a Fragment:
class HomeFragment : Fragment() { ... }
Now I'm trying to add an Actionbar to it, but this doesn't work:
setSupportActionBar(findViewById(R.id.toolbar_main))
How can I set the Support and then add Items to the ActionBar?
This is how it works in an AppCompatActivity:
// This adds items to the ActionBar
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_toolbar_main, menu)
return true
}
// This is the OnClickListener for the Buttons in the ActionBar
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.toolbar_edit -> {
true
}
R.id.toolbar_search -> {
true
}
else -> {
// If we got here, the user's action was not recognized.
// Invoke the superclass to handle it.
super.onOptionsItemSelected(item)
}
}
Big thanks in advance!
Override onCreateOptionsMenu in your Fragment and inflate your menu inside. Than in onCreate method of Fragment set setHasOptionsMenu() to true. To inflate different menus depending on Fragment creation clear the menu first.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setHasOptionsMenu(true)
}
override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) {
super.onCreateOptionsMenu(menu, inflater)
inflater?.inflate(Your menu here, menu)
}

Categories

Resources