Action mode started by calling getActivity().startActionMode(calback); is automatically canceled after back button pressed. Is possible avoid this behavior? I need to do another operation after back button was pressed in some situation during action mode.
This is an interesting problem. When the ActionMode is active the back key event is consumed internally. The event is not propagated to either onBackPressed() or onKeyUp(int keyCode, KeyEvent event) callbacks.
Fortunately, you can use dispatchKeyEvent(KeyEvent event) which is still called.
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
if(mActionModeIsActive) {
if (event.getKeyCode() == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
// handle your back button code here
return true; // consumes the back key event - ActionMode is not finished
}
}
return super.dispatchKeyEvent(event);
}
You might wonder what will be the behavior in case you have a submenu in the ActionMode and you close it with the back key. In this case dispatchKeyEvent() is not called so you can safely use the code.
The above code works also with ActionBarSherlock. The only problem I found is on Android 3.1 device when the native ActionMode is used, in this case the dispatchKeyEvent() is not called. Use ActionBarSherlock's ActionMode to solve it.
Suggested solutions did not work for me. So I decide to create back event manually. I needed this event in my fragment so I created BaseFragment that all my fragments will extend.
public abstract class BaseFragment extends Fragment {
private ActionModeState actionModeState = ActionModeState.ITEM_NOT_CLICKED;
protected enum ActionModeState {
ITEM_NOT_CLICKED, ITEM_CLICKED
}
protected void onActionItemClicked() {
actionModeState = ActionModeState.ITEM_CLICKED;
}
protected void onDestroyActionMode() {
if (actionModeState == ActionModeState.ITEM_NOT_CLICKED) {
onActionModeBackPressed();
} else {
// reset state
actionModeState = ActionModeState.ITEM_NOT_CLICKED;
}
}
protected void onActionModeBackPressed() { }
}
Main fragment
public class YourMainFragment extends BaseMapFragment {
#Override
public void onActionModeBackPressed() {
// you code for action mode back button
}
private ActionMode.Callback actionModeCallback = new ActionMode.Callback() {
#Override
public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
YourMainFragment.this.onActionItemClicked();
....
}
#Override
public void onDestroyActionMode(ActionMode mode) {
YourMainFragment.this.onDestroyActionMode();
...
}
};
Create your own Window.Callback and intercept event before it is passed to AppCompatDelegateImplBase.
#Override
public boolean onCreateActionMode(ActionMode mode, Menu menu) {
//default delegate
final Window window = getActivity().getWindow();
mWindowCallbackDelegate = new WindowCallbackDelegate(window.getCallback(), this);
window.setCallback(mWindowCallbackDelegate);
return true;
}
In your own delegate :
#Override
public boolean dispatchKeyEvent(KeyEvent event) {
return myWindowDelegate.dispatchKeyEvent(event) || mOriginalWindowCallback.dispatchKeyEvent(event);
}
When you destroy action mode, restore reference to previous delegate
#Override
public void onDestroyActionMode(ActionMode mode) {
Window.Callback originalWindowCallback = mWindowCallbackDelegate.getOriginalWindowCallback();
if (originalWindowCallback != null) {
getActivity().getWindow().setCallback(originalWindowCallback);
}}
You own delegate signature:
public class WindowCallbackDelegate implements Window.Callback {...}
Related
I want to change the functionality of back button for a particular bunch of code being active. Once its done how do i reset it to default.
I am using following piece of code:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
if(keyCode== KeyEvent.KEYCODE_BACK && issearchopen)
{
searchView.closeSearch();
}
else{
}
return true;
}
boolean issearchopen is true whenever my 'bunch' of code is active.
Thanks in advance
It should look like this
#Override
public void onBackPressed() {
if(isSearchOpen){
// do stuff
} else {
super.onBackPressed(); // default behaviour
}
}
To do it you need to override onBackPressed() method.
Your code will looks like this
#Override
public void onBackPressed() {
if(issearchopen){
searchView.closeSearch();
issearchopen = false;
} else {
super.onBackPressed(); // default back press behaviour
}
}
Also don't forget to change issearchopen to true when open searchView.
I am able to detect when the back button is pressed by using:
#Override
public void onBackPressed() {
//Do my thing
}
But now I want to detect when the back button is released. How can I do that? I'm unable to find any method like onBackReleased() which I can override to do my thing.
There is no method that specifically detects the back button release, but you could use the onKeyDown() and onKeyUp() methods. Check to make sure the key is the back button, and do your stuff in the method. Here is something you could do:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// do your stuff for when the back button is initially pressed
return true;
} else {
return false;
}
}
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
// do your stuff for when the back button is released
return true;
} else {
return false;
}
}
If you wanted to override the default back press, you could just use the onBackPressed() method, like so:
#Override
public void onBackPressed() {
// Prevents the user from clicking the back button and returning to the parent activity
}
These methods can be implemented directly in your class. Hope it helps!
I am using Master/Detail layout in a tablet, on the left few buttons to open several fragments, one of the fragments contains youtube player.
The Problem,
When the youtube player is full screen, and i press back button, The activity onBackPressed is called, and the whole activity is closed.
What i have tried,
1- Added on KeyListener for parent fragment (which contains the Youtube Fragment) and handle when click on back button, but this listener is called only if the player is not fullscreen, otherwise it is not called,
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 && event.getAction() == KeyEvent.ACTION_UP) {
try {
// Close full screen
return true;
} catch (Exception e) {
AHHExceptionHandler.handleException(e);
}
}
return false;
}
});
2- Added onKeyListener to youtubeFragment View to check if it is full screen then close the full screen mode
youTubeFragment.getView().setFocusableInTouchMode(true);
youTubeFragment.getView().requestFocus();
youTubeFragment.getView().setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_UP) {
try {
youTubePlayer.setFullscreen(false);
return true;
} catch (Exception e) {
AHHExceptionHandler.handleException(e);
}
}
return false;
}
});
And this is also not called in all cases.
I need to handle the hardware back button while the youtube player is in fullscreen, the fullscreen mode is closed and the application is in its previous state.
Thanks.
Edit 1:
- I want to handle this inside the fragment, instead of handling it in the parent Activity, I already handling it inside the parent activity, but I don't like this solution.
Just to add to Andre's reply.
In your parent Activity add:
public YouTubePlayer youTubePlayer;
public boolean isYouTubePlayerFullScreen;
In your fragment add:
MainActivity mainActivity = (MainActivity) getActivity();
Then, on YouTubePlayerFragment initialization:
#Override
public void onInitializationSuccess(YouTubePlayer.Provider provider, YouTubePlayer player, boolean b) {
mainActivity.youTubePlayer = player;
player.loadVideo(videoId);
player.setOnFullscreenListener(new YouTubePlayer.OnFullscreenListener() {
#Override
public void onFullscreen(boolean b) {
mainActivity.isYouTubePlayerFullScreen = b;
}
});
}
Now your Activity has access to YouTubePlayer and information if player is full screen or not.
Next, inside your Activity, override onBackPressed like this:
#Override
public void onBackPressed() {
if (youTubePlayer != null && isYouTubePlayerFullScreen){
youTubePlayer.setFullscreen(false);
} else {
super.onBackPressed();
}
}
You can override onBackPressed() in your activity and in case your player is in fullscreen, you can handle it there as follows:
#Override
public void onBackPressed() {
if (youtubePlayer != null && isFullscreen) {
// if fullscreen, set fullscreen false
youtubePlayer.setFullscreen(false);
} else {
// if NOT fullscreen, perform default call on back press
super.onBackPressed();
}
}
P.S. In above code fragment, isFullscreen is instance variable of our
activity and is synced with youtubePlayer state from within
OnFullscreenListener set on youtubePlayer.setOnFullScreenListener().
Hope that makes sense :)
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 have this hierarchy of the views:
Activity -> ViewPager -> MyViewGroup
In MyViewGroup (and only there) I override onKeyUp event:
#Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
Log.i(TAG, "Back");
return true;
} else {
return super.onKeyUp(keyCode, event);
}
}
But when I press "Back" key, this override is not called and the app is closed. Why?
By default, the back button finishes the activity. From the android source:
Called when the activity has detected the user's press of the back key. The default implementation simply finishes the current activity, but you can override this to do whatever you want.
public void onBackPressed() {
if (!mFragments.popBackStackImmediate()) {
finish();
}
}
If you wish to alter this behavior you have to override onBackPressed in your Activity and make it not call super. Then you can implement your own behavior.
public #Override void onBackPressed()
{
if (doSomeThingOnBackPressed) {
// do some stuff
return;
}
super.onBackPressed();
}