Go back to fragment of Activity A from Activity B - java

In my app, am using a navigation drawer (in Fragment A) to navigate to fragments:
public void displayView(int viewId){
Fragment fragment = null;
String title = getString(R.string.app_name);
switch (viewId) {
case R.id.nav_menu:
fragment = new MenuFragment();
title = getString(R.string.menu_title);
viewIsAtHome = false;
break;
case R.id.nav_reservation:
fragment = new ReservationFragment();
title = getString(R.string.reservation_title);
viewIsAtHome = false;
break;
...
if (fragment != null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame, fragment);
ft.commit();
}
// set the toolbar title
if (getSupportActionBar() != null) {
getSupportActionBar().setTitle(title);
}
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
drawer.closeDrawer(GravityCompat.START);
}
}
displayView() is called in onNavigationItemSelected:
#Override
public boolean onNavigationItemSelected(MenuItem item) {
// Handle navigation view item clicks here.
displayView(item.getItemId());
return true;
}
Now, in ReservationFragment I am displaying a list of reservations and a FloatingActionButton to start Activity B where the user can add a reservation if there are no reservations. When the user is done adding a reservation, I want to display it in the Reservation fragment. This requires me to "go back" to the Fragment.How do I accomplish this since Activity B knows nothing about Activity A?
What I've tried:
I tried creating a method in Activity A like this:
public void navigateToFragment(int viewId) {
displayView(R.id.nav_reservation);
}
and then called this method from Activity B:
saveButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
new MainActivity().navigateToFragment(R.id.nav_reservation);
//MainActivity is Activity A
}
});
the app crashes due to a nullPointerException in displayView() from the line:
String title = getString(R.string.app_name);
This isn't surprising since am creating a new MainActivity object that knows nothing about the previous state of the Activity, right?
This question mirrors my problem but is based on Settings so I can't really use the answer in my case.
How do I accomplish this task?

There are three quick methods that come to my mind. First you can start activityB with startActivityForResult and handle the result in activityA after user does what he wants in activityB. Second you can set activityA as singleTop and before finishing activityB you can startActivityA with clearTop an intent flag called clear_top(https://developer.android.com/reference/android/content/Intent.html#FLAG_ACTIVITY_CLEAR_TOP).
Last but not the least, you can connect two activity by binding service in both activities and communicate via that service that you bound.

Related

Fragment not Opening Previous Fragment on Back Button

I have been struggling to implement a super simple app layout, where my MainActivity opens Fragment#1 in its onCreate method, then the Fragment#1 opens Fragment#2 when an item is clicked.
As of right now, when I open Fragment#1 from my MainActivity, I add Fragment#1 to the BackStack. After opening Fragment#2, when I hit the back button the first click does nothing, then the second click sends me all the way back to my login page, skipping past Fragment #1 and MainActivity.
How can I make it so when I hit the back button on Fragment#2, it opens Fragment#1 back up?
(MainActivity opens Fragment#1)
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivity";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
SearchListFragment fragment = new SearchListFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.main_fragment_container, fragment);
transaction.addToBackStack(TAG);
transaction.commit();
}
}
(Fragment#1 opens Fragment#2)
public class SearchListFragment extends Fragment {
public void viewResults(SearchModel search) {
Bundle args = new Bundle();
args.putString("ID", search.getId());
ResultsFragment fragment = new ResultsFragment();
fragment.setArguments(args);
FragmentTransaction transaction = getParentFragmentManager().beginTransaction();
transaction.replace(R.id.main_fragment_container, fragment);
transaction.commit();
}
}
EDIT
I should have mentioned that I have tried to handle the back press event myself. I tried adding this to my MainActivity but it did not change the behavior:
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
this.finish();
} else {
getSupportFragmentManager().popBackStack();
}
}
onCreate with
Kotlin
val backpress = requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, true) {
// Handle the back button event
}
Java
OnBackPressedCallback callback = new OnBackPressedCallback(true /* enabled by default */) {
#Override
public void handleOnBackPressed() {
// Handle the back button event
}
});
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
You can provide your back operations in the relevant sections.
getSupportFragmentManager().beginTransaction().replace(R.id.frag_frame, fragment).addToBackStack("text").commit();

