How to implement onBackPressed in fragment and reach HomeFragment? - android

I am using 3 Fragment(HomeFragment,RegisterFragment and NewsFragment).How to move from NewsFragment to direct HomeFragment, when I click on Back Button. I don,t want to move to other Fragment ,when I click on Back Button.

I made a utility class which will control my fragment navigation.
Here is what this class looks like:
public class FragmentController {
private static final String TAG = FragmentController.class.getCanonicalName();
private static FragmentController mInstance;
private ArrayList<BaseFragment> mFragmentsList;
private BaseActivity mActivity;
private FrameLayout mFragmentContainer;
public FragmentController(BaseActivity activity) {
set(activity);
}
public static FragmentController getInstance(BaseActivity activity) {
if (mInstance == null)
mInstance = new FragmentController(activity);
return mInstance;
}
public static void setInstance(FragmentController mInstance) {
FragmentController.mInstance = mInstance;
}
public void set(BaseActivity activity) {
mActivity = activity;
if (mActivity instanceof MainActivity) {
mFragmentContainer = ((MainActivity) mActivity).getFragmentContainer();
}
mFragmentsList = new ArrayList<>();
}
public void addFirstFragment(BaseFragment fragment) {
mFragmentsList.add(fragment);
}
public void presentFragment(BaseFragment fragment, boolean removeAllFromBackstack) {
try {
Log.d(TAG, "presentFragment: " + fragment.getTagFm());
if (mActivity.getCurrentFocus() != null) {
ViewUtils.hideKeyboard(mActivity.getCurrentFocus());
}
FragmentTransaction ft = mActivity.getSupportFragmentManager().beginTransaction();
ft.setCustomAnimations(R.anim.fade_in, R.anim.fade_out);
ft.replace(mFragmentContainer.getId(), fragment, fragment.getTagFm());
ft.addToBackStack(fragment.getTagFm());
ft.commitAllowingStateLoss();
if (mFragmentsList.size() != 0)
if (removeAllFromBackstack) {
if (mFragmentsList.size() == 2) {
mFragmentsList.remove(1);
} else if (mFragmentsList.size() > 2) {
mFragmentsList.subList(1, mFragmentsList.size()).clear();
}
}
if (!isFragmentPresent(fragment))
mFragmentsList.add(fragment);
} catch (Exception e) {
e.printStackTrace();
}
}
public void removeSecondFragment() {
mFragmentsList.remove(1);
}
private boolean isFragmentPresent(BaseFragment fragment) {
for (BaseFragment baseFragment : mFragmentsList) {
if (baseFragment.getTagFm().equals(fragment.getTagFm()))
return true;
}
return false;
}
public BaseFragment getCurrentFragment() {
return mFragmentsList.get(mFragmentsList.size() - 1);
}
public ArrayList<BaseFragment> getFragmentsList() {
return mFragmentsList;
}
}
Now, I instantiate this class in a Activity
mFragmentController = FragmentController.getInstance(this);
and now, I do this onBackPressed() from my Activity
#Override
public void onBackPressed() {
if (mFragmentController.getFragmentsList().size() == 1) {
finish();
}
else {
mFragmentController.getFragmentsList().remove(mFragmentController.getFragmentsList().size() - 1);
mFragmentController.presentFragment(mFragmentController.getFragmentsList().get(mFragmentController.getFragmentsList().size() - 1), false);
}
}
In FragmentController class, you will see that im using method getTagFm() on BaseFragment class. This is basically for recognizing which fragment is present in que and which is not. BaseFragment class will be something like this:
public class BaseFragment extends Fragment {
public String getTagFm() {
if (this.getClass().getSimpleName().equals(LoginFragment.class.getSimpleName())) {
return Constants.TAG_FRAGMENT_LOGIN;
} else if (this.getClass().getSimpleName().equals(OrderFragment.class.getSimpleName())) {
return Constants.TAG_FRAGMENT_ORDER;
} else if (this.getClass().getSimpleName().equals(ProgramFragment.class.getSimpleName())) {
return Constants.TAG_FRAGMENT_PROGRAM;
}
return Constants.TAG_FRAGMENT_ORDER;
}
}
where all of these fragments(LoginFragment, OrderFragment, ProgramFragment) extend BaseFragment.
Hope this helps somebody.

