I have a main activity that is situated with two navigation drawers. The left one is always accessible, but the right one is used for displaying some necessary lists and performing certain actions. When I have certain fragments loaded into the center FrameLayout of the activity they have actions that require the right drawer be opened and an item be selected. All of this is working great, but I want to make sure that when either the back button is pressed or the screen is clicked outside the drawer that the drawer is closed, locked and the fragment loaded in there is removed. I should also mention that this right drawer holds a FrameLayout and relies on fragments.
In my main activity this is what I'm calling:
#Override
public void onBackPressed(){
Log.d(TAG, "onBackPressed() called");
if(drawerLayoutRight.isDrawerOpen(drawerFrameRight)){
Log.d(TAG, "----------------------");
drawerLayoutRight.closeDrawer(drawerFrameRight);
drawerLayoutRight.setDrawerLockMode(1, drawerFrameRight);
ExcerciseList fragment = (ExcerciseList) getFragmentManager().findFragmentById(R.id.right_drawer);
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.remove(fragment).commit();
} else {
super.onBackPressed();
}
}
I also tried:
public boolean onKeyDown(int keyCode, KeyEvent event){
if (keyCode == KeyEvent.KEYCODE_BACK) {
Log.d(TAG, "onKeyDown called");
return true;
}
return super.onKeyDown(keyCode, event);
}
But this hasn't worked either. Whenever I click the back button the drawer will close, but neither of these methods would be called. But once the drawer is closed, if the back button is pressed these methods will be invoked. I was also wondering if anyone knew of a simple way to handle a click outside of the drawer. I figure I could make a custom DrawerLayout and override drawerClose() or check the coordinates that are clicked on a touch event. Just curious if anyone has a simpler way.
Put this line into your code:
mDrawerLayout.setFocusableInTouchMode(false);
Then it will call your overwritten onBackPressed() when you press the back button.
Quoting myself from the question at Force EditText to lose focus when back pressed
"In my experience onBackPressed() (at least the default #Override one in an activity) will not normally fire when pushing the back button to close the keyboard. As far as I know it will only fire when a Back press would initiate a finish() on the current activity."
This probably applies the same way to the navigation drawer.
This is most likely due to the fact that the current "Activity" is not in focus when the drawer opens (same with the SoftKeyboard) so the #Override back button is not called.
I was able to solve my problem by using DrawerLayout.DrawerListener. This worked in my situation because when the back button was being pressed the drawer was still closing even though the method onBackPressed() wasn't being called.
#MH said, "The DrawerLayout (I assume that's what you're using) will consume the back button event if the drawer is opened."
Although in the documentation I could not find an explicit mention of this; there was some mention of the back button here. Unfortunately it was not of much help.
What #MH said explains why onBackPressed() was not being called when the drawer was opened, and why it was being called while it was closed.
drawerLayoutRight.setDrawerListener(this);
Where drawerLayoutRight is a DrawerLayout. And my listener looks like this:
#Override
public void onDrawerClosed(View arg0) {
if(drawerLayoutRight.getDrawerLockMode(drawerFrameRight)!= 1){
drawerLayoutRight.setDrawerLockMode(1, drawerFrameRight);
}
ExcerciseList fragment = (ExcerciseList) getFragmentManager().findFragmentById(R.id.right_drawer);
if (fragment != null) {
FragmentTransaction transaction = getFragmentManager().beginTransaction();
transaction.remove(fragment).commit();
}
}
onDrawerOpened(), onDrawerSlide() and onDrawerStateChanged() are all empty. Only one of my drawers is using the listener so I don't have to check the view.
Just simple overrede your activity or drawerlayout, will do.
#Override public boolean onKeyDown(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode
&& mDrawerLayout != null
&& mDrawerLayout.isDrawerOpen(leftDrawerView)) {
KeyEventCompat.startTracking(event);
return true;
}
return super.onKeyDown(keyCode, event);
}
#Override public boolean onKeyUp(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode
&& !event.isCanceled()
&& mDrawerToggle != null
&& mDrawerLayout.isDrawerOpen(leftDrawerView)) {
mDrawerLayout.closeDrawers();
return true;
}
return super.onKeyUp(keyCode, event);
}
The problem, as #Alex Vasilkov indicated, seems to do something with the drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); method.
None of the suggestions above did work for me. So I wanted to write my own solution. I digged the source code of DrawerLayout Class and tried overriding onKeyDown and onKeyUp methods, but the Back Button click is not getting triggered at all, if the Lock mode is LOCK_MODE_LOCKED_CLOSED. I think this is a bug.
So in my case, I needed to lock left navigation drawer only, not the right one. And I ended up calling drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.START); this method to lock my left navigation drawer instead of calling drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); this method, which locks both of the drawers and causes this back button bug.
So as a summary, drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); disables back button click somehow. Maybe only if you have two navigation drawers.
After adding this line back button will manage to close DrawerLayout.
mDrawerLayout.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
I had a very similar issue with an empty list in a fragment (wouldn't respond to back button press when the list was empty) and some of the solutions mentioned here helped me solving my issue.
The fragment causing the issue with the "onBackPressed()":
<TextView
android:id="#android:id/empty"
... />
<ListView
android:id="#android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The issue is similar in that when the list returned by the adapter is empty (#android:id/empty), the first view (TextView) doesn't seem to be "considered" as a focusable/focused view by Android (whereas the second view - ListView - is).
So pressing the back button wouldn't be registered by the view currently displayed and wouldn't be caught by my custom code in the fragment (instead closing the activity directly).
In my case, adding the following to onCreateView solved my issue (and allowed the back button press to be caught by the fragment even when the list is empty):
View view = inflater.inflate(R.layout.fragment_content, container, false);
view.setFocusableInTouchMode(true);
view.requestFocus();
I know it is a bit late but still... this answer will help others
#Override
public void onBackPressed()
{
if (mDrawerLayout.isOpen())
mDrawerLayout.close();
else
super.onBackPressed();
}
Related
I am working on a app where I have only one Login activity and one main activity. I replace fragments down to three level ie like A->B->C or A->B->D and more scenarios like this.I keep them on back stack and on back press retrieve them.Like on backpress of C , B appears. I am having a scenario where I receive push notification then on click of it the app should redirect to C. Now to achieve it I have to go through the launch screen. It means that before clicking on notification if I am on D and I get notification for C then I will land on C , but on backpress I will not come to D but to B because A->B>C . So is there a structure for one activity and all fragments where even if I click on notification,I can get back the flow as it is before the notification arrived ..??? ie directly maintain C to D and then regular flow as it is.
Thank you.
It may depend slightly on what the contents of those fragments are but I think actually the Android docs would suggest the behaviour you have at the moment is actually the correct one. Essentially once you've navigated into the app from a notification you the navigation should be the same as if you'd entered that point in the app normally by navigating through the screens. It should have the previous navigation history cleared.
The docs elaborate on this:
https://developer.android.com/design/patterns/navigation.html#into-your-app
Firstly implements TabLayout.OnTabSelectedListener to your fragment hosted activity,override methods then on onTabSelected get tab position(Let's say tabPosition),from this position try following code in same activity(with required changes):
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
// TODO Auto-generated method stub
if ((keyCode == KeyEvent.KEYCODE_BACK))
{
if(tabPosition!=0)
{
pager.setCurrentItem(tabPosition- 1);
return true;
}
else
{
onBackPressed();
}
}
return super.onKeyDown(keyCode, event);
}
You can implement a custom backstack of you own.
So if you have a history that looks like this:
A->B->C->D
If an event then causes the history to add C once more, e.g. the notification you specify above, then what you specify above suggests you want the history to look like this afterward:
A->B->C->D->C
And what you get currently, which you don't want, is this:
A->B->C
To do this you can implement custom fragment history handling in your Activity. Where you would normally add your fragment transition to the backstack, instead add the fragment to an ArrayList or similar entity. I have thrown together some code below:
private ArrayList<Fragment> mFragmentHistory = new ArrayList<>();
private void loadFragment(Fragment fragment, boolean addToHistory) {
// your fragment transition code here without adding to backstack
// add the fragment to your custom history
if (addToHistory) {
mFragmentHistory.add(fragment);
}
}
You will then need to implement handling code in the onBackPressed method of the same Activity:
#Override
public void onBackPressed() {
// if the history is not at the "top", i.e. only one (or 0) fragments in history
if (mFragmentHistory.size() > 1) {
// remove the last (==current) fragment from history
mFragmentHistory.remove(mFragmentHistory.size() - 1);
// get new last fragment from history - this is now the one to load
Fragment loadFragment = mFragmentHistory.get(mFragmentHistory.size() - 1);
// load this fragment, don't add it to the history
loadFragment(loadFragment, false);
} else {
// perform default system back or implement your custom handler here
super.onBackPressed();
}
}
You will have to consider maintaining state over Activity restarts (screen rotation etc.).
For example, I have 2 activities which are named Main and Photos. I start Photos from Main. When I press back button when Photos activity opened, my navigation drawer menu is still selected. However, if I press back menu on action bar, Main activity refreshes, so selection disappears. I want to apply action bar back's event on back button. How can I do?
Its better to make your action bar button implement androids back buttons action rather than vice-versa, however, it can be done.
// Newer, but Im not sure what API version it came in
#Override
public void onBackPressed() {
//super.onBackPressed();
mMyAbBackBtn.callOnClick();
}
// Older, still supported
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (KeyEvent.KEYCODE_BACK == keyCode) {
mMyAbBackBtn.callOnClick();
return true; //handled
}
return super.onKeyUp(keyCode, event);
}
The "back button on action bar" is actually called an Up button. Just to clarify the differences: the Back button returns you to the previous activity (this could bring you to a different app) while the Up button will return you to the previous activity in the current application.
I'm quite confused how you have an Up button and a navigation drawer at the same time, would you put up some screenshots?
Assuming your "navigation drawer menu" isn't a native part of android that you're talking about, I don't recommend overriding your Back button to get your desired behavior.
Instead consider overriding onActivityCreated or onResume:
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(); // don't restore the state
// alternatively:
// super.onActivityCreated(savedInstanceState); // you may have other things you
// want to preserve
// unSelect(); // your code to unselect whatever your navigation menu drawer is
}
#Override
public void onResume()
{
super.onResume();
unSelect(); // your code to unselect whatever your navigation menu drawer is
}
I'm using the Sherlock Side Navigation Drawer implementation and i'd like to open the side menu by clicking on the back button, so i did:
In the main activity, i overrided the onKeyDown event, and i prepared actions when keycode==KEYCODE_BACK
I Changed inside MyFragment the visibility of the mDrawerLayout:
public static DrawerLayout mDrawerLayout;
I Called to the openDrawer method in the main activity:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch(keyCode)
{
case KeyEvent.KEYCODE_BACK:
MyFragment.mDrawerLayout.openDrawer(Gravity.LEFT);
}
return super.onKeyDown(keyCode, event);
}
The fact is that there was a little sliding movement (very quick), and then back to hide. After that i figured out that there was sliding while i held the back button pressed, but if i released it, the side menu went back again to original hided position.
What am i doing wrong??
Thx in advance.
I want to go back to the last fragment in the back stack, so I want to make the back button popback the stack. Should I do this? and if so, should I override onBackPressed() or onKeyDown()?
#Override
public void onBackPressed()
{
Intent intent = new Intent(this,ABC.class);
startActivity(intent);
}
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK ) {
// do something on back.
return true;
}
return super.onKeyDown(keyCode, event);
}
My fragments aren't being added to the back stack properly for some reason
I am using this to try to go back to the previous fragment, but the order is acting strange. What exactly should I do to make the order proper?
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft){
if(teamsFrag !=null)
{
if(manage.getBackStackEntryCount() > 0)
manage.popBackStack(manage.getBackStackEntryAt(manage.getBackStackEntryCount()-1).getName(), FragmentManager.POP_BACK_STACK_INCLUSIVE);
//ft.detach(dataFrag);
}
Short answer? No.
Long answer? If you have to ask, No.
You should set your fragments up using the fragment manager so that the back button does what you want. You shouldn't override the back button instead of implementing your stack correctly.
I want to go back to the last fragment in the back stack, so I want to make the back button popback the stack.
This is the default behavior when you use FragmentTransaction#addToBackStack() while adding new Fragments with the FragmentManager.
I have a main activity. It's main purpose is to handle Tab menu. If a user click a tab it'll load another activity as a sub-activity, still showing the tab menu. I am using Intent to do this:
setContent(new Intent(this,B.class))
This sub-activity has an onclick function as well. If the user clicks one of the links it'll load xml layout file using setContentView command:
setContentView(R.layout.B1);
Now, when the back button is pressed while xml file is loaded, it'll close the entire application. Is there a way to prevent this, say, return to the sub-activity or the main activity?
thanks for all your help.
You should override the onBackPressed method in your activity or sub activity:
#Override
public void onBackPressed() {
//TODO Do your task here
}
In your sub activity you should override the fallowing:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) //Override Keyback to do nothing in this case.
{
return true;
}
return super.onKeyDown(keyCode, event); //-->All others key will work as usual
}