Add event when back arrow(getSupportActionBar().setDisplayHomeAsUpEnabled(true)) is clicked

My problem is almost the same as this Stop AsyncTask in Fragments when Back Button is pressed
But I want to stop my AsyncTask when the back arrow is clicked. I have a code in stoping asynctask and it works when I implemented it in another way. I tried what I researched so far but I still got errors. Please help me with this.
I tried this code
#Override
public boolean onOptionsItemSelected(MenuItem item) {
int id=item.getItemId();
if(id==android.R.id.home)
{
Intent returnIntent = new Intent();
returnIntent.putExtra("flag",userid);
setResult(Activity.RESULT_OK,returnIntent);
finish();
return true;
}}
UPDATED:
I am using this code to go to another fragment.
Fragment2 fragmentChild = new Fragment2 ();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.add(R.id.content, fragmentChild);
transaction.addToBackStack(null);
transaction.commit();
I have no problem in my backtrack when I go to another fragment. Then I'm using this code (getSupportActionBar().setDisplayHomeAsUpEnabled(true)) in my (Drawer.java) to show the back arrow. Now I want to add event when I clicked back arrow.
EDIT: (Drawer.java)
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(
this, drawer, toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close);
drawer.setDrawerListener(toggle);
toggle.syncState();
final View.OnClickListener originalToolbarListener = toggle.getToolbarNavigationClickListener();
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
toggle.setDrawerIndicatorEnabled(false);
toggle.setToolbarNavigationClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
getSupportFragmentManager().popBackStack();
}
});
} else {
toggle.setDrawerIndicatorEnabled(true);
toggle.setToolbarNavigationClickListener(originalToolbarListener);
}
}
});
If you properly setup the action bar, then you should be able to achive that with the following:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Add the code to stop the task
break;
}
return super.onOptionsItemSelected(item);
}
EDIT: After you are able to handle the back button event, you can add the code to close the tasks in fragments. First of all, I created an interface that is implement by all fragments that should be able to close this tasks.
public interface TaskCancelFragment {
void cancelTask();
}
The implementation in fragments should look something like this:
#Override
public void cancelTask() {
//Add the code to cancel the task
Toast.makeText(getContext(), "The task is canceled", Toast.LENGTH_SHORT).show();
}
The final step is to send the event from activity to fragment. Here I will assume some things since I don't have access to your code to see how you add/find the fragments, but fortunately this doesn't matter so much and is easy to change. In addition to the initial answer, I created a new method in activity to find the current fragment and close the task.
private void cancelTasks() {
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.placeholder);
if (fragment instanceof TaskCancelFragment) {
((TaskCancelFragment) fragment).cancelTask();
}
}
And in the end, the case android.R.id.home looks like this:
case android.R.id.home:
cancelTasks();
onBackPressed(); // I think that you want to close the activity too
break;

Implementing back stacks with fragments