In NewsFragment :
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (Integer.parseInt(android.os.Build.VERSION.SDK) > 5
&& keyCode == KeyEvent.KEYCODE_BACK
&& event.getRepeatCount() == 0) {
onBackPressed();
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override
public void onBackPressed() {
HomeFragment f = (HomeFragment) getSupportFragmentManager().findFragmentById(R.id.fragment_home);
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.show(f);
}

You may have an Activity handling all this fragments replacements. In this Activity, if you define a variable with the actual showing fragment, you can override onBackPressed and checks if its showing NewsFragment and replace it with HomeFragment.
#Override
public void onBackPressed() {
if(actualFragment instanceof NewsFragment) {
HomeFragment homeFragment = new HomeFragment();
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.replace(R.id.fragment_container, homeFragment);
transaction.commit();
}
}

This works form me when I press the return button!
I don't know your fragment structure, but I use a single layout and replace it with the respective fragment.
Here's my code:
#Override
//Pressed return button - returns to the results menu
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){
frag_name frag_name = new frag_name();
FragmentManager manager = getActivity().getSupportFragmentManager();
manager.beginTransaction().replace(R.id.yourlayout, frag_name, frag_name.getTag()).commit();
return true;
}
return false;
}
});
}

Instead of checking each fragment on back press, maintain one stack which keeps the track of each fragment in view hierarchy.
Stack<Fragment> fragmentStack= new Stack<Fragment>();
// add first fragment
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction ft = fragmentManager.beginTransaction();
ft.add(R.id.container, "your fragment object");
//push it in stack
fragmentStack.push(homeListFragment);
ft.commit();
// add second fragment
ResultListFragment resultListFragment = new ResultListFragment();
ft.add(R.id.container, resultListFragment);
fragmentStack.lastElement().onPause();
ft.hide(fragmentStack.lastElement());
fragmentStack.push(resultListFragment);
ft.commit();
//now back button press logic
#Override
public void onBackPressed() {
if (fragmentStack.size() == 2) {
FragmentTransaction ft = fragmentManager.beginTransaction();
fragmentStack.lastElement().onPause();
ft.remove(fragmentStack.pop());
fragmentStack.lastElement().onResume();
ft.show(fragmentStack.lastElement());
ft.commit();
} else {
super.onBackPressed();
}
}

Related

How to detect when back button pressed in fragment android?

