How to clear/reset a map from outside the fragment? - android

I have a simple app for testing. It has a "basic activity" that Android Studio brings by default. Also a Google maps fragment shown in the basic activity (the code is at the end of the post).
As you all know, the basic activity has a floating button like this:
The main problem is that I'm completely new to working with fragments, and I don't quite understand how they work (I am working on it) but what I want to do is that: by clicking on the floating button, the map is "restarted", reloading again and cleaning it of markers that the user has placed.
What would be the correct way to do this?
I have found several suggestions but I can't get them to work or I can't see how to implement them correctly. One of the ways I have tried is to detach and attach the map fragment, but this causes it to crash and shows no results. The code is the following, which I add in the floating button listener:
val frg : Fragment? = supportFragmentManager.findFragmentById(R.id.map);
val frgTransac = supportFragmentManager.beginTransaction();
if (frg != null) {
frgTransac.detach(frg);
frgTransac.attach(frg);
frgTransac.commit();
}
Another option is to use "googleMap.clear" but I don't know exactly how to get access to that object from the floating button listener.
I hope you can help me with this and, above all, understand how the fragments work and what I am doing wrong.
Main activity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.toolbar))
findViewById<FloatingActionButton>(R.id.fab).setOnClickListener { view ->
// CLEAR MAP FROM HERE
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
}
Map Fragment
class FMaps : Fragment() {
private val callback = OnMapReadyCallback { googleMap ->
val sydney = LatLng(-34.0, 151.0)
googleMap.addMarker(MarkerOptions().position(sydney).title("Marker in Sydney"))
googleMap.moveCamera(CameraUpdateFactory.newLatLng(sydney))
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_f_maps, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mapFragment = childFragmentManager.findFragmentById(R.id.map) as SupportMapFragment?
mapFragment?.getMapAsync(callback)
}
}

You can use custom interface defined in the fragment and then called from the activity when the button is pressed (example how to create interface here: Communicating between a fragment and an activity - best practices). When you press the button and the interface is called, you can just refresh the map using mapFragment?.getMapAsync(callback) inside the fragment.

Related

Fragments UI's overlapping

I'm creating a shopping app where the user can choose next product from the fragment. That will open the next fragment of the same class with more products and so on. I want to use swipe to dismiss behaviour, so when I swipe my finger down I dissmiss the current fragment. It's essential that while swiping the current fragment the user will be able to see the fragment underneath, so I can only use add instead of replace. Here's the problem: When I use add in my fragment manager, the created fragments UI's overlap, which makes the user swipe multiple fragments at once and not show swiping animation. The fragment uses one xml layout so it shares id across all fragments so I have no way of controlling which layouts are disabled. How do I make the user only swipe one fragment/disable touching fragments underneath? The swipe to dissmis is working well with replace but that's not the point. Here's my code.
Activity:
class PullDismissActivity : AppCompatActivity(), PullDismissLayout.Listener {
private lateinit var viewModel: StackingFragmentsViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_pulldismiss)
viewModel = ViewModelProvider(this).get(StackingFragmentsViewModel::class.java)
supportFragmentManager.beginTransaction()
.add(R.id.pull, SwipeFragment(), null)
.addToBackStack(null) // name can be null
.commit()
val layout = findViewById<PullDismissLayout>(R.id.pull)
layout.setListener(this)
}
override fun onDismissed() {
viewModel.popBackStack()
supportFragmentManager.popBackStack()
}
override fun onShouldInterceptTouchEvent(): Boolean {
return false
}
Fragment:
class SwipeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val viewModel = ViewModelProvider(requireActivity()).get(StackingFragmentsViewModel::class.java)
viewModel.addFragment(this.hashCode())
val fragid = view.findViewById<TextView>(R.id.fragid)
fragid?.text = System.currentTimeMillis().toString()
val button = view.findViewById<Button>(R.id.button)
button.setOnClickListener {
requireActivity().supportFragmentManager.beginTransaction()
.add(R.id.pull, SwipeFragment(), null)
.addToBackStack(null)
.commit()
}
}
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment,container,false)
}

switch from one fragment to another in main activity

Example of what i'm trying to achieve https://i.stack.imgur.com/lhp10.png
I'm stuck on the part where i need to switch to createUserFragment from defaultFragment
i attached this to my button, but nothing happens when i press it, not sure what's wrong there
MainActivity
class MainActivity : AppCompatActivity() {
lateinit var appBarConfiguration: AppBarConfiguration
lateinit var navController: NavController
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
navController = findNavController(R.id.hostFragment)
appBarConfiguration = AppBarConfiguration(navController.graph,drawer_layout)
NavigationUI.setupActionBarWithNavController(this, navController,drawer_layout)
NavigationUI.setupWithNavController(navigation_drawer,navController)
}
}
override fun onSupportNavigateUp(): Boolean {
return NavigationUI.navigateUp(navController,appBarConfiguration)
}
defaultFragment
class defaultFragment : Fragment() {
private lateinit var binding: defaultFragmentBinding
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
binding = defaultFragmentBinding.inflate(inflater)
return (binding.root)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.createNewBlankFAB.setOnClickListener{
val transaction = (activity as MainActivity).supportFragmentManager.beginTransaction()
transaction.replace(R.id.defaultFragment, createUserFragment())
transaction.disallowAddToBackStack()
transaction.commit()
}
}
}
My questions are:
1)How can i fix my thing?
2)Will it work properly? I.e no memory leaks or some other funky stuff
3)Do i have to use another fragment for data input or maybe there's another way?
UPDATE
I stil have no idea how this works, but apparently i was replacing wrong fragment, i switched this R.id.defaultFragment in transaction.replace to R.id.hostFragment from which, i assume, all other fragments spawn, but now it just spawn on top of my existing fragment and the drawer button doesn't change its state, i guess i have to either do all of this differently or somehow pass to the drawer navigation information that current fragment was changed?
This is how we navigate within fragments in Navigation component library . We use navigate to then id of the destination Fragment which is defined in navgraph
binding.createNewBlankFAB.setOnClickListener{
Navigation.findNavController(view).navigate(R.id.createUserFragment);
}
I think you have to call "transaction.add" instead of "replace" since you are calling your fragment from an activity. Replace is called when you call a fragment from another fragment, I believe.

