Android activity navigation save state - android

In the main activity ActivityA I replace FragmentA by fragment FragmentB. From FragmentB the user can start a new activity ActivityB. By hitting the back button in ActivityB, ActivityA is displayed showing FragmentA. I was expecting to see FragmentB with its last state. Do I have to save the state of the previous activities separately to provide this behaviour?
ActivityA(FragmentA) -> ActivityA(FragmentB) -> ActivityB
BACK
ActivityA(FragmentB)
In the main activity I set the current fragment using:
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.a_main_frame_content, new FragmentB())
.addToBackStack(null)
.commit();
From the fragment I start a new activity using:
Intent intent = new Intent(getActivity(), ActivityB.class);
getActivity().startActivity(intent);
ActivityA is set as parent activity for ActivityB to provide proper navigation.
[UPDTATE] It looks like the problem lies in the different behaviour of navigating back and navigating up. If I navigate back, the activity is displayed in its last state while navigating up forces the activity to recreate.

Lets try this:
In the intent of the parentActivity(if you can set it before you create parentActivity its best, otherwise you may have to use setIntent):
currentActivityIntent.putExtra("random-unique-key-for-each-activity",
random-unique-key-for-each-activity);
And before you create a child activity, u put following in a map:
myKeyIntentMap.put(random-unique-key-for-each-activity, currentActivityIntent);
In the method triggered on "Up" event :
{
String parentKey = currentActivity.parentActivity.getIntent.getStringExtra("random-unique-key-for-each-activity");
Intent intentToLaunch = (Intent)myKeyIntentMap.get(parentKey);
intentToLaunch.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP|Intent.FLAG_ACTIVITY_SINGLE_TOP );
startActivity(intentToLaunch);
}
This way, using the intent, even if your History Stack is A-someAct1-someAct2-B, and u launch intent resolving to A, it will be "brought to front" killing someActs.
P.S. I havent done any null checks and havent kept in mind the exact method names, just given you an approach.

Behaviour of "up" is sometimes misleading indeed. When I faced similar problem I preferred to save my time and not deal with saving states.
You can quickly solve it by catching your navigation "up" event in your Activity:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (item.getItemId() == android.R.id.home) {
onBackPressed();
}
return true;
}

This is happening because when you are moving to ActivityB activityA is going to puse and destroy state respectively, thus when you back to activity ActivityA ActivityA is starting again thus you are getting fragmentA as view. you need to save the state using sharedPfer. Use some flag to save the state in your onCreateView() check the state and set correct fragment for the view. Hope you have got my point.
public static final String PREFS_NAME = "mypref";
boolean isVisited;
//check sharedpref
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
isVisited= settings.getBoolean("isVisited", false);
if(!isVisited){
// set fragmentA
}else{
// set fragmentB
}
// inside fragment transaction block
Editor edit = settings.editor();
isVisited.setBoolean(true);
edit.commit();

Related

Navigating back to previous Activity

I have two Activities A and B.
Activity A has a Tablayout with some Tabs. When I navigate from A to B I use this:
Intent intent = new Intent(A, B.class);
A.startActivity(intent);
When I now navigate back from B to A I have a question:
1) When using Android's back button, the selected tab / scrolling position from A was remembered
2) When using an Intent or NavUtils.navigateUpFromSameTask(this); then the selected tab and scroll Position is NOT remembered but set to initial value
Can someone explain me what is going on here?
1) when navigation from activity A to B, the android system does not destroy activity A, but takes it to the back stack and adds B to the foreground. thats why when you press the back button or call onBackPressed() from the java code activity B is destroyed and A is set to the foreground. here is an example from the docs : Understand Tasks and Back stack
2) when using an intent/navigateUpFromSameTask activity A is recreated and set to the foreground and B is set to the background, it's like adding another activity A to the stack so it will be A,B,A but if you press the back btn then you will be back to B and then A.
if you want to keep the scroll position and other data in activity A you call the onBackPressed in B or use the onSaveInstanceState to save the data and use it in the onCreate .
here is an example of saved instance:
#Override
public void onSaveInstanceState(Bundle savedInstanceState) {
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("VariableName", variableData);
savedInstanceState.putString("VariableName", variableData);
savedInstanceState.putString("VariableName", variableData);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.penguin_main);
if(savedInstanceState!=null){
bookData = (String) savedInstanceState.getSerializable("VariableName");
bookData = (String) savedInstanceState.getSerializable("VariableName");
bookData = (String) savedInstanceState.getSerializable("VariableName");
}
}
You can set the current scroll position and tab position in activity A's on overriding onSaveInstance(Bundle savedInstanceState) method. When return to activity you can get onRestoreInstanceState(Bundle savedInstanceState) to restore it.
Hope it helps :)
Because NavUtils.navigateUpFromSameTask() just calls startActivity() and if android:launchMode="standard" the activity will be instantiated and created again and that is why can not remember the previous selected tab. To solve this issue you can override onNavigateUp() and inside that setCurrentItem(index) the index of tab you want to be displayed.
#Override
public boolean onNavigateUp() {
myViewPager.setCurrentItem(position, true);
return true;
}
Edit
You can use another solution to solve the problem by setting android:launchMode="singleTop" on activity but this solution may not applicable in all the application.
When you start an Activity, the first page will be opened! But when the back button is pressed, it navigates between the saved state of the activityTo simulate the back button pressed, you can try this:
#Override
public void onBackPressed() {
finish(); //your choice, thought not needed as super.onBackPressed(); is called if nothing is assigned here
}
or on toolbar back button clicked click:
toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
finish();
}
});

