How can we get the onBackPressed callback only in the fragment class? Instead ,now it is going to the onBackPressed in the activity classes? I need to restrict sending the callback to Activity on backpress from fragment.
example::
#Override
public void onBackPressed() {
Log.d("SKT", "backPressHere");
}
Implement this in the Activity and restriction part you can handle via the interface.
The fragment itself does not have a return button, but instead you can put this code in the onBackPressed() method in the activity to which the fragment is attached, and with the getSupportFragmentManager() method you can control the examples of running fragments or delete and return do.
here is example :
#Override
public void onBackPressed() {
int count = getSupportFragmentManager().getBackStackEntryCount();
if (count == 0) {
// no fragment attach
super.onBackPressed();
} else {
// fragment attach
getSupportFragmentManager().popBackStack();
}
}
The fragment does not receive the onBackPressed callback. You should implement dispatching the event in the activity.
public interface BackPressedSupport{
/**
* return true if the fragment consumed the event. otherwise false.
*/
boolean onBackPressed();
}
In your Activity :
#Override
public void onBackPressed() {
Fragment f = getActiveFragment();
if (f instanceof BackPressedSupport && (BackPressedSupport f).onBackPressed()){
return;
}
super.onBackPressed();
// Other backPressed handling of activity.
}
public Fragment getActiveFragment() {
if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
return null;
}
String tag = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1).getName();
return getSupportFragmentManager().findFragmentByTag(tag);
}
In your Fragment:
public MyFragment extends Fragment implements BackPressedSupport {
/* Your code */
#Override
public boolean onBackPressed() {
/* Handle backpressed */
Log.d("SKT", "backPressHere");
return true ; // tell the activity to stop handling this backPressed event
// return false; // keep the default behaviour
}
}
Related
In my app i have navigation drawer,So i have one MainActivity and rest are fragments. My app is working fine. Whenever i press back button it redirect to previous fragment.it works fine.but what i want is after successful payment i am displaying Successful payment page,on this page when user press back button i want to redirect to HomeFragment,but right now it goes to Placeorder fragment.
#Override
public void onBackPressed() {
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
else {
getFragmentManager().popBackStack();
}
}
Override your onBackPress method:
#Override
public void onBackPressed() {
if (fragment != null && fragment.getChildFragmentManager().getBackStackEntryCount() > 0){
fragment.getChildFragmentManager().popBackStack();
}else {
super.onBackPressed();
}
}
One way would be saving previous fragment (previousFragment) variable while opening new fragment and making it null when you are in first or placeholder fragment .... then check value of previousFragment inside onBackPressed method , if null perform exit operation else replace fragment with previousFragment by which you will get back to previous fragment on back press.
You should simulate function as below in abstract class fragment
/**
* Handle "Back" key pressed.
* #return TRUE if the "Back" key pressed was handled. FALSE indicate it's not handled and parent class can handle it.
*/
public abstract boolean onBackPressed()
And in MainActivity, you will handle this function
#Override
public void onBackPressed() {
if (mSelectedFragment != null && mSelectedFragment.onBackPressed()) {
return;
} else {
super.onBackPressed
}
}
inside your onBackPressed() do like this:
#Override
public void onBackPressed() {
if(getSupportFragmentManger.findFragmentByTag("paymentFragment") != null){
//add homeFragment;
}
else{
if(getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
}
else {
getFragmentManager().popBackStack();
}
}
}
you can set some global variable (true when payment is success else false) in Application class and in onbackpressed of your activity just check that variable is true or not.Then in backpressed of your activity call this function.
public void clearBackStack() {
FragmentManager manager = getSupportFragmentManager();
if (manager.getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry first = manager.getBackStackEntryAt(0);
manager.popBackStack(first.getId(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
}
Use:
fragmentTransaction.replace(R.id.fragment_container, mFeedFragment);
fragmentTransaction.commit();
Don't do this:
fragmentTransaction.addToBackStack(null);
Follow this simple method
declare an integer as
public int count;
initialise this count as count=0 in your main page
now include this in your main activity
#Override
public void onBackPressed() {
if (count == 1) {
if (exit) {
super.onBackPressed();
return;
} else {
Toast.makeText(this, "Press Back again to Exit.", Toast.LENGTH_SHORT).show();
exit = true;
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
exit = false;
}
}, 2000);
}
}else {
toolbar.setTitle("Home");
mDrawerLayout.closeDrawers();
count=1;
display(0);
}
}
I have a problem with the back/up button in a Fragment.
I have an Activity in which I have some fragments. In one Fragment, that I call "1", I have a list view. When I click on any item it goes to another fragment "2".
I need functionality such that the back/up button only works in Fragment 2 but not in Fragment 1.
Is there a way this can be done?
I have tried this in the Activity, but I don't understand how can this help:
#Override
public void onBackPressed() {
int count = getFragmentManager().getBackStackEntryCount();
if (count == 0) {
super.onBackPressed();
//additional code
} else {
getFragmentManager().popBackStack();
}
}
When i change from fragment 1 to fragment 2 i have this code:
fragment = new MaterialesFragment();
FragmentManager fragmentManager3 = getFragmentManager();
fragmentManager3.beginTransaction().replace(R.id.frame, fragment).addToBackStack("tagMateriales").commit();
Thanks a lot :-)
Try to use addToBackStack() method on the FragmentTransaction object you use to replace the fragment2 into the screen:
fragmentManager.beginTransaction().replace(R.id.content_frame,fragment2).addToBackStack("tagHere").commit();
You can pass null as the parameter if you don't need the actual tag.
Please use this code for your 'fragment 2'
public void showFragmentWithoutStack(Fragment fragment, String tag, int id) {
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);
transaction.add(id, fragment, tag);
transaction.commitAllowingStateLoss();
}
Where tag might be String class name, id of your root view in activity (Because fragment should appear on the top layer).
There are a lot of way to do this job. In my opinion the most elegant is the following. I would isolate the logic in each component. Every fragment implements an interface like "IBackHandler" that allows activity and fragment to communicate. In the activity onBackPressed asks first the fragment (if it is instantiated) what to do and then acts.
The interface:
public interface IBackHandler {
boolean doBack();
}
The fragment that implements that interface and put its logic to handle the back button pressed:
public class YourFragment1 extends YourBaseFragment implements IBackHandler {
// your code
#Override
public boolean doBack() {
// return true means that you have done your stuff
//and you don't want 'super.onBackPressed()' to be called
return true; // this means do nothing
}
}
Fragment 2:
public class YourFragment2 extends YourBaseFragment implements IBackHandler {
// your code
#Override
public boolean doBack() {
return false; //this means: call 'super.onBackPressed()'
}
}
Your Activity:
public class YourActivity extends YourBaseActivity {
//your code
#Override
public void onBackPressed() {
Fragment currentFragment = getFragmentManager().findFragmentById(R.id.yourFragment);
if (currentFragment instanceof IBackHandler) {
if (!((IBackHandler) currentFragment).doBack()) {
super.onBackPressed();
}
} else
super.onBackPressed();
}
}
You could think that this is overengineering but I can reassure you that in this way you'll always be able to control what'happening in your flow.
Hope this help!
In order to use the onBackPressed() method override in the Activity as you have in the question, you also need to add this to your Fragment:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
// Assuming MainActivity is the Activity subclass name
if (getActivity() instanceof MainActivity) {
((MainActivity) getActivity()).onBackPressed();
}
return true;
default:
return super.onOptionsItemSelected(item);
}
}
In order to show/hide the back button, add these methods to your Activity:
public void showUpButton() {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
public void hideUpButton() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
}
Then, when you want to show or hide the back/up button from a Fragment, call these methods.
In your case, you could hide the back/up button from onResume() in Fragment 1:
((MainActivity) getActivity()).hideUpButton();
And then show it in onResume() in Fragment 2:
((MainActivity) getActivity()).showUpButton();
I solved this problem use this code when change fragment:
String fragmentIndexString="fragmenttwo";//or fragmentone something
transaction.addToBackStack();
This is full code in Activity:
FirstFragment firstFragment; // my first fragment
SecondFragment secondFragment; // my second fragment
public static String FIRST="asfasgasg"; //first fragment index
public static String SECOND="gsadgsagd"; //second fragment index
...
/* u can change fragment by using setFragment funiction */
public void setFragment(String page){
FragmentTransaction transaction = fragmentManager.beginTransaction();
switch (page){
case FIRST:
if(firstFragment==null) firstFragment= SettingFragment.newInstance();
if (!firstFragment.isAdded()) {
transaction.replace(R.id.fragmentcontainer, firstFragment);
transaction.addToBackStack(INDEX);
transaction.commit();
} else {
transaction.show(firstFragment);
}
break;
case SECOND:
if(secondFragment==null) secondFragment= SettingFragment.newInstance();
if (!secondFragment.isAdded()) {
transaction.replace(R.id.fragmentcontainer, secondFragment);
transaction.addToBackStack(INDEX);
transaction.commit();
} else {
transaction.show(secondFragment);
}
break;
default:
setFragment(INDEX);
}
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN){
if(fragmentManager.getBackStackEntryCount()>1){
fragmentManager.popBackStack();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
FragmentManager.BackStackEntry bSE = getSupportFragmentManager().getBackStackEntryAt(getSupportFragmentManager().getBackStackEntryCount() - 1);
Fragment frag = getSupportFragmentManager().findFragmentByTag(bSE.getName());
if (frag instanceof BackButtonBlocker)
{
BackButtonBlocker callback = (BackButtonBlocker) frag;
callback.onBackPressed();
}
else {
getSupportFragmentManager().popBackStack();
}
} else {
super.onBackPressed();
}
}
BackButtonBlocker interface is for callback. my fragment implements BackButtonBlocker but sometimes onBackPressed not occurs (onKeyUp as well).
when I open navigation drawer, onBackPressed and full logic works exactly.
why should activity lose focus?
android.support.v4.app.Fragment
public interface BackButtonBlocker{ void onBackPressed();}
take out super.onBackPressed(); from else{}
What I have done is that, I have not implemented any interface to handle back pressed see below.
#Override
public void onBackPressed() {
if (getDrawerLayout().isDrawerOpen(GravityCompat.START)) {
closeLeftMenu();
}
else {
if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
super.onBackPressed();
} else {
finish(); // Means it is home and you can exit it from here.
}
}}
When drawer is open just close it then use your code.
I fixed this issue by adding setFocusableInTouchMode(true); requestFocus(); on Mainactivity's container view
I am trying to override onbackpressed method inside fragment . But it gives me syntax error that it should override a super method in interface. Why so? I have other methods like ondestroy also in my fragment class but no error. Why for this backpressed alone. I tried onkeydown also. Same error. Pasting below my code.
public TestClass extends Fragment implements Testinterface
{ #Override //error must override or implement supertype method
public void onBackPressed ()
{
if (check)
Do somethin
else
getActivity().finish ()
//super.onBackPressed () // error here if I use this
}
You have to implement on key down in fragment, check for key code. The onBackPress() method can be used in an Activity -- which is the logical parent of your fragment.
Try this:
frag.getView().setFocusableInTouchMode(true);
frag.getView().setOnKeyListener( new OnKeyListener(){
#Override
public boolean onKey( View v, int keyCode, KeyEvent event ){
if( keyCode == KeyEvent.KEYCODE_BACK ){
return true;
}
return false;
}
} );
You can propagate onBackPressed() to all your fragments,for that you need to create two classes with the following methods and then make all your activties and fragments inherit from them:
public class BaseActivity extends ActionBarActivity {
#Override
public void onBackPressed() {
boolean eventConsumed = false;
List<Fragment> fragments = getSupportFragmentManager().getFragments();
if (fragments != null) {
for (Fragment fragment : fragments) {
if (fragment instanceof BaseFragment) {
eventConsumed = eventConsumed
|| ((BaseFragment) fragment).onBackPressed();
}
}
}
if (!eventConsumed) {
super.onBackPressed();
}
}
}
public class BaseFragment extends Fragment {
public boolean onBackPressed() {
return false;
}
}
Note that this code is using the support library, if you are not using it you need to do the appropriate changes.
Try to avoid using onKey in fragment. There is a better way.
For maintenance, I recommend you to use getBackStackEntryCount()
in Activity
final FragmentMananger fm = new getSupportFragmentManager();
#Override
protected void onCreate(Bundle b){
super.onCreate(b);
setContentView(R.layout.activity_main);
Fragment fragment = new SomeFragment();
// if you don't run on prior to Android 3.0 use getFragmentManager();
fm.beginTransaction().replace(R.id.frame_container, fragment).addToBackStack(null).commit();
// R.id.frame_container is the id of FrameLayout in activity_main.xml
}
#Override
public void onBackPressed(){
if(fm.getBackStackEntryCount() == 0){
finish();
}else{
super.onBackPressed();
}
}
I have a tabbed Actionbar/viewpager layout with three tabs say A, B, and C. In tab C tab(fragment),I am adding another fragment say fragment D. with
DFragment f= new DFragment();
ft.add(android.R.id.content, f, "");
ft.remove(CFragment.this);
ft.addToBackStack(null);
ft.commit();
I modify actionbar in DFragment's onResume to add up button:
ActionBar ab = getActivity().getActionBar();
ab.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowHomeEnabled(true);
Now in DFragment, when I press hardware(phone) Back button, I return to the original Tabbed(ABC) layout with CFragment selected. How can I achieve this functionality with actionbar up button?
Implement OnBackStackChangedListener and add this code to your Fragment Activity.
#Override
public void onCreate(Bundle savedInstanceState) {
//Listen for changes in the back stack
getSupportFragmentManager().addOnBackStackChangedListener(this);
//Handle when activity is recreated like on orientation Change
shouldDisplayHomeUp();
}
#Override
public void onBackStackChanged() {
shouldDisplayHomeUp();
}
public void shouldDisplayHomeUp(){
//Enable Up button only if there are entries in the back stack
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
getSupportActionBar().setDisplayHomeAsUpEnabled(canGoBack);
}
#Override
public boolean onSupportNavigateUp() {
//This method is called when the up button is pressed. Just the pop back stack.
getSupportFragmentManager().popBackStack();
return true;
}
I got it. just override onOptionsItemSelected in hosting activity and popup the backstack, e.g.
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home: {
FragmentManager fm = getSupportFragmentManager();
if (fm.getBackStackEntryCount() > 0) {
fm.popBackStack();
return true;
}
break;
}
}
return super.onOptionsItemSelected(item);
}
Call getActionBar().setDisplayHomeAsUpEnabled(boolean); and getActionBar().setHomeButtonEnabled(boolean); in onBackStackChanged() as explained in an answer below.
If you have one parent activity and want this up button to work as a back button, you can use this code:
add this to the onCreate in your main activity class
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
int stackHeight = getSupportFragmentManager().getBackStackEntryCount();
if (stackHeight > 0) { // if we have something on the stack (doesn't include the current shown fragment)
getSupportActionBar().setHomeButtonEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
} else {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
getSupportActionBar().setHomeButtonEnabled(false);
}
}
});
and then add onOptionsItemSelected like so:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
....
}
I generally use this all the time and seems pretty legit
you can go back with up button like back button ;
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
super.onBackPressed();
return true;
}
return super.onOptionsItemSelected(item);
}
I used a combination of Roger Garzon Nieto's and sohailaziz's answers. My app has a single MainActivity, and fragments A, B, C that are loaded into it. My "home" fragment (A) implements OnBackStackChangedListener, and checks the size of the backStack; if it's less than one, then it hides the UP button. Fragments B and C always load the back button (in my design, B is launched from A, and C is launched from B). The MainActivity itself just pops the backstack on UP button tap, and has methods to show/hide the button, which the fragments call:
MainActivity:
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
// Respond to the action bar's Up/Home button
case android.R.id.home:
getSupportFragmentManager().popBackStack();
return true;
}
return super.onOptionsItemSelected(item);
}
public void showUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(true); }
public void hideUpButton() { getSupportActionBar().setDisplayHomeAsUpEnabled(false); }
fragmentA (implements FragmentManager.OnBackStackChangedListener):
public void onCreate(Bundle savedinstanceSate) {
// listen to backstack changes
getActivity().getSupportFragmentManager().addOnBackStackChangedListener(this);
// other fragment init stuff
...
}
public void onBackStackChanged() {
// enable Up button only if there are entries on the backstack
if(getActivity().getSupportFragmentManager().getBackStackEntryCount() < 1) {
((MainActivity)getActivity()).hideUpButton();
}
}
fragmentB, fragmentC:
public void onCreate(Bundle savedinstanceSate) {
// show the UP button
((MainActivity)getActivity()).showUpButton();
// other fragment init stuff
...
}
I know this question is old, but may be someone (like me) also needs it.
If your Activity extends AppCompatActivity, you can use a simpler (two-step) solution:
1 - Whenever you add a non-home fragment just show the up button, right after commiting the fragment transaction. Like this:
// ... add a fragment
// Commit the transaction
transaction.commit();
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
2 - Then when UP button is pressed, you hide it.
#Override
public boolean onSupportNavigateUp() {
getSupportActionBar().setDisplayHomeAsUpEnabled(false);
return true;
}
That's it.
This worked for me. Override onSupportNavigateUp and onBackPressed, for example (code in Kotlin);
override fun onBackPressed() {
val count = supportFragmentManager.backStackEntryCount
if (count == 0) {
super.onBackPressed()
} else {
supportFragmentManager.popBackStack()
}
}
override fun onSupportNavigateUp(): Boolean {
super.onSupportNavigateUp()
onBackPressed()
return true
}
Now in the fragment, if you display the up arrow
activity.supportActionBar?.setDisplayHomeAsUpEnabled(true)
Clicking on it takes you back the previous activity.
Kotlin:
class MyActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
...
supportFragmentManager.addOnBackStackChangedListener { setupHomeAsUp() }
setupHomeAsUp()
}
private fun setupHomeAsUp() {
val shouldShow = 0 < supportFragmentManager.backStackEntryCount
supportActionBar?.setDisplayHomeAsUpEnabled(shouldShow)
}
override fun onSupportNavigateUp(): Boolean =
supportFragmentManager.popBackStack().run { true }
...
}
This is a very good and reliable solution: http://vinsol.com/blog/2014/10/01/handling-back-button-press-inside-fragments/
The guy has made an abstract fragment that handles the backPress behaviour and is switching between the active fragments using the strategy pattern.
For some of you there maybe a little drawback in the abstract class...
Shortly, the solution from the link goes like this:
// Abstract Fragment handling the back presses
public abstract class BackHandledFragment extends Fragment {
protected BackHandlerInterface backHandlerInterface;
public abstract String getTagText();
public abstract boolean onBackPressed();
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(!(getActivity() instanceof BackHandlerInterface)) {
throw new ClassCastException("Hosting activity must implement BackHandlerInterface");
} else {
backHandlerInterface = (BackHandlerInterface) getActivity();
}
}
#Override
public void onStart() {
super.onStart();
// Mark this fragment as the selected Fragment.
backHandlerInterface.setSelectedFragment(this);
}
public interface BackHandlerInterface {
public void setSelectedFragment(BackHandledFragment backHandledFragment);
}
}
And usage in the activity:
// BASIC ACTIVITY CODE THAT LETS ITS FRAGMENT UTILIZE onBackPress EVENTS
// IN AN ADAPTIVE AND ORGANIZED PATTERN USING BackHandledFragment
public class TheActivity extends FragmentActivity implements BackHandlerInterface {
private BackHandledFragment selectedFragment;
#Override
public void onBackPressed() {
if(selectedFragment == null || !selectedFragment.onBackPressed()) {
// Selected fragment did not consume the back press event.
super.onBackPressed();
}
}
#Override
public void setSelectedFragment(BackHandledFragment selectedFragment) {
this.selectedFragment = selectedFragment;
}
}
If you want to go back to your previous activity if this activity has an empty stack of fragments:
This could be useful if you have a MainActivity and you are navigating to e.g. a SettingsActivity with nested prefernceScreens. NavigateUp will pop fragments until you can finish the SettingsActivity to go back to parentActivity/root.
/**
* On actionbar up-button popping fragments from stack until it is empty.
* #return true if fragment popped or returned to parent activity successfully.
*/
#Override
public boolean onSupportNavigateUp() {
//Pop back stack if the up button is pressed.
boolean canGoBack = getSupportFragmentManager().getBackStackEntryCount()>0;
if (canGoBack) {
getSupportFragmentManager().popBackStack();
} else {
finish();
return super.onSupportNavigateUp();
}
return true;
}
Note: setDisplayHomeAsUpEnabled(true); in fragment activities onCreate()