How to maintain backstack of fragment in Navigation Drawer? - java

I have taken one activity and with activity I have attached navigation drawer and the home fragment. In that navigation drawer there is a option of "Contact Us". When user click on that option a fragment gets open. But I am not able to maintain the stack of that. means when I am on contact us fragment then using navigation drawer again click on contact us it overlaps previous one. I have to press back button 2 times to go on home fragment. Please Help me how to maintain back stack for this. Here is my code..
navigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.rate:
Uri uri = Uri.parse("market://details?id=" + getPackageName());
Intent myAppLinkToMarket = new Intent(Intent.ACTION_VIEW, uri);
try {
startActivity(myAppLinkToMarket);
} catch (ActivityNotFoundException e) {
Toast.makeText(getApplicationContext(), "Unable to find source market app!", Toast.LENGTH_SHORT).show();
}
break;
case R.id.contact_us:
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
transaction.add(R.id.fragment_container_dashboard, ContactUsFragment.newInstance());
transaction.addToBackStack(null);
transaction.commit();
mDrawerLayout.closeDrawer(GravityCompat.START);
break;
}
return false;
}
});
After this code I use this to maintain back stack:
#Override
public void onBackPressed() {
if (getFragmentManager().getBackStackEntryCount() != 0) {
getFragmentManager().popBackStack();
}
else new AlertDialog.Builder(this)
.setMessage("Are you sure you want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Dashboard.super.onBackPressed();
}
})
.setNegativeButton("No", null)
.show();
}
But the issue is It's show exit dialogue on contact us fragment when ever I press back button. But I want firstly I reach to home fragment and after that If I press back button then it show exit dialogue.

I solved this by adding this in the onCreate method.
getSupportFragmentManager().addOnBackStackChangedListener(new FragmentManager.OnBackStackChangedListener() {
#Override
public void onBackStackChanged() {
if(getSupportFragmentManager().getBackStackEntryCount() == 0){
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED);
menuToggle.setDrawerIndicatorEnabled(true);
// your dialog
}else{
drawer.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED);
menuToggle.setDrawerIndicatorEnabled(false);
// remove to your previous fragment
}
}
});

Related

When I press the back button of my phone the app closes

I have made an app with many activities and I have included a bottom navigation bar to all of them. I used finish(); after any intent not to get any memory leak problem but when I press the back button of my phone the app closes. What should I do?
//an intent of my app
Button btnsuita = (Button) findViewById(R.id.souita_btn);
btnsuita.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(HotelRooms.this, HotelRoomsSouita.class);
startActivity(intent);
finish();
}
});
//An Example of Bottom Navigation Bar
BottomNavigationView bottomNavigationView = (BottomNavigationView) findViewById(R.id.BottomNavView_bar);
BottomNavigationViewHelper.disableShiftMode(bottomNavigationView);
Menu menu = bottomNavigationView.getMenu();
android.view.MenuItem menuItem = menu.getItem(0);
menuItem.setChecked(true);
bottomNavigationView.setOnNavigationItemSelectedListener(new BottomNavigationView.OnNavigationItemSelectedListener() {
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item) {
switch (item.getItemId()) {
case R.id.ic_home:
Intent intent1 = new Intent(HotelRoomsSouita.this, MainMenu.class);
startActivity(intent1);
finish();
break;
case R.id.ic_back:
Intent intent2 = new Intent(HotelRoomsSouita.this, HotelRooms.class);
startActivity(intent2);
finish();
break;
case R.id.ic_contact_us:
Intent intent3 = new Intent(HotelRoomsSouita.this, Mail.class);
startActivity(intent3);
finish();
break;
}
return false;
}
});
Remove finish() in the switch every case.
If you want to ignore back button clicks, then you can add this code in the activity.
#Override
public void onBackPressed() {
//Do anything if you wish
}
Since you are finishing every activity ,there is o activity remain in the backstack or background . so the application closes.
You can use view pager to inflate fragment for bottom navigation view . This is the correct approach by google material design guid lines.
Never use activity for bottom navigation view .
And still if you continue with your activity , then override the onBackpress method of the activtiy .
If I understand your problem correctly you want the user to be able to go back in previous activities while your application does not cause any memory leaks(by starting same activity again and again)
Solution:
Remove finish() from each case in onNavigationItemSelected
Go to your manifest file and add android:launchMode="singleInstance" to each Activity
Ovveride onBackPressed so that it does nothing in your first-started activity as dcanh121 said.
If you do that then if user tries to open the same Activity which was already opened before, it will cause the app to open the previous activity instead of creating a new one.. (read more about this)
Therefore you won't have any memory leak problems :)

How to create navigation view open on condition?