I have project with navigation drawer with fragment, with 5 menu, the problem is when i go to menu 4 and the i press the back button the app closed, but i need the app back to first menu which is all the menu in fragment.
This is code for Main Activity (Navigation Drawer)
public class MainActivity extends AppCompatActivity{
DrawerLayout mDrawerLayout;
NavigationView mNavigationView;
FragmentManager mFragmentManager;
FragmentTransaction mFragmentTransaction;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
/**
*Setup the DrawerLayout and NavigationView
*/
mDrawerLayout = (DrawerLayout) findViewById(R.id.drawerLayout);
mNavigationView = (NavigationView) findViewById(R.id.shitstuff) ;
/**
* Lets inflate the very first fragment
* Here , we are inflating the TabFragment as the first Fragment
*/
mFragmentManager = getSupportFragmentManager();
mFragmentTransaction = mFragmentManager.beginTransaction();
mFragmentTransaction.replace(R.id.containerView,new Recommendation()).commit();
/**
* Setup click events on the Navigation View Items.
*/
mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(MenuItem menuItem) {
mDrawerLayout.closeDrawers();
if (menuItem.getItemId() == R.id.nav_item_lux_level_recomen) {
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.containerView,new Recommendation()).commit();
}
if (menuItem.getItemId() == R.id.nav_item_room_index_calc) {
FragmentTransaction xfragmentTransaction = mFragmentManager.beginTransaction();
xfragmentTransaction.replace(R.id.containerView,new RoomIndex()).commit();
}
if (menuItem.getItemId() == R.id.nav_item_utilization_factor_calc) {
FragmentTransaction xfragmentTransaction = mFragmentManager.beginTransaction();
xfragmentTransaction.replace(R.id.containerView,new UtilizationFactorCalculator()).commit();
}
if (menuItem.getItemId() == R.id.nav_item_conversions) {
FragmentTransaction xfragmentTransaction = mFragmentManager.beginTransaction();
xfragmentTransaction.replace(R.id.containerView,new Conversion()).commit();
}
if (menuItem.getItemId() == R.id.nav_item_lux) {
FragmentTransaction xfragmentTransaction = mFragmentManager.beginTransaction();
xfragmentTransaction.replace(R.id.containerView,new LuxSensor()).commit();
}
return false;
}
});
/**
* Setup Drawer Toggle of the Toolbar
*/
android.support.v7.widget.Toolbar toolbar = (android.support.v7.widget.Toolbar) findViewById(R.id.toolbar);
ActionBarDrawerToggle mDrawerToggle = new ActionBarDrawerToggle(this,mDrawerLayout, toolbar,R.string.app_name,
R.string.app_name);
mDrawerLayout.setDrawerListener(mDrawerToggle);
mDrawerToggle.syncState();
}
This one of the Menu (Fragment)
public class LuxSensor extends Fragment {
TextView textLIGHT_available, textLIGHT_reading;
FragmentManager mFragmentManager;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View x = inflater.inflate(R.layout.lux_sensor,null);
textLIGHT_reading
= (TextView)x.findViewById(R.id.LIGHT_reading);
SensorManager mySensorManager = (SensorManager)getActivity().getSystemService(Context.SENSOR_SERVICE);
Sensor LightSensor = mySensorManager.getDefaultSensor(Sensor.TYPE_LIGHT);
if(LightSensor != null){
mySensorManager.registerListener(
LightSensorListener,
LightSensor,
SensorManager.SENSOR_DELAY_NORMAL);
}else{
textLIGHT_available.setText("Sensor.TYPE_LIGHT NOT Available");
}
FragmentManager fm = getFragmentManager();
return x;
}
private final SensorEventListener LightSensorListener
= new SensorEventListener(){
#Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
// TODO Auto-generated method stub
}
#Override
public void onSensorChanged(SensorEvent event) {
if(event.sensor.getType() == Sensor.TYPE_LIGHT){
textLIGHT_reading.setText(event.values[0]+" LUX");
}
}
};
}
I have tried solution in internet but it still closed
In fragment you should use something like this:
#Override
//Pressed return button - returns to the results menu
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){
//your code
return true;
}
return false;
}
});
}
onCreateView of fragment add this
Java
requireActivity().getOnBackPressedDispatcher().addCallback(activity, new OnBackPressedCallback(true) {
#Override
public void handleOnBackPressed() {
// in here you can do logic when backPress is clicked
}
});
Kotlin
requireActivity().onBackPressedDispatcher.addCallback(object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
// in here you can do logic when backPress is clicked
}
})
In main activity you need to override the following function
#Override
public void onBackPressed()
{
// code here to check what fragment you are on and handle that accordingly
super.onBackPressed(); // this exits the app.
}
Documentation is available here: https://developer.android.com/reference/android/app/Activity.html#onBackPressed()
In your MainActivity
#Override
public void onBackPressed()
{
FragmentTransaction fragmentTransaction = mFragmentManager.beginTransaction();
fragmentTransaction.replace(R.id.containerView,new Recommendation()).commit();
}
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
You can key press listener in android using setOnKeyListener(). And then, if you want to handle the back press, override the onKey method :
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK) {
// do your thing
}
}
Now the onBackPressed method is deprecated and we need to use this way (much simpler !):
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
activity?.onBackPressedDispatcher?.addCallback(this, object : OnBackPressedCallback(true) {
override fun handleOnBackPressed() {
if(shouldInterceptBackPress()){
// in here you can do logic when backPress is clicked
}else{
isEnabled = false
activity?.onBackPressed()
}
}
})
}
You can found full explanation here
With this example it shows how to do when you are not sure that you need to consume the back press event