Go back to a Fragment from an Activity onBackpress

I Tried the following code to move from a Fragment extends Fragment to an Activity extends activity.
Intent mIntent = new Intent(getActivity(), UserProfile.class);
Bundle bundle = new Bundle();
bundle.putBoolean(Constant.isFromChatScreen, false);
mIntent.putExtras(bundle);
startActivity(mIntent);
In BaseActivity.java I have Fragment A, Fragment B, Fragment C..from Fragment A I moved to Userprofile.java now I want to move back to Fragment A onBackPressed
How can I do that?
Check Google documentation for findFragmentByTag() here.
You can also use addToBackStack() while calling the fragment from FragmentTransaction
Just use addToBackStack(null) before commit when you make fragment transactions and then when you press the back button you will get back to your fragment.
Old question, but anyone looking for an answer to this can just do this:
#Override
public void onBackPressed() {
finish();
}
finish() "closes" the current Activity and takes you back to the Fragment (Actually, it takes you back to the Activity hosting the Fragment)

How to navigate from fragment in one activity to fragment in another activity?

I want to add back navigation to toolbar. I need to get from a fragment in an activity to a specific fragment in another activity.
It looks a little like this, where every orange line means navigating to a new activity or fragment:
How do I move from fragment B to fragment A from OtherActivity?
Consider these steps:
From Activity 1 holding Fragment A , you want to directly load Fragment B in Activity 2.
Now, I am thinking first, then you press a button in Fragment A, you can directly go to Activity B.
Then it means, you can simply load Fragment B as soon as you arrive in Activity 2.
Since you are dealing with back navigation (I believe you mean the upNavigation?), you can override the following:
But watch clearly, because if you need to load an exact fragment in Activity 2, you need to know somehow:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
Intent intent = new Intent(this, Activity2.class);
intent.putExtra("frag", "fragmentB");
startActivity(intent);
return true;
default:
return super.onOptionsItemSelected(item);
}
}
As you can see, when you click the back arrow on the toolbar, we pass a value through our intent to identity which fragment we want to load.
Next, in your Activity2, simply get the intent extra and do a switch or an if statement:
#Override
public void onResume(){
super.onResume();
Intent intent = getIntent();
String frag = intent.getExtras().getString("frag");
switch(frag){
case "fragmentB":
//here you can set Fragment B to your activity as usual;
fragmentManager.beginTransaction().replace(R.id.container_body, new FragmentB()).commit();
break;
}
}
From here, you should have your Fragment B showing in Activity 2.
Now you can handle the same thing while inside Activity 2 to decide where to go when a user clicks the back home arrow!
I hope this helps you get an idea.
Note: I thought about the interface approach and realized it is not necessary since this can be done easily with this approach!
To navigate from one Activity to another Activity's Fragment, with Kotlin version 1.4.0 and, for example, calling a click listener on a button it works so:
binding.yourButton.setOnClickListener {
supportFragmentManager.beginTransaction().replace(R.id.yourLayout, NameOfYourFragment()).commit()
}
Use this code to change your fragment
fragmentManager.beginTransaction().replace(R.id.container_body, new FragmentC()).commit();
and to show navigation on custom toolbar add
Toolbar toolbar = (Toolbar) rootView.findViewById(R.id.toolbar);
((AppCompatActivity)getActivity()).setSupportActionBar(toolbar);
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true)
((AppCompatActivity)getActivity()).getSupportActionBar().setHomeButtonEnabled(true);

Navigate up from activity back to fragment