kotlin android "OnbackPressed" overrides nothing

I know this already asked a few times, but I still don't get anything after all (I'm quite new in android development).
So i set up my back button in the MainActivity.kt like this:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = DataBindingUtil.setContentView<ActivityMainBinding>(this, R.layout.activity_main)
val navController = this.findNavController(R.id.myNavHostFragment)
NavigationUI.setupActionBarWithNavController(this, navController)
supportActionBar?.setDisplayHomeAsUpEnabled(false)
}
// Set up the back button on action bar
override fun onSupportNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
}
What I want is that this back button is disabled in some fragments, so I tried to override the onBackPressed() function (It is what most people on the internet told) in one of the fragments:
class DashboardFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Declare that this fragment has menu
setHasOptionsMenu(true)
// Set action bar title to "Main Dashboard"
(activity as AppCompatActivity).supportActionBar?.title = "Main Dashboard"
// Binding object for this fragment and the layout
val binding: FragmentDashboardBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_dashboard, container, false)
//Some codes here//
return binding.root
}
// This is where the error occured
override fun onBackPressed() {
super.onBackPressed()
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
super.onCreateOptionsMenu(menu, inflater)
inflater?.inflate(R.menu.nav_overflow_menu, menu)
}
}
But it returns an error saying:
"OnBackPressed" overrides Nothing
Am I missing something? I'm already searching for the solutions but still confused over this.
Who knew... onSupportNavigateUp() works only on 4.0 and above. For below onNavigateUp() is called.
so
override fun onNavigateUp(): Boolean {
val navController = this.findNavController(R.id.myNavHostFragment)
return navController.navigateUp()
}
What you could do is set your nonbackbutton fragments to backstack when inserting them, which is done by
val transaction = supportFragmentManager.beginTransaction()
transaction.addToBackStack(null)
transaction.replace(R.id.frame, fragment) //or add, whatever you use
transaction.commit()
(if you want back button functionality, just skip the addtoBackStack line)
Then, also in your activity, you override the onBackPressed() and check whether the backstack is empty or full. If it is empty, which is checked by
if(supportFragmentManager.backStackEntryCount() == 0)
then you are, for example, on a fragment that supports back button, and just do super.onBackPressed().
On the other hand, if it has something, you can do the navController.navigateUp() part, and when done, pop it using supportFragmentManager.popBackStackImmediate().
I think you could make something like this work, try it, and let us know :)

Kotlin close fragment onclick

I have a fragment that is called from the activity_main screen when a main menu drop down option is clicked. I am trying to have the fragment close and show the activity_main screen again when the cancel button is clicked. I have user input in the activity_main screen that I would like to still be there if the fragment is closed while clicking cancel but at this point i will settle for the fragment just closing and the activity_main showing back at it's original state.
I am using androidx and all of the answers I have found so far have been compatible with android.support.v7
This is the code that i have in my fragment, it does not do anything at all when I click the 'Cancel' button, the fragment just continues to sit there over the activity_main.
class SaveFragment: Fragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
val view: View = inflater.inflate(R.layout.savefragment, container, false)
return view
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
b_cancel.setOnClickListener {
fragmentManager?.popBackStack()
}
}
}
Here is the code that calls the savefragment from the MainActivity:
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.i_save -> {
val SaveFragment = SaveFragment()
supportFragmentManager.beginTransaction().replace(R.id.activity_main_main, SaveFragment).commit()
true
}
R.id.i_recall -> {
val RecallFragment = RecallFragment()
supportFragmentManager.beginTransaction().replace(R.id.activity_main_main, RecallFragment).commit()
true
}
else -> super.onOptionsItemSelected(item)
}
}
Any help at all in Kotlin would be appreciated.

how to make custom back button to go back to certain destination using navigation controller in Android?

I need to make a custom behaviour, when the user press the back button then the user will go to certain destination programatically. I actually have read this Handling back button in Android Navigation Component
but I don't understand how to use that custom back button code.it seems weird to me.
I have tried using this code below
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
fragmentView = inflater.inflate(R.layout.fragment_search_setting, container, false)
// set custom back button
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// navigate to certain destination
Navigation.findNavController(fragmentView).popBackStack(R.id.destination_create_event, false)
}
return fragmentView
}
but I get type mismatch error like this
You must create new Instance of the OnBackPressedCallback abstract class and implement its abstract method .
I hope this helps you:
val callback = requireActivity().onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true){
override fun handleOnBackPressed() {
Navigation.findNavController(fragmentView).popBackStack(R.id.destination_create_event, false)
}
})
// The callback can be enabled or disabled here or in the lambda
}

Categories

Resources