Android Move to previous fragment without reload

I have a HomeFragment and LoginFragment in the same Activity. At first it shows the HomeFragment and then go to LoginFragment. But when I back to HomeFragment it reloads the HomeFragment.
How to make the HomeFragment not reload when I press back from LoginFragment.
Thanks in advance. Here is my code :
MainActivity.java
private void setEvent() {
img_action_back.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
closeKeyboard(getApplication(), img_action_back.getWindowToken());
onBackPressed();
}
});
}
#Override
public void onBackPressed() {
if (fragment instanceof LoginFragment)
{
LoginFragment loginFragment = (LoginFragment)fragment;
if(loginFragment.fromDrawer)
Navigator.showHomeFragment(mContext);
else
finish();
}
}
}
Navigator.java
public static void showHomeFragment(Context context) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID, new HomeFragment());
transaction.commit();
}
public static void showLoginFragment(Context context,Boolean fromDrawer,String infoRegister) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID,
LoginFragment.newInstance(fromDrawer,infoRegister),"login");
transaction.commit();
}
Your issue is that you're recreating a new HomeFragment instance when you press back. You should instead pop the FragmentManager backstack.
#Override
public void onBackPressed() {
if (fragment instanceof LoginFragment) {
LoginFragment loginFragment = (LoginFragment)fragment;
if(loginFragment.fromDrawer) {
getFragmentManager(mContext).popBackStack();
} else {
finish();
}
}
}
You also need to make sure you add the LoginFragment to the backstack.
public static void showLoginFragment(Context context,Boolean fromDrawer,String infoRegister)
{
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID,
LoginFragment.newInstance(fromDrawer,infoRegister),"login");
transaction.addToBackStack(null);
transaction.commit();
}
#SunnySydeUp has a nice answer. I've found some improvement scope in your code.
You need to add the Fragment in the back stack so that the super.onBackPressed call won't recreate the Fragment again like the other answer says.
But as you're really worried about recreating Fragment you might initialize them once and use the instance again during transaction.
So in the onCreate function of your Activity initialize both of them first like this.
private HomeFragment mHomeFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
mHomeFragment = new HomeFragment();
}
Now your onBackPressed may look like
#Override
public void onBackPressed() {
if (fragment instanceof LoginFragment) {
if(loginFragment.fromDrawer) {
super.onBackPressed();
} else {
finish();
}
}
}
And the Navigator.java
public static void showHomeFragment(Context context) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID, mHomeFragment);
transaction.commit();
}
public static void showLoginFragment(Context context,Boolean fromDrawer,String infoRegister) {
final FragmentTransaction transaction =
getFragmentManager(context).beginTransaction();
transaction.replace(CONTAINER_ID,
LoginFragment.newInstance(fromDrawer,infoRegister),"login");
transaction.addToBackStack(null).commit();
}

MainActivity to fragment and back to MainActivity on pressing back button