First of all, I have two navigation view for both sides in android as shown in this Image. Left side for user who signup as parents meanwhile the right side for the user who signup as a tuition provider. For example, if User A signs up as a parent, so he/she can only open the left side navigation ONLY. I have made some studies, mostly using radio button/radio group. Unfortunately, I have to use the spinner, what coding should I write to ensure the navigation view can be opened for certain users. For your information, I have 3 activity.
1) User.Java wherein coding, I have coding initialize, constructor and getter.
2) RegistrationActivity.Java where I put all the spinner coding.
//USER TYPE SPINNER
List<String> categories = new ArrayList<>();
categories.add(0, "Choose Category");
categories.add("Parents");
categories.add("Tuition Provider");
ArrayAdapter<String> dataAdapter;
dataAdapter = new ArrayAdapter(this,android.R.layout.simple_spinner_item, categories);
dataAdapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
RegisterUserType.setAdapter(dataAdapter);
RegisterUserType.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener()
{
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id)
{
if(position==0)
{
//Toast.makeText(getApplicationContext(),"No Item Selected",Toast.LENGTH_LONG).show();
}
else
{
Toast.makeText(getApplicationContext(),parent.getItemAtPosition(position) +" Selected",Toast.LENGTH_SHORT).show();
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
});
3) Home.Java where I put all my navigation view coding.
menuLeft = (ImageButton) findViewById(R.id.menu_left);
menuRight = (ImageButton) findViewById(R.id.menu_right);
parentsNavigation = findViewById(R.id.nav_view);
tuitionProviderNavigation = findViewById(R.id.nav_view2);
menuLeft.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if (drawerLayout.isDrawerOpen(GravityCompat.START))
{
drawerLayout.closeDrawer(GravityCompat.START);
}
else
{
drawerLayout.openDrawer(GravityCompat.START);
}
}
});
menuRight.setOnClickListener(new View.OnClickListener()
{
#Override
public void onClick(View v)
{
if (drawerLayout.isDrawerOpen(GravityCompat.END))
{
drawerLayout.closeDrawer(GravityCompat.END);
}
else
{
drawerLayout.openDrawer(GravityCompat.END);
}
}
});
parentsNavigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener()
{
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item)
{
UserMenuSelector(item);
return false;
}
});
tuitionProviderNavigation.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener()
{
#Override
public boolean onNavigationItemSelected(#NonNull MenuItem item)
{
UserMenuSelector(item);
return false;
}
});
I can not paste all my coding here because it is too long but I can email you to better understand my XML layout.
All you have to do is lock the drawer that you want to Disable and unlock again if you want to Enable it
use drawerLayout.setDrawerLockMode();
in your case below code will close your one drawer that have gravity start
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.START);
and when you want to unlock again then simply
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
Remember one more thing
drawerLayout.setDrawerLockMode() allows the application to restrict the user's ability to open or close the given drawer (cannot open via sliding) but DrawerLayout will still respond to calls to openDrawer(), closeDrawer()
so in your case you also consider disabling the ImageView click because they have these methods
In Your Case
when user will click on a spinner item you check if(position==1)
if this condition true you know it's a parent
so do this in your Home.Java
do this
if(parent==true){
//this will disable right drawer
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, GravityCompat.END);
//and this will enable left drawer
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED, GravityCompat.START);
//and also do to disable right ImageView
menuRight.setEnabled(false);
//and also do to enable left ImageView
menuLeft.setEnabled(true);}
now for tuition provider do same but this time enable right and disable left,
also same for menu button.

How to apply Back button pressed in fragment [duplicate]

