I have 2 Activity(s). Inside 1st Activity there is initially one Fragment
MainActivity.java
public class MainActivity extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.myactivity);
if(savedInstanceState == null) {
getFragmentManager().beginTransaction().replace(R.id.fragmentContainer, MainFragment.newInstance().commit();
}
}
}
then clicking on a button replaces it with another Fragment.
#Override
public void onClick(View arg0) {
DetailFragment detail = (DetailFragment)getFragmentManager().findFragmentById(R.id.detail);
getFragmentManager().beginTransaction().replace(R.id.detail, detail, "detail").commit();
}
On 2nd Fragment there is another button, clicking on it opens a New Activity.
Intent popUp = new Intent(MainActivity.this, PopUp.class);
popUp.putExtra("CarID", carID);
startActivity(popUp);
From PopUp Activity, pressing device back will go back to MainActivity.
Now the challenge is for the Application's business logic I need to update the ActionBar's title of previous MainActivity when user goes back.
For this I'm listening for onResume() on both MainFragment and DetailFragment. Also when user goes back from DetailFragment to MainFragment I update the ActionBar title with different text.
So I need to know when exactly user goes back from:
1) PopUp Activity > Detail Fragment
2) Detail Fragment > Main Fragment
Currently onResume() is fired on both MainFragment and DetailFragment when PopUpActivity is closed. On MainFragment I can't exactly find out whether onResume() is called for 1st or 2nd case.
What is the best practice to fire onResume() on DetailFragment only when user goes back from PopUpActivity > DetailFragment. In other words, how do I detect from DetailFragment that PopUpActivity is closed without firing onResume() on MainFragment.
I wouldn't mess with onResume() for something like this.
I would suggest doing the following:
Create a Stack<String> for titles.
Implement FragmentManager.OnBackStackChangedListener in your MainActivity.
In your onBackStackChanged() implementation, check if the back stack has been pushed or popped using FragmentManager.getBackStackEntryCount().
If the back stack has been pushed, push the newly-displayed fragment's title to your title stack.
If the back stack has been popped, pop a title and set the title bar with the title at the new top of stack.
If you set a title from a fragment that isn't added to the back stack, pop a title from the title stack and push the newly-displayed fragment's title, i.e. replace the title at the top of the stack.
Invoke your PopupActivity with startActivityForResult() instead of startActivity().
Override onActivityResult() in your MainActivity so that when PopupActivity returns, you set the title bar with the title at the top of stack.
Don't forget to persist your title stack in onSaveInstanceState() in your MainActivity and restore it in onCreate().
That might seem like a lot of work just for maintaining titles, but you will drive yourself crazy trying to do this with onResume.
try it:
#Override
public void onResume() {
super.onResume();
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
// handle back button
return true;
}
return false;
}
});
}
Related
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();
}
});
I have two Activities: MainActivity and OtherActivity. A fragment in MainActivity launches the OtherActivity, but when I press the back button at the top, it returns me to the MainActivity instead of the fragment that launched the OtherActivity.
The end result is the enduser is sent back to the main screen instead of the fragment that they were using.
This is my manifest. I have set MainActivity to be the parent of OtherActivity:
<activity android:name=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".OtherActivity"
android:parentActivityName="com.example.test.MainActivity" >
</activity>
In MainActivity, when a user clicks on a button, it loads a List fragment:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// ...
button.findViewById(R.id.events).setOnClickListener(new View.OnClickListener() {
Fragment ef = new MyItemsFragment();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.addToBackStack(null);
fragmentTransaction.replace(R.id.content, ef, "my_fragment");
fragmentTransaction.commit();
});
}
public void callOtherActivity() {
Intent i = new Intent(this, OtherActivity.class);
this.startActivity(i);
}
When an item is clicked in the List Fragment, it launches OtherActivity. A lot of the code for this was auto-generated by android studio. I have changed some of it. In MyItemsRecyclerViewAdapter:
#Override
public void onBindViewHolder(final ViewHolder holder, int position) {
holder.mItem = mValues.get(position);
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (null != mListener) {
// Notify the active callbacks interface (the activity, if the
// fragment is attached to one) that an item has been selected.
mListener.onEventFragmentInteraction(holder.mItem);
MainActivity ma = (MainActivity) holder.mView.getContext();
ma.callOtherActivity();
}
}
});
}
Notice it calls ma.callOtherActivity(), which is defined in MainActivity and will launch OtherActivity when an item is clicked in the list fragment.
This is OtherActivity:
public class OtherActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_other);
}
}
Now, when I click on the back arrow button in OtherActivity, the app will just show MainActivity, and not the List fragment that the user last saw.
How can I make sure that on return from OtherActivity, the user will see the list fragment again for better user experience?
EDIT: There seems to be confusion about the 'back' button. Maybe they are the same, but I am talking about the one when switching Activities. It is not the back button used to exit the application. See image
When you start a new activity, the existing one is stacked so you can retrieve it later such as when you press the Back key.
On pressing the Back key, the Activity is restarted. This is the important part. It is not just brought back exactly as it was when it was last used. This means that you as the developer must take care to restore the Activity back to the state that you want it to be in. Any fragments will already be in the correct state but you must take care to not overwrite that state when your Activity restarts.
In your case, I suspect that your MainActivity code is indiscriminately re-initialising your fragments instead of checking whether fragments already exist for that particular activity before conditionally initialising them only if they don't.
It is difficult to say for sure as you do not include any of that code in your question. Check the code that displays the fragments in your MainActivity. Before you display them, are you checking whether there are already fragments visible? If not, you should be.
For example, the code below in an Activity, first checks whether a fragment is already displayed before it adds a new one. Essentially, the first time this is run, there will be no Fragments and therefore existing will be null and a new Fragment will be created and displayed.
After you have started a new OtherActivity etc and then returned to this activity with the Back key, existing will no longer be null as the Fragment system will have restored any existing fragments back into your container. Therefore we actually just do nothing.
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
Fragment existing = getSupportFragmentManager().findFragmentById(R.id.content);
if (existing == null) {
Fragment newFragment = new MyFragment();
getSupportFragmentManager().beginTransaction()
.replace(R.id.content, newFragment)
.commit();
}
}
Hope this is clear.
Just delete this line fragmentTransaction.addToBackStack(null);
you can use getSupportFragmentManager().getBackStackEntryCount(); method in MainActivity's onBackPressed Method.
OnClick bottom System back button.
follow below method.
#Override
public void onBackPressed() {
int backstack = getSupportFragmentManager().getBackStackEntryCount();
if (backstack > 0) {
getSupportFragmentManager().popBackStack();
} else {
super.onBackPressed();
System.exit(0);
}}
In fragment, press the back button at the top.
try this,
in onCreate method or onCreateView method of Fragment call this default method
setHasOptionsMenu(true);
and then override below method:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getFragmentManager().popBackStack();
break;
}
return super.onOptionsItemSelected(item);
}
You can use Activity for result for this start Other Activity and as soon as you want to finish OtherActivity just do setResult, MainActivity would receive this result and there do the following
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
The code would go like this in MainActivity
public void callOtherActivity() {
Intent i = new Intent(this, OtherActivity.class);
startActivityForResult(i, 1);
}
and in OtherActivity's onBackPress call below method.
Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(Activity.RESULT_OK,returnIntent);
finish();
and now in MainActivity onActivityresult
#Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == 1) {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
getSupportFragmentManager().popBackStack();
}
}
}
I might be late but in my case error was coming because of the LoadFragment Method in OnStart method of MainActivity
I have 2 viewpagers: swipeViewPager and nonSwipeViewPager in MainActivity
In swipeableViewPager, I have fragments: homeFragmentFirst and homeFragmentSecond
In nonSwipeableViewPager, I have fragments: AppleFragment , MangoFragment, GrapesFragment and CherryFragment
First I want swipeableViewPager to be viewed in MainActivity. So I have set setVisibility of nonSwipeableViewPager as GONE.
After selecting an item from the drawer, I setVisibility of swipeableViewPager as GONE and of nonSwipeableViewPager as Visible.
By this, I open AppleFragment
Situation:
Suppose, I selected Grapes from drawer, GrapesFragment opens.
I do my some stuff there. And then when I press back button, the app closes i.e. the MainActivity closes.
What I want is when back button is pressed, I want to again bring back homeFragmentFirst from swipeableViewPager.
I have tried below code in GrapesFragment
#Override
public void onBackPressed() {
super.onBackPressed();
// Do some operations here
MainActivity mainActivity = new MainActivity();
mainActivity.swipeHomeViewPager.setVisibility(View.VISIBLE);
mainActivity.nonSwipeHomeViewPager.setVisibility(View.GONE);
}
I solved it myself. I added following in MainActivity:
#Override
public void onBackPressed() {
if (nonSwipeHomeViewPager.getVisibility() == View.VISIBLE) {
nonSwipeHomeViewPager.setVisibility(View.GONE);
swipeHomeViewPager.setVisibility(View.VISIBLE);
swipeHomeViewPager.setCurrentItem(0,true);
} else if (swipeHomeViewPager.getVisibility() == View.VISIBLE){
android.os.Process.killProcess(android.os.Process.myPid());
System.exit(0);
}
}
You can implement backstack for the fragments (while adding / replacing fragment) add it to backstack. the back press will automatically take you to previous fragment.
otherwise you need to override OnBackPressed method to handle with your own logic.
I have a mainActivity with viewPager and three tabs, each tab has its respective fragment. i want when this mainActivity is launched and the backButton is pressed, nothing to happen.
to achieve this, i override onBackPressed() inside the mainActivity the one has viewPager with tabs, but when the mainActivity ia launched one of the tabs, which represents Fragment, by default appears, and when i press the backButton, i go back, which means the Backbuton is not disabled.
that is it, in the mainActivity, i have:
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
super.onBackPressed();
Log.w(TAG, "#onBackPressed().");
}
and he backbutton is not disabled in the Fragment, each time i press it, the logCat displays the message in the onBackPressed and i go back, and i do not want this, i want while i am in the Fragment "any one of the tabs", the backButton should has no effect.
please let me know how to achieve this?
Try not calling
super.onBackPressed();
Although the log will still print because the method will be called, this will stop Android performing it's logic and going back.
If you want the fragment tabs to do nothing, but the viewpager to handle it, do something like this
#Override
public void onBackPressed() {
if (getCurrentTab == 0) {
super.onBackPressed();
} else {
//This will stop the fragment tabs from doing anything when back is pressed
}
}
If you need avoid the back button, comment the super call //super.onBackPressed();
I want to go back to the last fragment in the back stack, so I want to make the back button popback the stack. Should I do this? and if so, should I override onBackPressed() or onKeyDown()?
#Override
public void onBackPressed()
{
Intent intent = new Intent(this,ABC.class);
startActivity(intent);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK ) {
// do something on back.
return true;
}
return super.onKeyDown(keyCode, event);
}
My fragments aren't being added to the back stack properly for some reason
I am using this to try to go back to the previous fragment, but the order is acting strange. What exactly should I do to make the order proper?
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft){
if(teamsFrag !=null)
{
if(manage.getBackStackEntryCount() > 0)
manage.popBackStack(manage.getBackStackEntryAt(manage.getBackStackEntryCount()-1).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
//ft.detach(dataFrag);
}
Short answer? No.
Long answer? If you have to ask, No.
You should set your fragments up using the fragment manager so that the back button does what you want. You shouldn't override the back button instead of implementing your stack correctly.
I want to go back to the last fragment in the back stack, so I want to make the back button popback the stack.
This is the default behavior when you use FragmentTransaction#addToBackStack() while adding new Fragments with the FragmentManager.