Let's say I have two activities called A_Activity and B_Activity. A_Activity has 3 fragments named A_Fragment, B_Fragment, and C_Fragment. A_Activity acts as a base activity for the three fragments.
From B_Fragment, it is possible to navigate to B_Activity and from B_Activityit is possible to navigate up to B_Fragment. from A_Activity it is possible to navigate to A_Fragment, B_Fragment, orC_Fragment. Lastly, from A_Fragment, B_Fragment, or C_Fragment, it is possible to navigate to any other fragment.
Here's an arbitrary representation of the hierarchy of activities and their respective fragments:
A_Activity
A_Fragment
B_Fragment
B_Activity
C_Fragment
Let it also be a given that A_Fragment is the default fragment for A_Activity.
My current solution to this is: in B_Activity's home icon selected, I am creating a new intent:
Intent intent = new Intent(B_Activity.this, A_Activity.class);
intent.putExtra("data_to_start_B_Fragment", "B"); // instead of starting A Fragment
startActivity(intent);
What is the proper way to navigate to B_Fragment from B_Activity?
From B_Fragment, starting B_Activity using startActivityForResult(...) by getApplicationcontext not getActivity().getApplicationContext().
In Activity B, you use setResult(data here) to return fragment and finish ActivityB to back fragment B.
From B_Activity to B_Fragment (if you start B_Activity from B_Fragment), you just need to detect action from either home button or system back button and then kill the Activity by calling
finish()
e.g. in B_Activity
#Override
public boolean onOptionsItemSelected(MenuItem item) {
if (id == android.R.id.home) {
finish();
}
return super.onOptionsItemSelected(item);
}

Bundle in a fragment created

Is there anyway to assign a Bundle to a fragment already created?. I tried something like this:
Bundle mArgumentos = new Bundle();
mArgumentos.putString("id", id);
this.setArguments(mArgumentos);
But I get the error: Fragment already active.
I'm just wondering if I can achieve this without overriding the onBackPressed() method.
My purpose with this is because I have a form for registry a new item. From here I can go to another fragment (lets call it fragment_B) with the new id of the item (I call fragment_B with a bundle assigning the new id). When the user presses back button, he gets to the previous form but the id is missing (this is because the form goes to the previous state, like empty), so if he tries to go to fragment_B again, he cant because the id.
Obviously the user cant go to fragment_B if the form has not been save. So the user saves the form and then press the button to go to fragment_B with its new id:
FragmentB fragment = new FragmentB();
Bundle mArgumentos = new Bundle();
mArgumentos.putString("id",id);
fragment.setArguments(mArgumentos);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(android.R.id.tabcontent, fragment, tag);
ft.addToBackStack(tag);
ft.commit();
Any suggestions?.
Edit:
The fragments are not in the same activity. When I create a new item and press button save I should be in a "edit mode". This means that after I create the item I can come back and edit it. I'm just using the same fragment to edit and create. So, when I create the item, press the button to go to fragment_B and then press back button, I should be like "edit mode" of this item. This means that from fragment_B to the previous I should pass the id of the item created. That's why I wanna know if I can do this without overriding the onBackpress method.
Edit2:
If there is no other way to achieve this, I was thinking in override the onBackpress method like this:
//get the fragment where I'm staying on.
Fragment fragmentActual = this.getSupportFragmentManager().findFragmentById(android.R.id.tabcontent);
//get the tag of the fragment
String fragmentTag = fragmentActual.getTag().toString();
if (fragmentTag.equals("tagFragmentB")){
Fragment fragmentA = new FragmentA();
Bundle mArguments = fragmentActual.getArguments();
String id = mArguments.getString("id");
if(id != null){
fragmentA .setArguments(mArguments);
FragmentManager manager = getSupportFragmentManager();
FragmentTransaction ft = manager.beginTransaction();
ft.replace(android.R.id.tabcontent, fragment, "fragmentA");
ft.addToBackStack("fragmentA");
ft.commit();
}
}else{
super.onBackPressed();
}
This is working but as you have notice, this change the order of the fragments called by the user and this should be invisible for the user. So, this forces me to put more code in order to get the calls that the user did.
I'm kind of new with this, so maybe I have not the "Android thinking". If is there a better way to do what I want, I'm open to everything. I just want to do the thinks right and dont have problems in the future just because a user pressed a back button.
Well in my particular situation a made it work doing the following: I noticed that when I fill the form, saved it, then go to fragmentB and finally press back button, the information of my form was retained. So I put a hidden etidText in my form:
<EditText
android:id="#+id/textEditHiddenId"
android:visibility="gone"
android:inputType="number" />
When the button save is pressed, I set the content of this editText:
etIdHidden.setText(id);
When the activity is created, according to http://developer.android.com/guide/components/fragments.html, the method onResume is called after the onCreateView. So, I get the value of my textEditHiddenId in this method:
#Override
public void onResume() {
super.onResume();
String id= etIdHidden.getText().toString();
if(id.equals("")==false){
mArgumentos=new Bundle();
mArgumentos.putString("id", ""+id);
}
};
The first time, the content of textEditHiddenId is empty, but when backbutton is pressed and the onResume method is called, I get the content initialized and I can override my Bundle.
Simplest solution ever..

Categories

Resources