In my main activity I have bottom navigation bar. Each button opens a different fragment.My code in main activity looks like this
class MainActivity : AppCompatActivity() {
private val homeFragment = HomeFragment()
private val calendarFragment = CalendarFragment()
private val addFragment = AddFragment()
private val plannerFragment = PlannerFragment()
private val profileFragment = ProfileFragment()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
replaceFragment(homeFragment)
nav_view.setOnNavigationItemSelectedListener{
when(it.itemId){
R.id.homeButton -> replaceFragment(homeFragment)
R.id.calendarButton -> replaceFragment(calendarFragment)
R.id.addButton -> replaceFragment(addFragment)
R.id.plannerButton -> replaceFragment(plannerFragment)
R.id.profileButton -> replaceFragment(profileFragment)
}
true
}
}
private fun replaceFragment(fragment: Fragment){
if (fragment!=null){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragmentContainer, fragment)
transaction.commit()
}
}
}
In profileFragment I have a button that opens a new activity called EditProfile. In that activity I have a button called goBackToProfileButton
I want to set a listener that will go back to mainactivity, but I want profileFragment to be open not the default fragment which is homeFragment.
goBackToProfileButton.setOnClickListener {
val intent = Intent(this,MainActivity::class.java)
startActivity(intent)
}
For now my code looks like this
You need to add some bundle data to your intent with information which fragment should be started
https://stackoverflow.com/a/819427/11538132
And then when you receive this bundle data then you can manually set your ** profileFragment**
You can add TAG while fragment transaction.
private fun replaceFragment(fragment: Fragment){
if (fragment!=null){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.fragmentContainer, fragment, "FragmentTag")
transaction.commit()
}
}
While calling the first time to replace a fragment, find your fragment by tag.
//replaceFragment(homeFragment)
val fragment = supportFragmentManager.findFragmentByTag("FragmentTag")
if(fragment !=null){
replaceFragment(fragment)
} else{
replaceFragment(homeFragment)
}
if findFragmentByTag return null means no fragment was added. So add Home Fragment. If not null , it will update with last fragment added.
You could use fragment.startActivityForResult() and call the ProfileFragment from the MainActivity once it receives the onActivityResult() callback.
Related
private val dashboardFragment = DashboardFragment
private val settingsFragment = SettingsFragment
private val infoFragment = InfoFragment
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityHomePageBinding.inflate(layoutInflater)
setContentView(binding?.root)
replaceFragment(dashboardFragment)
}
private fun replaceFragment(fragment: Fragment){
if(fragment != null){
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.frag_container, fragment)
transaction.commit()
}
}
I have 3 fragments setup in my project namely - Dashboard, settings and Info. I am getting an error for type mismatch everytime I enter my fragment name in the function replaceFragment. I am referring to this video - https://www.youtube.com/watch?v=v8MbOjBCu0o&t=1s
You need to instantiate these fields . right now they r just pointing to the class type . Its similar to new keyword in java .
private val dashboardFragment = DashboardFragment()
private val settingsFragment = SettingsFragment()
private val infoFragment = InfoFragment()
I have two Fragments A and B with RecyclerView list.
When fragment A is active I call:
fragmentTransaction.hide(Frag_A);
and I add fragment B with command:
fragmentTransaction.add(R.id.fragment_container, Frag_B);
When fragment B is active and I do some changes I need to silently call:
mAdapter.notifyDataSetChanged();
on Fragment A.
So it is possible to call this, when fragment is hidden?
Now I am using
#Override
public void onHiddenChanged(boolean hidden) {
super.onHiddenChanged(hidden);
}
But this method is triggered when I am going back from fragment B to A.
I want to refresh fragment A when fragment B is active. (User see fragment B list).
How to do that?
From my understanding, you want to refresh data in FragmentA while FragmentB is active.
First, you have create an Interface
interface FragmentCallback {
fun onResumeFragment()
}
Next, before you want to go to FragmentA, you are using add method. So, you have to add a tagfor FragmentA.
private fun loadFragmentA(someId: Int) {
val bun = Bundle()
bun.apply {
putInt(Constant.SOME_ID, someId)
}
val fragmentA = FragmentA()
fragmentA.arguments = bun
val fragmentManager = supportFragmentManager
val fragmentTransaction: FragmentTransaction =
fragmentManager.beginTransaction()
fragmentTransaction.add(
R.id.fragment_container,
fragmentA,
"TagFragmentA"
)
fragmentTransaction.commit()
}
At the MainActivity, you need to implement the interface that we created. inside the onResumeFragment method, we have to find that FragmentA by using tag. So, we can use the method inside that fragment.
class MainActivity : AppCompatActivity(), FragmentCallback {
override fun onResumeFragment() {
val fragmentA =
supportFragmentManager.findFragmentByTag("TagFragmentA") as FragmentA
fragmentA.refreshData()
}
}
Inside FragmentA, create refreshData method and make it public. So inside this method, you can notify the data changed.
fun refreshData(){
viewModel.getListSomething()
}
Inside FragmentB, you need to override the onAttach method to make the object for FragmentCallback.
private var fragmentCallback: FragmentCallback? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is FragmentCallback) {
fragmentCallback = context
} else {
throw RuntimeException(
context.javaClass.simpleName
.toString() + " must implement FragmentCallback"
)
}
}
Finally create a method to trigger when there is data changes.
private fun timeToUpdateDataAtFragmentA(){
fragmentCallback?.onResumeFragment()
}
When you from FragmentB, back press to FragmentA, the data will update at the FragmentA.
I have a main activity which has a frame and two buttons. Clicking on each button displays a fragment.
App Imageļ¼
Code:
package com.example.activitylifecycle
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import androidx.fragment.app.Fragment
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val firstFragment = FirstFragment()
val secondFragment = SecondFragment()
supportFragmentManager.beginTransaction().apply {
replace(R.id.flFragment, firstFragment)
commit()
}
val btnFragmentOne = findViewById<Button>(R.id.btnFragment1)
val btnFragmentTwo = findViewById<Button>(R.id.btnFragment2)
btnFragmentOne.setOnClickListener {
supportFragmentManager.beginTransaction().apply {
replace(R.id.flFragment, firstFragment)
addToBackStack(null)
commit()
}
}
btnFragmentTwo.setOnClickListener {
supportFragmentManager.beginTransaction().apply {
replace(R.id.flFragment, secondFragment)
addToBackStack(null)
commit()
}
}
}
}
I am using addToBackStack() function to make sure that when someone has pressed the fragment2 button and after that they press back button the fragment1 is shown. It's working perfectly.
But when I press the fragment2 button 5 times. I have to press back button 5 times to get to fragment1.
How can I check if the fragment is already in the stack before adding it again?
There is one way, that before creating some kind of fragment it is better to check if this fragment is active and visible on the screen. If it is active, a new fragment of that class will not be created. That may solve your problem
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val firstFragment = FirstFragment()
val secondFragment = SecondFragment()
//First Fragment class name entered as Tag
supportFragmentManager.beginTransaction().apply {
replace(R.id.flFragment, firstFragment, firstFragment.javaClass.name)
commit()
}
val btnFragmentOne = findViewById<Button>(R.id.btnFragment1)
val btnFragmentTwo = findViewById<Button>(R.id.btnFragment2)
btnFragmentOne.setOnClickListener {
val fragment1 =
supportFragmentManager.findFragmentByTag(FirstFragment::class.java.name) as FirstFragment?
// Checking is First Fragment is not active
if (!(fragment1 != null && fragment1.isVisible)) {
supportFragmentManager.beginTransaction().apply {
//First Fragment class name entered as Tag
replace(R.id.flFragment, firstFragment, firstFragment.javaClass.name)
addToBackStack(null)
commit()
}
}
}
btnFragmentTwo.setOnClickListener {
val fragment2 =
supportFragmentManager.findFragmentByTag(SecondFragment::class.java.name) as SecondFragment?
// Checking is Second Fragment is not active
if (!(fragment2 != null && fragment2.isVisible)) {
supportFragmentManager.beginTransaction().apply {
//Second Fragment class name entered as Tag
replace(R.id.flFragment, secondFragment, secondFragment.javaClass.name)
addToBackStack(null)
commit()
}
}
}
}
Hope that will help to you
When you add it to the stack, it gets added everytime you press the button.
So if you press fragment2button 5 times, it gets added 5 times to the stack, and you need to press backbutton 5 times to remove all 5 of those from the stack and get back to fragment1
You need to edit the code such that if the fragment already exists, then it shouldn't be added to the stack again
I have the following method
// Change fragment extension function to handle navigation easily
fun changeFragment(fragmentManager: FragmentManager?, #IdRes containerId: Int, fragment: Fragment?, addToBackStack: Boolean = false) {
if (fragmentManager == null || fragment == null) return
val fragmentTransaction = fragmentManager.beginTransaction()
if (addToBackStack) fragmentTransaction.addToBackStack(null)
fragmentTransaction.replace(containerId, fragment, fragment::class.java.simpleName).commit()
}
and I am using it inside one of my fragments -
class InspectionFragment : Fragment(R.layout.fragment_inspection) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
inspectionAdapter = InspectionAdapter { inspection ->
changeFragment(
parentFragmentManager, R.id.fragment_inspection_frame_layout,
InspectionDetailsFragment.newInstance(inspection), true
)
}
}
}
class InspectionDetailsFragment : Fragment() {
companion object {
fun newInstance(inspection: Inspection): InspectionDetailsFragment {
val inspectionDetailsFragment = InspectionDetailsFragment()
val bundle = Bundle()
bundle.putParcelable(GeneralConstants.INSPECTION, inspection)
inspectionDetailsFragment.arguments = bundle
return inspectionDetailsFragment
}
}
}
The thing is that, when I instansiate onetop of the 'InspectionDetailsFragment' layer another fragment and press back, it goes directly back to the 'InspectionFragment' parent.
I can't understand why this is happening.
Anyone has an idea?
When you call changeFragment function, you're passing true as last argument:
changeFragment(
parentFragmentManager, R.id.fragment_inspection_frame_layout,
InspectionDetailsFragment.newInstance(inspection), true
)
In changeFragment function:
if (addToBackStack) fragmentTransaction.addToBackStack(null)
It doesn't get added to backStack. Instead of null i think you should pass String value as the name of the backStack you want to use.
I'm not 100% sure about this. You can read more about fragment transactions from here: https://developer.android.com/guide/fragments/transactions
I have an activity with two fragments added to it using :
fun addFragment(fragment: Fragment){
private val fragmentManager = supportFragmentManager
val fragmentTag = fragment::class.simpleName.toString()
val ft = fragmentManager.beginTransaction()
ft.add(R.id.fragments_container, fragment,fragmentTag)
ft.addToBackStack(fragmentTag)
ft.commit()
}
I add two fragments A and B to activity with this order:
A ---> B
when i press back button on phone it return from B to A as expected
but the problem is that when i call activity's onBackPressed method when clicking on a view for example:
imgBack.setOnClickListener {
onBackPressed()
}
it does not work like when i press back button on the phone
it returns to fragment A but not showing fragment A views as expected.
onBackPressed:
override fun onBackPressed() {
if (fragmentManager.backStackEntryCount > 1) {
fragmentManager.popBackStack()
} else {
finish()
}
}
if you imgBack are in fragmentB call
imgBack.setOnClickListener {
activity?.onBackPressed()
}