Come back to fragment, Android - android

I have an activity called MainActivity with 2 fragment (let's call them A and B). This activity shows A on the left and B on the right in case of large display, but, for small display, there is only one frameLayout and I change the fragment in it after I clicked something in the fragment B.
So, for small device, I show initially fragment A and from it I can go to B, (I select a day from my CalendarView in fragment A and then I can go to B).
In B then I can go to another Activity by clicking a button. From that activity I want to came back to my previously activity but I don't know how to have the fragment B already inflated. Right now if I go back to my MainActivity I find fragment A also if I left this activity with B inflated.
I can come back either with the back button on my actionbar or after I close my last activity (the one I go after B).
Can anyone help me?
This is the Main activity class in which I have the two fragments.
public class MainActivity extends AppCompatActivity implements CalendarFragment.OnCalendarSelectedListener{
private ActionBar actionBar;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
actionBar=getSupportActionBar();
if(findViewById(R.id.left_fragment) != null){
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
AFragment leftFragment = new AFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.left_fragment, leftFragment).commit();
}
// Check that the activity is using the layout version with
// the fragment_container FrameLayout
if (findViewById(R.id.right_fragment) != null) {
// However, if we're being restored from a previous state,
// then we don't need to do anything and should return or else
// we could end up with overlapping fragments.
if (savedInstanceState != null) {
return;
}
// Create a new Fragment to be placed in the activity layout
BFragment bFragment = new BFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.right_fragment, bFragment).commit();
}
}
...
//this is how I inflate the fragment B from a click on fragment A
public void onCalendarSelected(int day, int month, int year) {
BFragment bFragment = (BFragment)
getSupportFragmentManager().findFragmentById(R.id.right_fragment);
if (bFragment != null) {
bFragment.updateList(day);
} else {
actionBar.setDisplayHomeAsUpEnabled(true);
BFragment newFragment = new BFragment();
Bundle args = new Bundle();
args.putInt("day",day);
newFragment.setArguments(args);
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.left_fragment, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
This code works fine but I don't know how to solve my problem.

Related

Handle Fragment Backstack According to the Current Fragment

Problem 1
I have a Navigation Drawer and most of my fragment transactions happens from here.
So say I have 4 Items in my drawer and I am doing the transaction from all of them. So if I am at the fragment [A] and now I click on the fragment [B], I need to come back to the previous fragment i.e. [A]. But if I keep clicking on the Item B of the navigation drawer that opens the fragment [B], I keep adding it to the backstack and when I press the back button, I am still at the same fragment.
Problem 2
How do I achieve the Clear Top behavior that is used for the intents for the fragments. As intents have the power to clear the activities from the stack from the top only, I want to achieve the same behavior.
Problem 1 & 2 solution Idea:
Create an Interface say FragmentInstanceHandler
public interface FragmentInstanceHandler {
public void openFragment(Fragment fragment, String fragmentTag);
}
Create a BaseFragment like below and extend this to all your Fragment classes:
public BaseFragment extends Fragment {
public FragmentInstanceHandler fragmentInstanceHandler;
public void setFragmentInstanceHandler(FragmentInstanceHandler fragmentInstanceHandler) {
this.fragmentInstanceHandler = fragmentInstanceHandler;
}
}
Implement the FragmentInstanceHandler interface to the Activity in which you are going to open all the Fragments. Let's say Activity is MainActivity:
public MainActivity extends Activity implements FragmentInstanceHandler {
private BaseFragment currentFragment;
#Override
public void openFragment(BaseFragment fragment, String fragmentTag) {
FragmentManager fragmentManager = getSupportFragmentManager();
Fragment oldFragmentInstance = fragmentManager .findFragmentByTag(fragmentTag);
boolean fragmentPopped = fragmentManager.popBackStackImmediate (fragmentTag, 0);
if (!fragmentPopped && oldFragmentInstance == null) {
fragment.setFragmentInstanceHandler(this);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.container, fragment, fragmentTag);
fragmentTransaction.addToBackStack(fragmentTag);
fragmentTransaction.commit();
currentFragment = fragment;
} else if(fragmentPopped ){
currentFragment = oldFragmentInstance;
}
if(mDrawerLayout!= null)
mDrawerLayout.closeDrawers();
}
}
Now whenever you want to open a new Fragment even from any other Fragment you can call method like below, It is advised to provide new tag if you want to have new instance of same Fragment:
fragmentInstanceHandler.openFragment(new MyFragment(), "FragmentNewInstance");
You can tweak FragmentInstanceHandlerto add your own method to replace the current Fragment instead of adding. Above solution just gives you an idea how you can acheive your solution by putting and mananging all your code from one place.

Different behavior of Fragment's onCreateView depending on how it is added to activity [duplicate]

I am testing situation where a user goes into my app after the system has terminated the app process due to low RAM. I am seeing unexpected behavior and hoping to get some help.
In my app, I have an activity, lets call it ActivityA, that immediately creates a fragment, Fragment A, and does a fragment replacement. FragmentA displays a ListView with two items in it. If the user clicks the first item, a second fragment, Fragment B is created and replaces FragmentA. Otherwise, another FragmentA is created and replaces the original FragmentA. I'm trying to create a file directory tree. FragmentA is for directories, and FragmentB is for files.
Lets say the user clicks on a file. This is the stage in the test where the user switches to another app, and the system terminates my app's process due to low memory. Then, the user goes back into my app expecting everything to be left the way it was. However, what actually happens is Fragment A (The parent directory) is being displayed instead of Fragment B (the file). When the user clicks the back button, Fragment B (the file) is then displayed. What am I doing wrong that is causing the system to restore the backstack in this way?
Here is an example program to further show what my app is doing:
// ActivityA.java
public class ActivityA extends AppCompatActivity implements onItemClickListener
{
#Override
public void onCreate(Bundle savedInstanceState)
{
FragmentA fragA = new FragmentA();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransation.replace(R.id.basic_frame, fragA);
fragmentTransaction.commit();
}
#Override
public void onItemClick(AdapterView<?> aView, View v, int position, long id)
{
if (position == 0)
{
FragmentB fragB = new FragmentB();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransation.replace(R.id.basic_frame, fragB);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
else
{
FragmentB fragA = new FragmentA();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransation.replace(R.id.basic_frame, fragA);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
}
When you call super.onCreate(), Fragments automatically restore their current state when savedInstanceState is not null.
Therefore if you're expecting to do one time set up by adding your intial Fragment, you should always surround it with an if (savedInstanceState == null) check:
#Override
public void onCreate(Bundle savedInstanceState)
{
// I assume you accidentally left out these lines
super.onCreate(savedInstanceState);
setContentView(R.id.your_content_view);
if (savedInstanceState == null) {
FragmentA fragA = new FragmentA();
FragmentTransaction fragmentTransaction =
getSupportFragmentManager().beginTransaction();
fragmentTransation.replace(R.id.basic_frame, fragA);
fragmentTransaction.commit();
}
}

Exiting from Activity with only fragments

My app contains one empty activity and a couple of fragments. The onCreate of the activity replaces the empty view in activity_main.xml with a MainFragment that contains some buttons. Each button launches a separate fragment, and user can navigate from one fragment to another, etc.
On the press of back key, the current fragment correctly gets replaced with the previous fragment, until you get to the MainFragment. When user presses back from MainFragment, it hides the main fragment and you see the white empty background of the main activity. But I want to exit from the activity at this point, as that would be the sensible behaviour.
I am able to achieve this by calling super.onBackPressed() for a second time from onBackPressed if there are no fragments left in the fragment manager.
#Override
public void onBackPressed() {
super.onBackPressed();
FragmentManager manager = getSupportFragmentManager();
List<Fragment> fragments = manager.getFragments();
if (fragments == null || fragments.size() == 0) {
Log.d(TAG, "No more fragments: exit");
super.onBackPressed();
}
}
Is this acceptable thing to do - would it create any issues in the activity workflow? Is there a better/standard way to handle this scenario?
There is no problem to do that, but probably it would be easier if when you add the main fragment to the activity you do NOT call .addToBackStack()
You don't really need to override onBackPressed in your Activity. I would suggest implementing a method for adding fragments in your Activity:
protected void addFragment(Fragment fragment, boolean addToBackStack) {
String tag = fragment.getClass().getName(); //It's optional, may be null
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction()
.add(R.id.your_container_id, fragment, tag);
if (addToBackStack) {
transaction.addToBackStack(tag);
}
transaction.commit();
}
And modify your onCreate method of activity like in the following snippet:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
// Add your fragment only if it is a first launch,
// otherwise it will be restored by system
addFragment(new YourFirstFragment(), false);
}
}
For all other fragments use:
addFragment(new OtherFragment(), true);

Does the fragment manager restore the last displayed fragment on screen after orientation change?

The app consists of an activity (containing a FrameLayout only) and three Fragments (all of them same in structure, have a button but the only difference being different background color).
When the activity is first created, all fragments are put into the FrameLayout one by one by replacing. When we click the button (of the fragment) on the screen, it replaces the current fragment with another fragment.
The problem being that on screen rotation, the activity shows the fragment which was displayed when the app was first started, irrespective of the fragment that was being displayed just before rotation.
Why does this happen? Why isn't the last displayed fragment restored on the screen?
I know that I can use onSavedInstanceState, but more importantly, I want to learn how the FragmentManager works.
The Main Activity:
public class MainActivity extends AppCompatActivity implements ActivityInstance {
fraga a;
fragb b;
fragc c;
FragmentManager fm;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fm = getSupportFragmentManager();
if (fm.findFragmentByTag("a")!=null){
a = (fraga) fm.findFragmentByTag("a");
}
else {
a = new fraga();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frame, a, "a" );
ft.commit();
}
if (fm.findFragmentByTag("b")!=null){
b = (fragb) fm.findFragmentByTag("b");
}
else {
b = new fragb();
FragmentTransaction fx = fm.beginTransaction();
fx.add(R.id.frame, b, "b" );
fx.commit();
}
if (fm.findFragmentByTag("c")!=null){
c = (fragc) fm.findFragmentByTag("c");
}
else {
c = new fragc();
FragmentTransaction fl = fm.beginTransaction();
fl.add(R.id.frame, c, "c" );
fl.commit();
}
}
public void changefrag(int i) { //This method is called by the fragment
using the ActivityInstance interface
switch (i){
case 1: FragmentTransaction f1 = fm.beginTransaction().replace(R.id.frame, a, "a" );
f1.commit();
break;
case 2: FragmentTransaction f2 = fm.beginTransaction().replace(R.id.frame, b, "b" );
f2.commit();
break;
case 3: FragmentTransaction f3 = fm.beginTransaction().replace(R.id.frame, c, "c" );
f3.commit();
break;
default:
{Toast.makeText(this, "default", Toast.LENGTH_SHORT).show();}
}
}
}
The FragmentManager restores the last displayed fragment after orientation change. Looks like you are adding a new fragment after every orientation change from the Activity. So a new fragment is added over the last fragment, which covers the last fragment.
You should add your fragment in onCreate only if the savedInstanceState is null. savedInstanceState will be null only the first time, it won't be null after orientation change.
if (savedInstanceState == null) {
// add your fragment
}
// else - don't add fragment, because all the fragments are restored automatically