This question already has answers here:
Android Fragment handle back button press [duplicate]
(25 answers)
Closed 7 years ago.
I am new on android. I cant handle onBackPressed method. I have an Activity class which has four fragment like A,B,C, D. When i lunched Activity by default Fragment A is active and there are link on fragment A to move another Fragment. I want when move another Fragment like B,C,D from Fragment A and pressed Back button it return to Fragment A and if i pressed Back button from Fragment A it show a dialog box.
I used onBackPressed() like below
public void onBackPressed() {
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(
MainActivity.this);
// set title
alertDialogBuilder.setTitle("Exit");
// set dialog message
AlertDialog.Builder builder = alertDialogBuilder
.setMessage("Do you really want to exit?")
.setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
MainActivity.this.finish();
}
})
.setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
But it work on all fragment and i want to work only Fragment A
You have to handle such requirements in the Activity's onBackPressed only. I usually follow the following approach:
I keep an enum having all the fragments defined and a parameter to track the current fragment:
enum FRAGMENTS{
fragmentA, fragmentB, fragmentC, fragmentD
}
FRAGMENTS mCurrentFragment;
Now the logic where you change the fragment should have:
public void changeFragment(FRAGMENTS newFragment){
//Your logic
mCurrentFragment = newFragment;
}
And finally the logic onBackPressed:
onBackPressed{
if(mCurrentFragment == FRAGMENTS.fragmentA){
//Your code here of asking the user if he/she really wants to quit
super.onBackPressed();
}else{
changeFragment(getPreviousFragment(mCurrentFragment));
}
}
If you dont want to use enum, you can have final int or any other string values to represent different fragments and a mCurrentFragment parameter to keep a track of the currentFragment being shown and then you can easily play with the code in your Activity's onBackPressed method
Let me know if you need more clarification.
Add fragment to backStack while replacing
Fragment newFragment = new ExampleFragment();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack if needed
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
And then override onBackPressed method inside an Activity
So Here is your answer which you are looking for :)
Method 1
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// Your stuff here
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
// set title
alertDialogBuilder.setTitle("Exit");
// set dialog message
AlertDialog.Builder builder = alertDialogBuilder.setMessage("Do you really want to exit?")
.setCancelable(false).setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
System.exit(0);
}
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
}
//Tell me if you face any issue
Method 2 or in your case just paste this method in your main activity so you can also achieve it what you want :)
#Override
public void onBackPressed() {
// TODO Auto-generated method stub
AlertDialog.Builder alertDialogBuilder = new AlertDialog.Builder(MainActivity.this);
// set title
alertDialogBuilder.setTitle("Exit");
// set dialog message
AlertDialog.Builder builder = alertDialogBuilder.setMessage("Do you really want to exit?").setCancelable(false)
.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, close
// current activity
System.exit(0);
}
}).setNegativeButton("No", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
// if this button is clicked, just close
// the dialog box and do nothing
dialog.cancel();
}
});
// create alert dialog
AlertDialog alertDialog = alertDialogBuilder.create();
// show it
alertDialog.show();
}
If you want to override the onBack pressed method then add this code in your in onActivityCreated() method
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) {
getFragmentManager().popBackStack();
return true;
}
return false;
}
});
Use the below code hope it helps.
//Always replace/add your fragment with a tag so that you can use that tag in future
Fragment fr = getSupportFragmentManager().findFragmentByTag("Fragment_Name");//Fragment Name is a tag to identify fragment
if (fr == null) {
getSupportFragmentManager().beginTransaction()
.replace(R.id.frame_container, new Demo_Fragment(), "Fragment_Name").commit();
}
//Now override onBackPressed method in MainActivity Only no need to do it in Fragment Class
#Override
public void onBackPressed() {
Fragment fr = getSupportFragmentManager().findFragmentByTag("FragmentA");
if(fr==null)
//replace FragmentA
else
//Show Alert Box
}
For that you need to do following
Create method on the BaseFragment or Activity that save the current fragment object
public void setCurrentFragment(Fragment currentFragment) {
this.currentFragment = currentFragment;
}
and also create a method that return your current fragment object
public Fragment getCurrentFragment() {
return currentFragment;
}
now just you need to call the getCurrentFragment Method and checked that if the Fragment is an instance of the A fragment or not
if (getCurrentFragment() instanceof AFragment) {
showAlert
} else {
super.onBackPressed()
}

onBackPressed with Fragments Android

I am trying to implement onBackPressed on an activity with 3 Fragments. My issue is I implemented a "press again to exit" after some time method however when back button is clicked twice it returns to the previous fragment, not quitting. This happens when that fragment is called (i.e if I open the game and exit straight away it works like a charm). My onBackPressed method on MainActivity looks like this;
#Override
public void onBackPressed() {
if(getSupportFragmentManager().getBackStackEntryCount()!=0) {
getSupportFragmentManager().popBackStack();
}
this.findViewById(R.id.linearLayout1).setVisibility(View.VISIBLE);
if(mBackPressed + TIME_INTERVAL > System.currentTimeMillis() && getSupportFragmentManager().getBackStackEntryCount()==0){
super.onBackPressed();
System.exit(0);
this.finish();
}
else { Toast.makeText(getBaseContext(), "Press back once more to exit", Toast.LENGTH_SHORT).show(); }
mBackPressed = System.currentTimeMillis();
}
I basically want to check if the user is in the mainActivity layout so I could evade using popBackStack. Any solution would be appreciated, I could provide more information if this is not enough.
Thanks,
Bektaş
If you really want to quit, ignore the back stack altogether. Use this code.
#Override
public void onBackPressed() {
this.findViewById(R.id.linearLayout1).setVisibility(View.VISIBLE);
if(mBackPressed + TIME_INTERVAL > System.currentTimeMillis()){
this.finish();
return;
}
else {
Toast.makeText(getBaseContext(), "Press back once more to exit", Toast.LENGTH_SHORT).show();
}
mBackPressed = System.currentTimeMillis();
}
I think you might find this link useful
http://developer.android.com/training/implementing-navigation/temporal.html
it says that you can add the reference to the stack using this paturn
// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail")
// Add this transaction to the back stack
.addToBackStack()
.commit();
#Override
public void onBackPressed() {
int count = getSupportFragmentManager().getBackStackEntryCount();
if (count == 0) {
getSupportFragmentManager().popBackStack(getSupportFragmentManager().getBackStackEntryCount(), getSupportFragmentManager().POP_BACK_STACK_INCLUSIVE);
android.app.AlertDialog.Builder builder = new android.app.AlertDialog.Builder(context);
builder.setCancelable(false);
builder.setMessage("Are you sure you want to exit?")
.setNegativeButton(android.R.string.no, null)
.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface arg0, int arg1) {
(Main Activity Name).super.onBackPressed();
}
}).create().show();
} else {
super.onBackPressed();
}
}

