I have this scenario in my app:
One activity(A) with multiple fragments calling another activity(B) at some point.
Flow for that goes like this: F1 => F2 => F3 => B or F1 => F2 => B where F(n) represents fragment.After I finish activity B it returns me to F3 or F2 but my goal is to show user F1 so I tried sending event via event bus and replacing any other fragment with F1,note that I'm adding every fragment to backstack.So I succeed with it but if I call fragment F2 or F3 application crashes also sometimes I get "IllegalStateException: Can not perform this action after onSaveInstanceState".
So after trying a lot of approaches I simply did this :
public void onClick(View v){
//started activity B
//replaced current fragment with F1
}
The end result of this was seeing F1 before activity B ,and everything else worked fine without crashing.So to solve that glitch I replaced fragment 100 ms after activity B is started.
public void onClick(View v){
//started activity B
new Handler().(new Runable(){
#Override
public void run()
{
//replaced current fragment with F1
}
},100);
}
But I feel this is ugly way to solve this problem and I want to ask you if there is better solution?
EDIT:
I was inspired by spcial answer so I did similar thing with states.
In activity A I have two variables.
boolean wasAnotherActivityCalled=false;
String showFragment=null;
In my fragment I have this :
public void onClick(View v){
//started activity B
getActivity().wasAnotherActivityCalled=true;
getActivity().showFragment=FragmentOne.class.getSimpleName();
}
in activity A I have this :
#Override
protected void onResumeFragments() {
super.onResumeFragments();
if(wasAnotherActivityCalled)
{
if(showFragment.equals(F1.class.getSimpleName()))
{ //do your logic here}
wasAnotherActivityCalled=false;
showFragment=null;
}
}
I have something similiar in my App. What I did was to use a simple "state-machine" where I have an int attribute which represents the current state (0 = Fragment1, 1 = Fragment2...etc) and an ArrayList with all fragments which I need. If I have to switch the fragment I also increment the state and just load the fragment from the ArrayList.
In my onPause() method I save the state in sharedPreferences() and in the onResume() method I load the state from sharedPreferences() and do a initFragment(state) where I just replace the fragmentLayout with the fragment from fragmentArray[state] :-)
With this behaviour I could handle backstack on my own, can go back and furth and save the state of the current needed fragment everytime the user changes the activity and comes back. Furthermore I don't commit the fragments into backstack because it is already handled through myself.
I hope I could help you with this.
Dont add your fragment into backstack while commiting it
Related
Let's say I got a fragment page called "A" and "B" and an activity called "C".
When I click the add button in Fragment B, it will takes the user to activity C. When the user input all the information in activity B and press submit, it will take the user back to fragment B and reload it.
My situation is I use the finish() function on "C" while I press the submit but when it go back to the fragment B it doesn't refresh.
My code:
private View.OnClickListener mSubmitButtonOnClick = new View.OnClickListener(){
#Override
public void onClick(View v)
{
if(getArray() != null) {
insertRecord(getArray());
finish();
}
else{
Toast.makeText(AddClaimFormPage.this, "Please select at least one item to claim.", Toast.LENGTH_SHORT).show();
}
}
};
Please help, how can I reload the fragment B when I used finish()?
You need to override the onResume() on fragment B:
#Override
public void onResume()
{
super.onResume();
// Load data and do stuff
}
Refer Fragment Lifecycle for more details.
you have to manage stake of fragment and when you back from another activity to previous activity than load fregment which is the top on stack.
Need your help to figure out what is the best approach to achieve the following:
Activity -> Fragment A -> Fragment B.
When a user is on fragment B, and he clicks save. I want it to Remove/popBackStack Fragment B and return to Fragment A with the data passed from Fragment B.
Thanks in advance!
you could define interface and attribute in Fragment B,such as:
public interface OnSelectedListener {
void onSelected(String s);
}
private OnSelectedListener mOnSelectedListener;
public void setOnSelectedListener(OnSelectedListener onSelectedListener) {
mOnSelectedListener = onSelectedListener;
}
when Fragment A -> Fragment B:
new OnSelectedListener();
when click save in Fragment B
onSelectedListener.onSelected("");
other method : DataBase, SharedPreferencesHelper
Try to use
LocalBroadcastManager
Web Reference with Example -
https://gist.github.com/Antarix/8131277
The quickest solution would be to save the desired values in onPause or onStop of your fragment-B using any preferred storage method (like SharedPreferences or SQLite) and load them back in the onResume or onStart of fragment-A.
or replace the save part by introducing a stub method in your Activity which takes the Bundle of values and pass-on to the other fragment.
I'm able to start a Fragment from an Activity.
However Im worried about potential problems with my implementation.
I have two fragments, FragmentA and FragmentB
And I have 3 activity classes, Activity1, Activity2, ResultActivity
public class NavigationTabs extends FragmentActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.simple_tabs_2);
FragmentStatePagerAdapter adapter = new MyTabs(getSupportFragmentManager());
....
....
}
static class MyTabs extends FragmentStatePagerAdapter {
public MyTabs(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch(position)
{
case 0:
FragmentA FragA = new FragmentA();
return FragA;
case 1:
FragmentB FragB = new FragmentB();
return FragB;
......
......
}
}
^How I call FragmentA and FragmentB
FragmentA starts Activity1 via an intent.
Intent intent = new Intent(getActivity(), Activity1.class);
startActivity(intent);
Activity1 then passes the results of a counter to ResultActivity
ResultActivity starts(or returns to) FragmentA and sets SharedPreferences via onClick like this
public void onClick(View v) {
if(v.getId()== R.id.button_1){
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
Editor edit = sp.edit();
edit.putInt(passedSavePref, counter);
edit.commit();
finish();
}
}
FragmentB starts Activity2 via an intent.
Intent intent = new Intent(getActivity(), Activity2.class);
startActivity(intent);
Activity2 then passes the results of a counter to ResultActivity
ResultActivity starts(returns to) FragmentB and sets SharedPreferences via onClick like this
public void onClick(View v) {
if(v.getId()== R.id.button_1){
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(this);
Editor edit = sp.edit();
edit.putInt(passedSavePref, counter);
edit.commit();
finish();
}
}
This all works for my needs. GC seems fine, its freeing and allocating memory.
ResultActivty returns to the correct Fragment, and it sets the SavedPreferences correctly.
Yet it seems very bad implementation.
For starters, while searching through other questions I've read, "Don't start an Activity directly from a Fragment" the poster also linked to the proper implementation here https://developer.android.com/guide/components/fragments.html#EventCallbacks
I've tried calling Activity1 from FragmentA like this, but I don't really see a difference in behavior or performance
Intent intent = new Intent(getActivity(), Activity1.class);
startActivity(intent);
getActivity().getSupportFragmentManager().popBackStack();
So my question is, do I need to finish/remove FragmentA when I start Activity1, then start FragmentA again from ResultActivityusing something like
FragmentTransaction transaction = getFragmentManager().beginTransaction();
Thanks in advance.
EDIT So what I was trying to was to either kill/finish/pop FragmentA so that I could re-start it from ResultActivity.
The reason I was tying to do that was because my savedPreferences were not loading when I was going back to FragmentA from ResultActivity.(well they were saving and loading correctly, but I couldn't see them)
As I understand it from the docs,Fragments go on pause. So calling my loadPreferences method onResume(); loaded my SavedPreferences.
Not marking this as an answer, because I did not implement any of the standard/proper practices of dealing with Fragments popBackStack(); FragmentTransactions etc
Quote: "Don't start an Activity directly from a Fragment"
I read the poster who wrote this, and I strongly disagree. His rationale is that it reduces the modularity of the fragment, and he believes you should impelment an interface to call back to the activity.
I disagree. It doesn't reduce modularity, in fact it increases it. Why implement a layer of abstraction to do something the fragment is intended to do in every implementation? Why re-write the same code in every activity, when it can be modularized in the fragment? In fact fragments wouldn't have their own specialized functions for starting activities if this was against design principles.
For instance, if you start an activity using fragment.startAcitivtyForResult(), the fragment is put directly as the onActivityResult receiver. Not so if you use your activity instance to directly start the new activity.
You do not need to remove framgents before starting a new Activity
Android will pause/stop your fragments, and potentially destroy them and the underlying activity as well if need be. Android is constantly destroying and recreating activities and fragments, like every time you change the orientation on your screen - the default settings has your activity and fragments commit mass suicide.
This is why functions like OnSaveInstanceState() are so important, because they let your activities and fragments return to a saved state. The general rule of android programming is your activity / fragments should be able to respond to spontaneous death gracefully.
I have a two class named Stock Details extends Fragment and another one is Stock info extends activity,
when I was trying to go back to my Stock details pages from Stock info pages it shows "Unfortunately,Application has stopped",but in my program I use
Intent intent = new Intent(this,StockDetails.class);
startActivity(intent);
finish();
for go back to fragment. but it does not work. help me to solve this problem
I am the beginner for android.
kindly help me to go back from an Activity to fragment
you do not need to start activity for StoreDetail as it is a subclass of Fragment not Activity.This is the reason that your app is crashed.
Now moving to your question if you want to go back to the fragment from the activty : Stock INFO you just need to call finish() it will finish the current activity(Stock Info) and the fragment which is in background will be resumed.I had same problem and solved by this way .This is the onCreate Method of Activity(in your case it is for StockInfo class).Have a look:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.setmCallBack(new IResultCallback() {
#Override
public void result(Result lastResult) {
if (lastResult!=null) {
finish();// by this line I have killed the current activity
}
else
{
Toast.makeText(getActivity(), "NotScan: ", Toast.LENGTH_SHORT).show();
}
}
});
}
Ok look,
startActivity(intent);
this method name startActivity. So it will start another activity not fragment.
You can See & read the fragment:
http://developer.android.com/guide/components/fragments.html
Add fragment to go back to manually to the backstack.
FragmentTransaction transaction = getFragmentManager().beginTransaction();
YourFragmentName myFragment = new YourFragmentName();
transaction.replace(R.id.fragment_container, myFragment);
transaction.addToBackStack(null);
transaction.commit();
And There is several methods here. Just take a look:-
http://developer.android.com/reference/android/app/FragmentManager.html#popBackStack()
If you want to go back to the fragment , you can do this :
getFragmentManager().popBackStack();
I'm converting an app to use fragments using the compatibility library.
Now currently I have a number of activities (A B C D) which chain onto one another, D has a button 'OK' which when pressed calls finish which then bubbles up through onActivityResult() to additionally destroy C and B.
For my pre Honycomb fragment version each activity is effectively a wrapper on fragments Af Bf Cf Df. All activities are launched via startActivityForResult() and onActivityResult() within each of the fragments can happily call getActivity().finish()
The problem that I am having though is in my Honeycomb version I only have one activity, A, and fragments Bf, Cf, Df are loaded using the FragmentManager.
What I don't understand is what to do in Df when 'OK' is pressed in order to remove fragments Df, Cf, and Bf?
I tried having the fragment popping itself off the stack but this resulted in an exception. onActivityResult() is useless because I have not loaded up the fragment using startActivityForResult().
Am I thinking about this completely the wrong way? Should I be implementing some sort of listener that communicates with either the parent fragment or activity in order to do the pop using the transaction manager?
While it might not be the best approach the closest equivalent I can think of that works is this with the support/compatibility library
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
or
getActivity().getFragmentManager().beginTransaction().remove(this).commit();
otherwise.
In addition you can use the backstack and pop it. However keep in mind that the fragment might not be on the backstack (depending on the fragmenttransaction that got it there..) or it might not be the last one that got onto the stack so popping the stack could remove the wrong one...
You can use the approach below, it works fine:
getActivity().getSupportFragmentManager().popBackStack();
What I don't understand is what to do in Df when 'OK' is pressed in order to remove fragments Df, Cf, and Bf?
Step #1: Have Df tell D "yo! we got the OK click!" via calling a method, either on the activity itself, or on an interface instance supplied by the activity.
Step #2: Have D remove the fragments via FragmentManager.
The hosting activity (D) is the one that knows what other fragments are in the activity (vs. being in other activities). Hence, in-fragment events that might affect the fragment mix should be propagated to the activity, which will make the appropriate orchestration moves.
You should let the Activity deal with adding and removing Fragments, as CommonsWare says, use a listener. Here is an example:
public class MyActivity extends FragmentActivity implements SuicidalFragmentListener {
// onCreate etc
#Override
public void onFragmentSuicide(String tag) {
// Check tag if you do this with more than one fragmen, then:
getSupportFragmentManager().popBackStack();
}
}
public interface SuicidalFragmentListener {
void onFragmentSuicide(String tag);
}
public class MyFragment extends Fragment {
// onCreateView etc
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
suicideListener = (SuicidalFragmentListener) activity;
} catch (ClassCastException e) {
throw new RuntimeException(getActivity().getClass().getSimpleName() + " must implement the suicide listener to use this fragment", e);
}
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Attach the close listener to whatever action on the fragment you want
addSuicideTouchListener();
}
private void addSuicideTouchListener() {
getView().setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
suicideListener.onFragmentSuicide(getTag());
}
});
}
}
In the Activity/AppCompatActivity:
#Override
public void onBackPressed() {
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)) {
// if you want to handle DrawerLayout
mDrawerLayout.closeDrawer(GravityCompat.START);
} else {
if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}
}
and then call in the fragment:
getActivity().onBackPressed();
or like stated in other answers, call this in the fragment:
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
If you are using the new Navigation Component, is simple as
findNavController().popBackStack()
It will do all the FragmentTransaction in behind for you.
See if your needs are met by a DialogFragment. DialogFragment has a dismiss() method. Much cleaner in my opinion.
I create simple method for that
popBackStack(getSupportFragmentManager());
Than place it in my ActivityUtils class
public static void popBackStack(FragmentManager manager){
FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
It's work great, have fun!
OnCreate:
//Add comment fragment
container = FindViewById<FrameLayout>(Resource.Id.frmAttachPicture);
mPictureFragment = new fmtAttachPicture();
var trans = SupportFragmentManager.BeginTransaction();
trans.Add(container.Id, mPictureFragment, "fmtPicture");
trans.Show(mPictureFragment); trans.Commit();
This is how I hide the fragment in click event 1
//Close fragment
var trans = SupportFragmentManager.BeginTransaction();
trans.Hide(mPictureFragment);
trans.AddToBackStack(null);
trans.Commit();
Then Shows it back int event 2
var trans = SupportFragmentManager.BeginTransaction();
trans.Show(mPictureFragment); trans.Commit();
If you need to popback from the fourth fragment in the backstack history to the first, use tags!!!
When you add the first fragment you should use something like this:
getFragmentManager.beginTransaction.addToBackStack("A").add(R.id.container, FragmentA).commit()
or
getFragmentManager.beginTransaction.addToBackStack("A").replace(R.id.container, FragmentA).commit()
And when you want to show Fragments B,C and D you use this:
getFragmentManager.beginTransaction.addToBackStack("B").replace(R.id.container, FragmentB, "B").commit()
and other letters....
To return to Fragment A, just call popBackStack(0, "A"), yes, use the flag that you specified when you add it, and note that it must be the same flag in the command addToBackStack(), not the one used in command replace or add.
You're welcome ;)
To Close a fragment while inside the same fragment
getActivity().onBackPressed();
kotlin -
requireActivity().onBackPressed()
parentFragmentManager.apply {
val f = this#MyFragment
beginTransaction().hide(f).remove(f).commit()
}
Why not just:
getActivity().finish();