I have a Mainactivity which contains a Layout which is parent of 4 sub layout. on clicking on sub layout i am going to a new fragment replacing main layout. But i cant go back to MainActivity after pressing Back button
MainActivity.java
public class MainActivity extends AppCompatActivity {
RelativeLayout aboutUs;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
aboutUs = (RelativeLayout) findViewById(R.id.aboutUs);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
fab.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
.setAction("Action", null).show();
}
});
}
//click methods goes here
public void clickAboutUs(View view){
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
FragmentAboutUs fragmentAboutUs = new FragmentAboutUs();
fragmentTransaction.replace(R.id.fragment_container,fragmentAboutUs);
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
FragmentAboutUs.java
public class FragmentAboutUs extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.about_us, container,false);
return view;
}
}
How to go back to main page again after pressing back button from fragment.
Try Like this
public boolean popFragment() {
boolean isPop = false;
Fragment currentFragment = getSupportFragmentManager()
.findFragmentById(R.id.flContent);
if (getSupportFragmentManager().getBackStackEntryCount() > 0) {
isPop = true;
getSupportFragmentManager().popBackStackImmediate();
}
return isPop;
}
#Override
public void onBackPressed() {
if (!popFragment()) {
finish();
}
}
public void replaceFragment(Fragment fragment, boolean addToBackStack) {
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
if (addToBackStack) {
transaction.addToBackStack(null);
} else {
getSupportFragmentManager().popBackStack(null,
FragmentManager.POP_BACK_STACK_INCLUSIVE);
}
transaction.replace(R.id.fragment_container, fragment);
transaction.commit();
getSupportFragmentManager().executePendingTransactions();
}
add above method is in your parent Activity
And Use like
FragmentAboutUs fragmentAboutUs = new FragmentAboutUs();
replaceFragment(fragmentAboutUs , true);
If you have one Activity and four fragments then set onBackPressed() as below in your MainActivity.
#Override
public void onBackPressed()
{
super.onBackPressed();
finish();
}
And in fragments:
#Override
public void onResume() {
super.onResume();
new PlayListFragment();
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(getFragmentManager().getBackStackEntryCount() > 0) {
getFragmentManager().popBackStack();
}
return true;
}
return false;
}
});
}
You can Override the onBackPressed of MainActivity
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 1) {
getFragmentManager().popBackStack();
} else {
super.onBackPressed();
}
}
Here is how it can be done in Xamarin:
Load fragment (I wrote helper method for that, but it's not necessary):
public void LoadFragment(Activity activity, Fragment fragment)
{
var backStateName = fragment.GetType().Name;
var fm = activity.FragmentManager;
var ft = fm.BeginTransaction();
ft.Replace(Resource.Id.mainContainer, fragment);
ft.AddToBackStack(backStateName);
ft.Commit();
}
Back button (in MainActivity):
public override void OnBackPressed()
{
if (isNavDrawerOpen()) drawerLayout.CloseDrawers();
else
{
var backStackEntryCount = FragmentManager.BackStackEntryCount;
if (backStackEntryCount == 1) Finish();
else if (backStackEntryCount > 1) FragmentManager.PopBackStack();
else base.OnBackPressed();
}
}
isNavDrawerOpen method:
bool isNavDrawerOpen()
{
return drawerLayout != null && drawerLayout.IsDrawerOpen(Android.Support.V4.View.GravityCompat.Start);
}

proper backstack navigation on pressing Up button

I implemented backstack navigation on Back button in MainActivity (which have many fragments) using onBackPressed(). I want to implement same backstack navigation on Up button press. Problem is I only want Up button in one fragment but the Up button appears on every fragment when I press Up button on desired fragment. Even setDisplayHomeAsEnabled(false) is not working. How to have Up button in one fragment only?
Desired Fragment:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
((AppCompatActivity)getActivity()).getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Main Activity
public class HomePage extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.homepage);
if (findViewById(R.id.fragment_place) != null) {
if (savedInstanceState != null) {
return;
}
HomeFrag firstFragment = new HomeFrag();
getSupportFragmentManager().beginTransaction().add(R.id.fragment_place, firstFragment).commit();
}
}
/* #Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
break;
default:
break;
}
return super.onOptionsItemSelected(item);
} */
public void selectFrag(View view) {
Fragment fr = null;
if (view == findViewById(R.id.home_btn)) {
fr = new HomeFrag();
} else if (view == findViewById(R.id.search_btn)) {
fr = new SearchFrag();
} else if (view == findViewById(R.id.log_btn)) {
fr = new LogFrag();
} else if (view == findViewById(R.id.events_btn)) {
fr = new EventFrag();
} else if (view == findViewById(R.id.profile_btn)) {
fr = new ProfileFrag();
}
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fm.beginTransaction();
fragmentTransaction.replace(R.id.fragment_place, fr).addToBackStack(null);
fragmentTransaction.commit();
}
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() > 0)
getFragmentManager().popBackStack();
else
super.onBackPressed();
}
}
I created a sample application on GitHub to solve such backstack problem.
Download Fragment Back Stack application

Android Fragment handle back button press [duplicate]

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;
}
});

Categories

Resources