Having two activities with Mainctivity causes the back button to not work properly

I have a MainActivity and I am posting partial code here:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.action_quit:
moveTaskToBack(true);
return true;
case R.id.action_help:
//display the help activity
Intent myIntent = new Intent( this, ShowHelp.class);
startActivityForResult(myIntent, 0);
return true;
case R.id.action_about:
//display the about window
//aboutApp();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
public void aboutApp() {
// custom dialog
final Dialog dialog = new Dialog(this);
dialog.setContentView(R.layout.about);
dialog.setTitle("About TollCulator");
TextView textViewAbout = (TextView) dialog.findViewById(R.id.tvContents);
textViewAbout.setText("About: ");
textViewAbout.append("\n\tTollCulator - Toll Calculator");
textViewAbout.setMovementMethod(new ScrollingMovementMethod());
Button btnCloseIt = (Button) dialog.findViewById(R.id.btnOk);
// if button is clicked, close the custom dialog
btnCloseIt.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
dialog.dismiss();
}
});
ImageView iv1 = (ImageView) dialog.findViewById(R.id.imgFB);
iv1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
Uri uri = Uri.parse("https://www.facebook.com/PagesByZ"););
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
dialog.cancel();
startActivity(intent);
}
});
dialog.show();
}
ShowHelp.class partial code here which is an Activity on it's own:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case 16908332:
Intent myIntent = new Intent(getApplicationContext(), MainActivity.class);
startActivityForResult(myIntent, 0);
//finish();
}
return super.onOptionsItemSelected(item);
}
#Override
public void onBackPressed() {
// do something on back.
Intent myIntent = new Intent(getApplicationContext(), MainActivity.class);
startActivityForResult(myIntent, 0);
//finish();
return;
}
Now when i choose action_help from the menu it opens the ShowClass Activity and I can press the back button and it brings me back to MainActivity without any problem.
Now If I choose action_about the About Dialog pops up and If I click on the icon within the Dialog the following pops up:
At this time If I decide to press BACK, instead of closing the pop and displaying my About Dialog, I am just taken to the Homescreen.
Commenting the finish(); method in ShowClass allows me to click the BACK button from the "Complete action using" But instead of taking me to the MainActivity, it takes me to ShowClass from there I can press the Back button again to come back to MainActivity. So as long as I choose action_help first, pressing back brings me back to the ShowHelp.
When the Complete action using pops up from the About Dialog, I set up a Log, and it's on the onPause() state and if I did not run ShowHelp in the beginning, my App goes directly to onStop() and onDestroy().
Please help me fix the issue.
To test it, I created a demo without the ShowHelp class with just the dialog and By pressing back button, the Complete actiong using dialog closes and then pressing back again closes the About Dialog. So I am not sure what is going on.
Declare a global boolean variable chooserIsShowing:
boolean chooserIsShowing;
Set it to true here:
ImageView iv1 = (ImageView) dialog.findViewById(R.id.imgFB);
iv1.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
// set the flag
chooserIsShowing = true;
Uri uri = Uri.parse("https://www.facebook.com/PagesByZ"););
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
dialog.cancel();
startActivity(intent);
}
});
Change the overriden onBackPressed() method:
#Override
public void onBackPressed() {
if (!chooserIsShowing) {
// do something on back.
Intent myIntent = new Intent(getApplicationContext(), MainActivity.class);
startActivityForResult(myIntent, 0);
finish();
}
// remove the flag
chooserIsShowing = false;
return;
}
Edit 1:
Declare dialog as a global variable:
Dialog dialog;
Change aboutApp():
public void aboutApp() {
dialog = new Dialog(this);
....
}
Override onKeyDown(int, KeyEvent) method in MainActivity:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (dialog != null && dialog.isShowing()) {
dialog.dismiss();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
You can remove chooserIsShowing as its not required.
Final Solution:
A noHistory flag was defined for this activity in the manifest. Removing that and fixing onBackPressed() method of ShowHelp resolved the issue.

Categories

Resources