Android app closes when back button pressed

I have the following activity which launches a fragment when the tab is selected:
public class MainActivity extends Activity implements TabListener {
Fragment f = null;
.....
public void onTabSelected(Tab tab, FragmentTransaction ft) {
.....
if (tab.getPosition() == 0) {
if (initalSync == true) {
progress1.setVisibility(TRIM_MEMORY_UI_HIDDEN);
}
f = new EventFragment();
Bundle data = new Bundle();
data.putInt("idx", tab.getPosition());
f.setArguments(data);
}
if (tab.getPosition() == 1) {
progress1.setVisibility(TRIM_MEMORY_UI_HIDDEN);
f = new MapsFragment();
Bundle data = new Bundle();
data.putInt("idx", tab.getPosition());
f.setArguments(data);
}
.....
ft.replace(android.R.id.content, f);
}
When ever I press the phones back button on any of the fragments it closes my app. I know this is related to the backstack but every method I have tried fails.
any ideas?
You need to add your fragments to the backstack if you don't want the activity to close by the time you press back, all you have to do is calling the following method:
ft.addToBackStack(null)
before you replace and commit your transaction.
This way the fragments injection you are using will be tracked down, and the back button will change to the previos fragment until reaching the first action and then it will close the app.
Regards!
You need to call addToBackstack(null) on the Transaction to add the fragment to the backstack. Then the back button should revert to the previous fragment.

Categories

Resources