Related
I'd like to use the BackButton in Fragments. I'm using this code to handle backbutton:
#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){
if (idozit.num > 0) {
if (!pmenu.pauseopen) {
pmenu.BeingPaused(idozit.idozitomegy,nextlevel,0);
} else {
pmenu.continuegame();
}
}
if (idozit.num == 0) {
idozit.numnull(db);
}
//Toast.makeText(getActivity(), "hello1", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
When I click on BackButton this code works fine BUT if click on the BackButton again the app calls the onBackPressed method from MainActivity. I don't know why but if I'm using only a Toast or Log.d something like that in the onKey method then I'm able to click it again.
I'd like to say that pmenu is a simple class which is only stops music,make things gone etc. It's seems like somehow I always stuck in that class .
Have you any idea what am I doing wrong?
A cleaner solution is to make an Interface implemented by every fragment with a method called onBackPressed() like this:
public interface FragmentInterface {
void onBackPressed();
}
Then, you override the onBackPressed in yout activity calling your current fragment's onBackPressed (I'm assuming that you have a method to get your currentFragment)
#Override
public void onBackPressed(){
FragmentInterface currentfragment = getCurrentFragment();
currentfragment.onBackPressed();
}
Of course, in your fragment, implemented method should look like this:
#Override
public void onBackPressed() {
if (idozit.num > 0) {
if (!pmenu.pauseopen) {
pmenu.BeingPaused(idozit.idozitomegy,nextlevel,0);
} else {
pmenu.continuegame();
}
}
if (idozit.num == 0) {
idozit.numnull(db);
}
//Toast.makeText(getActivity(), "hello1", Toast.LENGTH_SHORT).show();
}
Replace false in last line with true and there's no need for return true before the last one
My application has an activity with so many fragments. I want to disable the back button press in some fragment. I tried with the below code. But it doesn't work.
In the main activity:
#Override
public void onBackPressed() {
super.onBackPressed();
OrderFragment.onBackPress();
}
In the fragment,
public static void onBackPressed()
{
Log.d(TAG,"It listen");
}
I have the log message but, how can I disable the back button from my fragment.
You should keep a reference to the fragment you want to disable/handle back press event on your main activity:
class MainActivity{
OrderFragment mOrderFragment;
#Override
public void onBackPressed() {
if(mOrderFragment.isVisible())
mOrderFragment.onBackPressed();
else
super.onBackPressed();
}
}
In OrderFragment:
public void onBackPressed() {
//handle back press event
}
In your oncreateView() method you need to write this code and in KEYCODE_BACk return should true then it will stop the back button option for particular fragment
View v = inflater.inflate(R.layout.xyz, container, false);
//Back pressed Logic for fragment
v.setFocusableInTouchMode(true);
v.requestFocus();
v.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
}
}
return false;
}
});
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if( keyCode == KeyEvent.KEYCODE_BACK ) {
// leave this blank in order to disable the back press
return true;
} else {
return false;
}
}
});
I think you have to Override onResume method in fragments which you need to disable the back press.Tryout the following code.
#Override
public void onResume() {
super.onResume();
this.getView().setFocusableInTouchMode(true);
this.getView().requestFocus();
this.getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
return true;
}
return false;
}
});
}
Have you tried like this, Actually it works for me before.
you can set listener for Back key. if you add it, i guess it works.
youfragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
return true;
}
return false;
}
}
);
super.onBackPressed in the activity is the default implementation. Remove this if you don't want it.
I know this is an old question and because of navigation components and new ways this situation rarely occurs, still, maybe my answer helps someone.
I came across a similar situation where I needed the back button to be disabled and I couldn't change the activity as well. So in a fragment, I added this code block.
Language: Kotlin
override fun onResume() {
super.onResume()
this.requireView().isFocusableInTouchMode = true
this.requireView().requestFocus()
this.requireView().setOnKeyListener { _, keyCode, _ ->
keyCode == KeyEvent.KEYCODE_BACK
}
}
This will disable your back press on that specific fragment
#Override
public void onBackPressed() {
Fragment currentFragment = getSupportFragmentManager().findFragmentById(R.id.fragment_container);
if (currentFragment instanceof YourFragment) {
currentFragment.onBackPressed();
} else
super.onBackPressed();
}
override fun onBackPressed() {
val currentFragment = findNavController(R.id.nav_host_fragment).currentDestination?.id
if (currentFragment == R.id.YourFragment) {
return
}
super.onBackPressed()
If you don't want your fragment to be in the backstack you can do so, by not adding it to the backstack in the transaction. So do NOT call FragmentTransaction.addToBackStack(); on the fragments you do not want in your backstack.
onBackPressed works like this:
public void More onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
Thus when you call super.onBackPressed() you pop the BackStack before finishing the activity. The best way to sort this out is to make sure you do not add the fragments to the BackStack. It is normally done like this:
fragmentTransaction.addToBackStack(TAG);
An alternative solution, but uglier since you have still useless code left, would be making onBackPressed() directly call finish() to end the activity.
I would strongly advise you not use getView().setOnKeyListener(...) since it is code smell.
Try this
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}else{
super.onBackPressed();}
}
I am now having an activity containing fragments
[1] , [2] , [3] , [4]
If pressing buttons , [3] , it can be redirected to [4]
I would like to implement the back button as shown follow..
when pressing back at [4] , it return to [3]
when pressing back at [3] , it return to [2]
when pressing back at [1] , the activity finishes();
When it comes to the current implementation, it finish the activity instead of popping up the Fragment. Would you please tell me what I should do or keep in mind ?
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if( keyCode==KeyEvent.KEYCODE_BACK)
{
finish();
}
return super.onKeyDown(keyCode, event);
}
This worked for me.
-Add .addToBackStack(null) when you call the new fragment from activity.
FragmentTransaction mFragmentTransaction = getFragmentManager()
.beginTransaction();
....
mFragmentTransaction.addToBackStack(null);
-Add onBackPressed() to your activity
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
this.finish();
} else {
getFragmentManager().popBackStack();
}
}
Easiest way ever:
onResume():
#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's click listener
Toast.makeText(getActivity(), "Back press", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
Edit 1: If fragment having EditText.
private EditText editText;
onCreateView():
editText = (EditText) rootView.findViewById(R.id.editText);
onResume():
#Override
public void onResume() {
super.onResume();
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
editText.clearFocus();
}
return false;
}
});
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's click listener
Toast.makeText(getActivity(), "Back press", Toast.LENGTH_SHORT).show();
return true;
}
return false;
}
});
}
Note: It will work if you have EditText in fragment.
Done
This is a working solution for me:
dialog.setOnKeyListener(new DialogInterface.OnKeyListener() {
#Override
public boolean onKey(DialogInterface dialog, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// DO WHAT YOU WANT ON BACK PRESSED
return true;
}
return false;
}
});
Edit: You can replace dialog with getView() for fragments.
Try this simple solution:
In your activity implement onBackPressed
#Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
getSupportFragmentManager().popBackStack();
} else {
finish();
}
}
This will work if you want to pop the top fragment on each back press.
Note:- While adding fragment to activity always do add the transaction to back stack for this to work
In your onCreate() in your activity housing your fragments add a backstack change listener like so:
fragmentManager.addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
List<Fragment> f = fragmentManager.getFragments();
Fragment frag = f.get(0);
currentFragment = frag.getClass().getSimpleName();
}
});
(Nb. my fragmentManager is declared global)
Now every time you change fragment the currentFragment String will become the name of the current fragment. Then in the activities onBackPressed() you can control the actions of your back button as so:
#Override
public void onBackPressed() {
switch (currentFragment) {
case "FragmentOne":
// your code here
return;
case "FragmentTwo":
// your code here
return;
default:
fragmentManager.popBackStack();
// default action for any other fragment (return to previous)
}
}
I can confirm that this method works for me.
Update : Kotlin
override fun onBackPressed() {
when(supportFragmentManager.fragments[0].javaClass.simpleName){
"FragmentOne" -> doActionOne()
"FragmentTwo" -> doActionTwo()
else -> supportFragmentManager.popBackStack()
}
}
Solution for Pressing or handling back button in Fragment.
The way I solved my issue I am sure it will helps you too:
1.If you don't have any Edit Text-box in your fragment you can use below code
Here MainHomeFragment is main Fragment (When I press back button from second fragment it will take me too MainHomeFragment)
#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){
MainHomeFragment mainHomeFragment = new SupplierHomeFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction =
getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, mainHomeFragment);
fragmentTransaction.commit();
return true;
}
return false;
}
}); }
2.If you have another fragment named as Somefragment and it has Edit text-box then you can do it by this way.
private EditText editText;
Then In,
onCreateView():
editText = (EditText) view.findViewById(R.id.editText);
Then Override OnResume,
#Override
public void onResume() {
super.onResume();
editText.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
editTextOFS.clearFocus();
getView().requestFocus();
}
return false;
}
});
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){
MainHomeFragment mainHomeFragment = new SupplierHomeFragment();
android.support.v4.app.FragmentTransaction fragmentTransaction =
getActivity().getSupportFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragment_container, mainHomeFragment);
fragmentTransaction.commit();
return true;
}
return false;
}
});
}
That's all folks (amitamie.com) :-) ;-)
Still better solution could be to follow a design pattern such that the back-button press event gets propagated from active fragment down to host Activity. So, it's like.. if one of the active fragments consume the back-press, the Activity wouldn't get to act upon it, and vice-versa.
One way to do it is to have all your Fragments extend a base fragment that has an abstract 'boolean onBackPressed()' method.
#Override
public boolean onBackPressed() {
if(some_condition)
// Do something
return true; //Back press consumed.
} else {
// Back-press not consumed. Let Activity handle it
return false;
}
}
Keep track of active fragment inside your Activity and inside it's onBackPressed callback write something like this
#Override
public void onBackPressed() {
if(!activeFragment.onBackPressed())
super.onBackPressed();
}
}
This post has this pattern described in detail
What I do in this cases is I implement the onBackPressed() function from the Activity:
#Override
public void onBackPressed() {
super.onBackPressed();
FragmentManager fm = getSupportFragmentManager();
MyFragment myFragment = (MyFragment) fm.findFragmentById(R.id.my_fragment);
if((myFragmen.isVisible()){
//Do what you want to do
}
}
How this works for you too.
if you are using webview inside a fragment than use this in your onCreateView method
webView.setOnKeyListener(new View.OnKeyListener(){
#Override
public boolean onKey(View view, int i, KeyEvent keyEvent) {
if((i==KeyEvent.KEYCODE_BACK)&& webView.canGoBack()){
webView.goBack();
return true;
}
return false;
}
});
and import this class
import android.view.KeyEvent;
You can use getFragmentManager().popBackStack() in basic Fragment to go back.
You also need to check Action_Down or Action_UP event. If you will not check then onKey() Method will call 2 times.
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Toast.makeText(getActivity(), "Back Pressed", Toast.LENGTH_SHORT).show();
return true;
}
}
return false;
}
});
Working very well for me.
you can use this one in onCreateView, you can use transaction or replace
OnBackPressedCallback callback = new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
//what you want to do
}
};
requireActivity().getOnBackPressedDispatcher().addCallback(this, callback);
Make sure to add the following:
if (event.getAction()!=KeyEvent.ACTION_DOWN)
return true;
in the onKey block of code to avoid the event calling twice.
i use a methode to change fragments it has thw following code
getSupportFragmentManager().beginTransaction().setCustomAnimations(R.anim.enter, R.anim.exit, R.anim.pop_enter, R.anim.pop_exit).replace(R.id.content_frame, mContent, mContent.getClass().getSimpleName()).addToBackStack(null)
.commit();
and for the back button this .
#Override
public void onBackPressed() {
// note: you can also use 'getSupportFragmentManager()'
FragmentManager mgr = getSupportFragmentManager();
if (mgr.getBackStackEntryCount() == 1) {
// No backstack to pop, so calling super
finish();
} else {
mgr.popBackStack();
}
}
the important thing to note is i use 1 for checking getBackStackEntryCount this is because if you dont use it and use 0 user sees nothing for the last back button.
use this (in kotlin)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// in here you can do logic when backPress is clicked
}
})
i think this is the most elegant way to do it
Try this, its helped me :)
#Override
public void onResume() { //Pressed return button - returns to the results menu
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) {
/*Here your code*/
return true;
}
return false;
}
});
}
just paste it in your activity main
#Override
public void onBackPressed() {
DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
if (drawer.isDrawerOpen(GravityCompat.START)) {
drawer.closeDrawer(GravityCompat.START);
} else if (getSupportFragmentManager().getBackStackEntryCount() > 1) {
getSupportFragmentManager().popBackStack();
} else {
finish();
}
}
I am working on a project and I need to be able to use the back button in each fragment to navigate between previous fragments, I have methods written to do so by using a back arrow in the action bar, however, I want to be able to use the same functionality on the back button pressed. I don't want to use the back stack. Is there a way to do this?
EDIT
Rather than using the back stack I want to be able to call the go back to previous method below when the user clicks the back button. I need to used the gobackpressed method within fragments. Is this possible? I hope this is clear and concise. Apologies for any confusion caused above.
Go Back to Previous
public void gobackToPreviousFragment(String preFragmentTag, Fragment preFragment){
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.close_slide_in,R.animator.close_slide_out);
ft.show(preFragment);
//**BY REMOVING FRAGMENT, WHEN USER TRIES TO REVISIT, FRAGMENT IS BLACK**
ft.remove(fm.findFragmentByTag(Misc.currentContentFragmentTag));
ft.addToBackStack(null);
ft.commit();
Misc.currentContentFragmentTag = preFragmentTag;
createBar(preFragment);
}
Go Forward
public void gotoNextFragment(String nextTag, Fragment nextFragment){
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.setCustomAnimations(R.animator.enter_slide_in, R.animator.enter_slide_out);
boolean newlyCreated = false;
if(nextFragment == null){
nextFragment = Fragment.instantiate(this, nextTag);
newlyCreated = true;
}
//hide current fragment
ft.hide(fm.findFragmentByTag(Misc.currentContentFragmentTag));
if(newlyCreated){
ft.add(R.id.content_frame, nextFragment, nextTag);
}
else{
ft.show(nextFragment);
}
ft.addToBackStack(null);
ft.commit();
Misc.currentContentFragmentTag = nextTag;
createBar(nextFragment);
}
These are how I navigate back and forth, and I'd like to be able to implement the go back method on the onBackPressed(). Does this make sense?
I didn't find any good answer about this problem, so this is my solution.
If you want to get backPress in each fragment do the following.
create interface OnBackPressedListener
public interface OnBackPressedListener {
void onBackPressed();
}
That each fragment that wants to be informed of backPress implements this interface.
In parent activity , you can override onBackPressed()
#Override
public void onBackPressed() {
List<Fragment> fragmentList = getSupportFragmentManager().getFragments();
if (fragmentList != null) {
//TODO: Perform your logic to pass back press here
for(Fragment fragment : fragmentList){
if(fragment instanceof OnBackPressedListener){
((OnBackPressedListener)fragment).onBackPressed();
}
}
}
}
Why don't you want to use the back stack? If there is an underlying problem or confusion maybe we can clear it up for you.
If you want to stick with your requirement just override your Activity's onBackPressed() method and call whatever method you're calling when the back arrow in your ActionBar gets clicked.
EDIT: How to solve the "black screen" fragment back stack problem:
You can get around that issue by adding a backstack listener to the fragment manager. That listener checks if the fragment back stack is empty and finishes the Activity accordingly:
You can set that listener in your Activity's onCreate method:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentManager fm = getFragmentManager();
fm.addOnBackStackChangedListener(new OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getFragmentManager().getBackStackEntryCount() == 0) finish();
}
});
}
requireActivity().onBackPressedDispatcher.addCallback(viewLifecycleOwner, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
Log.w("a","")
}
})
In the fragment where you would like to handle your back button you should attach stuff to your view in the oncreateview
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.second_fragment, container, false);
v.setOnKeyListener(pressed);
return v;
}
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if( keyCode == KeyEvent.KEYCODE_BACK ){
// back to previous fragment by tag
myfragmentclass fragment = (myfragmentclass) getActivity().getSupportFragmentManager().findFragmentByTag(TAG);
if(fragment != null){
(getActivity().getSupportFragmentManager().beginTransaction()).replace(R.id.cf_g1_mainframe_fm, fragment).commit();
}
return true;
}
return false;
}
};
This works for me :D
#Override
public void onResume() {
super.onResume();
if(getView() == null){
return;
}
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's click listener
return true;
}
return false;
}
});}
You can try to override onCreateAnimation, parameter and catch enter==false. This will fire before every back press.
#Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
if(!enter){
//leaving fragment
Log.d(TAG,"leaving fragment");
}
return super.onCreateAnimation(transit, enter, nextAnim);
}
For a Fragment you can simply add
getActivity().onBackPressed();
to your code
I found a new way to do it without interfaces. You only need to add the below code to the Fragment’s onCreate() method:
//overriding the fragment's oncreate
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//calling onBackPressedDispatcher and adding call back
requireActivity().onBackPressedDispatcher.addCallback(this) {
//do stuff here
}
}
Use this:
#Override
public void onBackPressed() {
int fragments = getFragmentManager().getBackStackEntryCount();
if (fragments == 1) {
finish();
}
super.onBackPressed();
}
This question already has answers here:
How to implement onBackPressed() in Fragments?
(58 answers)
Closed 6 years ago.
I have some fragments in my activity
[1], [2], [3], [4], [5], [6]
And on Back Button Press I must to return from [2] to [1] if current active fragment is [2], or do nothing otherwise.
What is the best practise to do that?
EDIT: Application must not return to [2] from [3]...[6]
When you are transitioning between Fragments, call addToBackStack() as part of your FragmentTransaction:
FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag" ).commit();
If you require more detailed control (i.e. when some Fragments are visible, you want to suppress the back key) you can set an OnKeyListener on the parent view of your fragment:
//You need to add the following line for this solution to work; thanks skayred
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
return true;
}
return false;
}
} );
I'd rather do something like this:
private final static String TAG_FRAGMENT = "TAG_FRAGMENT";
private void showFragment() {
final Myfragment fragment = new MyFragment();
final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment, fragment, TAG_FRAGMENT);
transaction.addToBackStack(null);
transaction.commit();
}
#Override
public void onBackPressed() {
final Myfragment fragment = (Myfragment) getSupportFragmentManager().findFragmentByTag(TAG_FRAGMENT);
if (fragment.allowBackPressed()) { // and then you define a method allowBackPressed with the logic to allow back pressed or not
super.onBackPressed();
}
}
if you overide the onKey method for the fragment view you're gonna need :
view.setFocusableInTouchMode(true);
view.requestFocus();
view.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.i(tag, "keyCode: " + keyCode);
if( keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
Log.i(tag, "onKey Back listener is working!!!");
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
return true;
}
return false;
}
});
Use addToBackStack method when replacing one fragment by another:
getFragmentManager().beginTransaction().replace(R.id.content_frame, fragment).addToBackStack("my_fragment").commit();
Then in your activity, use the following code to go back from a fragment to another (the previous one).
#Override
public void onBackPressed() {
if (getParentFragmentManager().getBackStackEntryCount() > 0) {
getParentFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
If you want to handle hardware Back key event than you have to do following code in your onActivityCreated() method of Fragment.
You also need to check Action_Down or Action_UP event. If you will not check then onKey() Method will call 2 times.
Also, If your rootview(getView()) will not contain focus then it will not work. If you have clicked on any control then again you need to give focus to rootview using getView().requestFocus(); After this only onKeydown() will call.
getView().setFocusableInTouchMode(true);
getView().requestFocus();
getView().setOnKeyListener(new OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Toast.makeText(getActivity(), "Back Pressed", Toast.LENGTH_SHORT).show();
return true;
}
}
return false;
}
});
Working very well for me.
Create interfaces:
BackButtonHandlerInterface
public interface BackButtonHandlerInterface {
void addBackClickListener (OnBackClickListener onBackClickListener);
void removeBackClickListener (OnBackClickListener onBackClickListener);
}
OnBackClickListener
public interface OnBackClickListener {
boolean onBackClick();
}
In Activity:
public class MainActivity extends AppCompatActivity implements BackButtonHandlerInterface {
private ArrayList<WeakReference<OnBackClickListener>> backClickListenersList = new ArrayList<>();
#Override
public void addBackClickListener(OnBackClickListener onBackClickListener) {
backClickListenersList.add(new WeakReference<>(onBackClickListener));
}
#Override
public void removeBackClickListener(OnBackClickListener onBackClickListener) {
for (Iterator<WeakReference<OnBackClickListener>> iterator = backClickListenersList.iterator();
iterator.hasNext();){
WeakReference<OnBackClickListener> weakRef = iterator.next();
if (weakRef.get() == onBackClickListener){
iterator.remove();
}
}
}
#Override
public void onBackPressed() {
if(!fragmentsBackKeyIntercept()){
super.onBackPressed();
}
}
private boolean fragmentsBackKeyIntercept() {
boolean isIntercept = false;
for (WeakReference<OnBackClickListener> weakRef : backClickListenersList) {
OnBackClickListener onBackClickListener = weakRef.get();
if (onBackClickListener != null) {
boolean isFragmIntercept = onBackClickListener.onBackClick();
if (!isIntercept) isIntercept = isFragmIntercept;
}
}
return isIntercept;
}
}
In Fragment:
public class MyFragment extends Fragment implements OnBackClickListener{
private BackButtonHandlerInterface backButtonHandler;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
backButtonHandler = (BackButtonHandlerInterface) activity;
backButtonHandler.addBackClickListener(this);
}
#Override
public void onDetach() {
super.onDetach();
backButtonHandler.removeBackClickListener(this);
backButtonHandler = null;
}
#Override
public boolean onBackClick() {
//This method handle onBackPressed()! return true or false
return false;
}
}
Update
Provide custom back navigation
class MyFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// This callback will only be called when MyFragment is at least Started.
val callback = requireActivity().onBackPressedDispatcher.addCallback(this) {
// Handle the back button event
}
// The callback can be enabled or disabled here or in the lambda
}
}
The most ideal way of doing this is found here:
Fragment: which callback invoked when press back button & customize it
public class MyActivity extends Activity
{
//...
//Defined in Activity class, so override
#Override
public void onBackPressed()
{
super.onBackPressed();
myFragment.onBackPressed();
}
}
public class MyFragment extends Fragment
{
//Your created method
public static void onBackPressed()
{
//Pop Fragments off backstack and do your other checks
}
}
#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){
if (mDrawerLayout.isDrawerOpen(GravityCompat.START)){
mDrawerLayout.closeDrawer(GravityCompat.START);
}
return true;
}
return false;
}
});
}
After looking at all solutions, I realised there is a much simpler solution.
In your activity's onBackPressed() that is hosting all your fragments, find the fragment that you want to prevent back press. Then if found, just return. Then popBackStack will never happen for this fragment.
#Override
public void onBackPressed() {
Fragment1 fragment1 = (Fragment1) getFragmentManager().findFragmentByTag(“Fragment1”);
if (fragment1 != null)
return;
if (getFragmentManager().getBackStackEntryCount() > 0){
getFragmentManager().popBackStack();
}
}
We created tiny library for handling back press across multiple fragments and/or in Activity. Usage is as simple as adding dependency in your gradle file:
compile 'net.skoumal.fragmentback:fragment-back:0.1.0'
Let your fragment implement BackFragment interface:
public abstract class MyFragment extends Fragment implements BackFragment {
public boolean onBackPressed() {
// -- your code --
// return true if you want to consume back-pressed event
return false;
}
public int getBackPriority() {
return NORMAL_BACK_PRIORITY;
}
}
Notify your fragments about back presses:
public class MainActivity extends AppCompatActivity {
#Override
public void onBackPressed() {
// first ask your fragments to handle back-pressed event
if(!BackFragmentHelper.fireOnBackPressedEvent(this)) {
// lets do the default back action if fragments don't consume it
super.onBackPressed();
}
}
}
For more details and other use-cases visit GitHub page:
https://github.com/skoumalcz/fragment-back
Or you could use getSupportFragmentManager().getBackStackEntryCount() to check what to do:
#Override
public void onBackPressed() {
logger.d("###### back stack entry count : " + getSupportFragmentManager().getBackStackEntryCount());
if (getSupportFragmentManager().getBackStackEntryCount() != 0) {
// only show dialog while there's back stack entry
dialog.show(getSupportFragmentManager(), "ConfirmDialogFragment");
} else if (getSupportFragmentManager().getBackStackEntryCount() == 0) {
// or just go back to main activity
super.onBackPressed();
}
}
If you manage the flow of adding to back stack every transaction, then you can do something like this in order to show the previous fragment when the user presses back button (you could map the home button too).
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0)
getFragmentManager().popBackStack();
else
super.onBackPressed();
}
For Those Who Use Static Fragment
In a case if you have a static fragment then It would be preferable.
Make an instance object of your fragment
private static MyFragment instance=null;
in onCreate() of MyFragment initialize that instance
instance=this;
also make a function to get Instance
public static MyFragment getInstance(){
return instance;
}
also make functions
public boolean allowBackPressed(){
if(allowBack==true){
return true;
}
return false;
}
//allowBack is a boolean variable that will be set to true at the action
//where you want that your backButton should not close activity. In my case I open
//Navigation Drawer then I set it to true. so when I press backbutton my
//drawer should be get closed
public void performSomeAction(){
//.. Your code
///Here I have closed my drawer
}
In Your Activity You can do
#Override
public void onBackPressed() {
if (MyFragment.getInstance().allowBackPressed()) {
MyFragment.getInstance().performSomeAction();
}
else{
super.onBackPressed();
}
}
Working Code:
package com.example.keralapolice;
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentManager.OnBackStackChangedListener;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
public class ChiefFragment extends Fragment {
View view;
// public OnBackPressedListener onBackPressedListener;
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup container, Bundle args) {
view = inflater.inflate(R.layout.activity_chief, container, false);
getActivity().getActionBar().hide();
view.setFocusableInTouchMode(true);
view.requestFocus();
view.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
Log.i(getTag(), "keyCode: " + keyCode);
if (keyCode == KeyEvent.KEYCODE_BACK) {
getActivity().getActionBar().show();
Log.i(getTag(), "onKey Back listener is working!!!");
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
// String cameback="CameBack";
Intent i = new Intent(getActivity(), home.class);
// i.putExtra("Comingback", cameback);
startActivity(i);
return true;
} else {
return false;
}
}
});
return view;
}
}
I think the easiest way is to create an interface, and in the Activity check if the fragment is of the interface type, and if so, call its method to handle the pop. Here's the interface to implement in the fragment.
public interface BackPressedFragment {
// Note for this to work, name AND tag must be set anytime the fragment is added to back stack, e.g.
// getActivity().getSupportFragmentManager().beginTransaction()
// .replace(R.id.fragment_container, MyFragment.newInstance(), "MY_FRAG_TAG")
// .addToBackStack("MY_FRAG_TAG")
// .commit();
// This is really an override. Should call popBackStack itself.
void onPopBackStack();
}
Here's how to implement it.
public class MyFragment extends Fragment implements BackPressedFragment
#Override
public void onPopBackStack() {
/* Your code goes here, do anything you want. */
getActivity().getSupportFragmentManager().popBackStack();
}
And in your Activity, when you handle the pop (likely in both onBackPressed and onOptionsItemSelected), pop the backstack using this method:
public void popBackStack() {
FragmentManager fm = getSupportFragmentManager();
// Call current fragment's onPopBackStack if it has one.
String fragmentTag = fm.getBackStackEntryAt(fm.getBackStackEntryCount() - 1).getName();
Fragment currentFragment = getSupportFragmentManager().findFragmentByTag(fragmentTag);
if (currentFragment instanceof BackPressedFragment)
((BackPressedFragment)currentFragment).onPopBackStack();
else
fm.popBackStack();
}
I'm working with SlidingMenu and Fragment, present my case here and hope helps somebody.
Logic when [Back] key pressed :
When SlidingMenu shows, close it, no more things to do.
Or when 2nd(or more) Fragment showing, slide back to previous Fragment, and no more things to do.
SlidingMenu not shows, current Fragment is #0, do the original [Back] key does.
public class Main extends SherlockFragmentActivity
{
private SlidingMenu menu=null;
Constants.VP=new ViewPager(this);
//Some stuff...
#Override
public void onBackPressed()
{
if(menu.isMenuShowing())
{
menu.showContent(true); //Close SlidingMenu when menu showing
return;
}
else
{
int page=Constants.VP.getCurrentItem();
if(page>0)
{
Constants.VP.setCurrentItem(page-1, true); //Show previous fragment until Fragment#0
return;
}
else
{super.onBackPressed();} //If SlidingMenu is not showing and current Fragment is #0, do the original [Back] key does. In my case is exit from APP
}
}
}
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;
}
}
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Fragment NameofFragment = new NameofFragment;
FragmentTransaction transaction=getFragmentManager().beginTransaction();
transaction.replace(R.id.frame_container,NameofFragment);
transaction.commit();
return true;
}
return false;
}
});
return rootView;
Add addToBackStack() to fragment transaction and then use below code for Implementing Back Navigation for Fragments
getSupportFragmentManager().addOnBackStackChangedListener(
new FragmentManager.OnBackStackChangedListener() {
public void onBackStackChanged() {
// Update your UI here.
}
});
if you are using FragmentActivity. then do like this
first call This inside your Fragment.
public void callParentMethod(){
getActivity().onBackPressed();
}
and then Call onBackPressed method in side your parent FragmentActivity class.
#Override
public void onBackPressed() {
//super.onBackPressed();
//create a dialog to ask yes no question whether or not the user wants to exit
...
}
You can use from getActionBar().setDisplayHomeAsUpEnabled() :
#Override
public void onBackStackChanged() {
int backStackEntryCount = getFragmentManager().getBackStackEntryCount();
if(backStackEntryCount > 0){
getActionBar().setDisplayHomeAsUpEnabled(true);
}else{
getActionBar().setDisplayHomeAsUpEnabled(false);
}
}
Add this code in your Activity
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() == 0) {
super.onBackPressed();
} else {
getFragmentManager().popBackStack();
}
}
And add this line in your Fragment before commit()
ft.addToBackStack("Any name");
in fragment class put this code for back event:
rootView.setFocusableInTouchMode(true);
rootView.requestFocus();
rootView.setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
FragmentManager fragmentManager = getFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.frame_container, new Book_service_provider()).commit();
return true;
}
return false;
}
} );
Checking the backstack works perfectly
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
if (getFragmentManager().getBackStackEntryCount() == 1)
{
// DO something here since there is only one fragment left
// Popping a dialog asking to quit the application
return false;
}
}
return super.onKeyDown(keyCode, event);
}
In your oncreateView() method you need to write this code and in KEYCODE_BACk condition you can write whatever the functionality you want
View v = inflater.inflate(R.layout.xyz, container, false);
//Back pressed Logic for fragment
v.setFocusableInTouchMode(true);
v.requestFocus();
v.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
getActivity().finish();
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
return true;
}
}
return false;
}
});