I'm attempting to implement a back stack while using fragments, but when using the Back button, I keep getting taken out of the app to the home screen.
Activity opens fragment A; Fragment A has a clickable TextView that opens fragment B (this works). Hitting BACK should return me to fragment A, but it takes me to the home screen instead.
Here is the activity's call to the opening of fragment A in onCreate:
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fragment_layout_container, new FragmentLogin(), "fragment_login")
.addToBackStack("login_screen")
.commit();
Log.d("Back", getFragmentManager().getBackStackEntryCount() +" <- Entry Count at LoginActivity.onCreate" );
At this point, the Log prints 0 <- Entry Count at LoginActivity.onCreate. Something I've done wrong keeps this from printing 1.
Then, the Fragment A has this listener:
forgottenPassword.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_layout_container, new FragmentForgottenPassword(), "fragment_password")
.addToBackStack("forgotten_password")
.commit();
Log.d("Back", getFragmentManager().getBackStackEntryCount() + " <- Entry Count at FragmentLogin.onCreateView.Listener");
}
});
The Log here prints 1 <- Entry Count at FragmentLogin.onCreateView.Listener. Here, the listener works and opens fragment B - but the back button returns me to the home screen.
Use this in your Activity it should pop out the fragments already added to backstack
#Override
public void onBackPressed()
{
if (getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
Try Like This,
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
} else {
getFragmentManager().popBackStack(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
transaction.replace(R.id.fragment_layout_container, fragment);
transaction.commitAllowingStateLoss();
getFragmentManager().executePendingTransactions();
}
and Used it like this,
replaceFragment(new FragmentForgottenPassword(), true);
There is a GitHub library that will do this work for you!https://github.com/rathodchintan/Fragment-Back-StackWhenever you are displaying any new fragment, just push that fragment into stack using following code.
//here this fragment is our first fragment
homeListFragment = new HomeListFragment();
fragmentStack.push(homeListFragment);It has many other stack options too.

Listen to "back" key pressed in a Fragment

I use a Fragment to let the user enter information in my Android application. If information has been entered, I want a warning to be sent to the user if he presses the "back" key. If he confirms he wants to abandon the changes, the Fragment has to be popped. If he cancels the abandon, the Fragment should remain as it is.
I already tried to implements onKeyListener to my Fragment with an onKey callback function. But it is never executed.
How can I do?
Try something like this. (I haven't been able to test this)
In your activity, override onBackPressed():
#Override
public void onBackPressed() {
Fragment frag = getSupportFragmentManager().findFragmentById(R.id.your_container);
if(frag instanceof TheFragmentYouAreTalkingAbout) {
((TheFragmentYouAreTalkingAbout)frag).showConfirmGoBackDialog();
} else {
super.onBackPressed();
}
}
The showConfirmGoBackDialog() method should create a dialog box. In this dialog you can put a listener on the confirm/OK button that will call getFragmentManager().popBackStack(); if pressed, otherwise do nothing.
I would prefer not to use instanceof comparison.
So my solution is to provide a tag when commiting fragment transaction. Then you can find fragment by tag:
private void showMyFragment(){
Fragment fragment = new MyFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, fragment, MY_FRAGMENT_TAG);
ft.commit();
}
#Override
public void onBackPressed() {
MyFragment fragment = (MyFragment)findFragmentByTag(MY_FRAGMENT_TAG);
if (fragment != null) {
fragment.showConfirmation
} else {
super.onBackPressed();
}
}

Replacing a fragment with another fragment inside activity group

I have a fragment inside a group activity and I want to replace it with another fragment:
FragmentTransaction ft = getActivity().getFragmentManager().beginTransaction();
SectionDescriptionFragment bdf = new SectionDescriptionFragment();
ft.replace(R.id.book_description_fragment, bdf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
It works fine when it is done as a seperate project without using activity group, every thing works fine in log cat as control goes inside getview(), but no view is visible, not even any exception arises, I want the book detail fragment to be replaced by section detail fragment.
Xml of book detail fragment has id book_description_fragment and xml for section description fragment has id section_description_fragment.
The above code is in onClick method of an item, I want that when user taps on an item in horizontal scroll view, then the fragment changes.
Fragments that are hard coded in XML, cannot be replaced. If you need to replace a fragment with another, you should have added them dynamically, first of all.
Note: R.id.fragment_container is a layout or container of your choice in the activity you are bringing the fragment to.
// Create new fragment and transaction
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
Please see this Question
You can only replace a "dynamically added fragment".
So, if you want to add a dynamic fragment, see this example.
I've made a gist with THE perfect method to manage fragment replacement and lifecycle.
It only replace the current fragment by a new one, if it's not the same and if it's not in backstack (in this case it will pop it).
It contain several option as if you want the fragment to be saved in backstack.
=> See Gist here
Using this and a single Activity, you may want to add this to your activity:
#Override
public void onBackPressed() {
int fragments = getSupportFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
finish();
return;
}
super.onBackPressed();
}
Use the below code in android.support.v4
FragmentTransaction ft1 = getFragmentManager().beginTransaction();
WebViewFragment w1 = new WebViewFragment();
w1.init(linkData.getLink());
ft1.addToBackStack(linkData.getName());
ft1.replace(R.id.listFragment, w1);
ft1.commit();
Use ViewPager. It's work for me.
final ViewPager viewPager = (ViewPager) getActivity().findViewById(R.id.vp_pager);
button = (Button)result.findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
viewPager.setCurrentItem(1);
}
});
hope you are doing well.when I started work with Android Fragments then I was also having the same problem then I read about
1- How to switch fragment with other.
2- How to add fragment if Fragment container does not have any fragment.
then after some R&D, I created a function which helps me in many Projects till now and I am still using this simple function.
public void switchFragment(BaseFragment baseFragment) {
try {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(android.R.anim.slide_in_left, android.R.anim.slide_out_right);
if (getSupportFragmentManager().findFragmentById(R.id.home_frame) == null) {
ft.add(R.id.home_frame, baseFragment);
} else {
ft.replace(R.id.home_frame, baseFragment);
}
ft.addToBackStack(null);
ft.commit();
} catch (Exception e) {
e.printStackTrace();
}
}
enjoy your code time :)
you can use simple code its work for transaction
Fragment newFragment = new MainCategoryFragment();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.content_frame_NavButtom, newFragment);
ft.commit();
You Can Use This code
((AppCompatActivity) getActivity()).getSupportFragmentManager().beginTransaction().replace(R.id.YourFrameLayout, new YourFragment()).commit();
or You Can This Use Code
YourFragment fragments=(YourFragment) getSupportFragmentManager().findFragmentById(R.id.FrameLayout);
if (fragments==null) {
getSupportFragmentManager().beginTransaction().replace(R.id.FrameLayout, new Fragment_News()).commit();
}
I change fragment dynamically in single line code
It is work in any SDK version and androidx
I use navigation as BottomNavigationView
BottomNavigationView btn_nav;
FragmentFirst fragmentFirst;
FragmentSecond fragmentSecond;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_search);
fragmentFirst = new FragmentFirst();
fragmentSecond = new FragmentSecond ();
changeFragment(fragmentFirst); // at first time load the fragmentFirst
btn_nav = findViewById(R.id.bottomNav);
btn_nav.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem menuItem) {
switch(menuItem.getItemId()){
case R.id.menu_first_frag:
changeFragment(fragmentFirst); // change fragmentFirst
break;
case R.id.menu_second_frag:
changeFragment(fragmentSecond); // change fragmentSecond
break;
default:
Toast.makeText(SearchActivity.this, "Click on wrong bottom SORRY!", Toast.LENGTH_SHORT).show();
}
return true;
}
});
}
public void changeFragment(Fragment fragment) {
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_layout_changer, fragment).commit();
}
In kotlin you can do:
// instantiate the new fragment
val fragment: Fragment = ExampleFragment()
val transaction = supportFragmentManager.beginTransaction()
transaction.replace(R.id.book_description_fragment, fragment)
transaction.addToBackStack("transaction_name")
// Commit the transaction
transaction.commit()
This will work if you're trying to change the fragment from another fragment.
Objects.requireNonNull(getActivity()).getSupportFragmentManager()
.beginTransaction()
.replace(R.id.home_fragment_container,new NewFragment())
NOTE As stated in the above answers, You need to have dynamic fragments.
You can use fragment-ktx
// If you are in fragmet
childFragmentManager.beginTransaction()
// or if you are in activiry
supportFragmentManager.beginTransaction()
// Create and commit a new transaction
supportFragmentManager.commit {
setReorderingAllowed(true)
// Replace whatever is in the fragment_container view with this fragment
replace<ExampleFragment>(R.id.fragment_container)
}
To replace a fragment with another one do this, Note that R.id.fragment comes from the id that you give to the first tag of the fragment in the XML.
barAudioPlaying.setOnClickListener(view -> {
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragment,new HomeFragment())
.commit();

Categories