I have a BottomSheetBehavior in my main activity to constantly show a bottom drawer.
When I open and then close the keyboard (when pressing an EditText and then pressing Back), the method "OnSlide" gets triggered:
mBottomSheetBehavior.setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() {
#Override
public void onSlide(#NonNull View bottomSheet, float slideOffset)
{
// my code
}
}
I have code in this function that runs when the user slides the drawer up and down with his finger. I don't want this code to be run when the keyboard comes up and back down. It doesn't make sense that it's being called. How can I prevent keyboard changes from running the code in this function ?
I tried:
Changing in my AndroidManifest android:windowSoftInputMode="adjustResize" to android:windowSoftInputMode="adjustNothing" which essentialy does what I want, but removes useful functionalities which I had added (enabling scroll while keyboard is up).
Listening to soft keyboard changes to only execute code when changes are not occuring which doesn't work since OnSlide is called after keyboard is fully hidden (see code below).
Keyboard change listener:
mContentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
Rect r = new Rect();
mContentView.getWindowVisibleDisplayFrame(r);
int screenHeight = mContentView.getRootView().getHeight();
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
int keypadHeight = screenHeight - r.bottom;
// 0.15 ratio is perhaps enough to determine keypad height.
mKeyboardIsOpen = keypadHeight > screenHeight * 0.15;
}
});
Related
Fragment layout
Actually the footer is set inside an activity class and the edittext is placed inside a fragment.
manifest file
<activity
android:name="HomeController"
android:label="#string/app_name"
android:windowSoftInputMode="stateHidden|adjustPan"/>
Inside my fragment class I added
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
These are the code snippet that I used, but it won't work!
Note: in Fragment UI, the whole screen has a scroll view,
Don't know why it is happening like this?Actually I don't want my footer on the keyboard's top.
Any suggestions on how to solve this behavior? And its appriciatble for the replies :)
Add this to your activity.
I did not test, but this might work. it is a bad way though(
contentView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
View rootView = getWindow().getDecorView().getRootView();
rootView.getWindowVisibleDisplayFrame(r);
int screenHeight = rootView.getHeight();
// r.bottom is the position above soft keypad or device button.
// if keypad is shown, the r.bottom is smaller than that before.
int keypadHeight = screenHeight - r.bottom;
Log.d(TAG, "keypadHeight = " + keypadHeight);
if (keypadHeight > screenHeight * 0.15) { // 0.15 ratio is perhaps enough to determine keypad height.
// keyboard is opened
//set bottom navigation(footer) bar to View.GONE
}
else {
// keyboard is closed
//set bottom navigation bar(footer) to View.VISIBLE
}
}
});
Use android:windowSoftInputMode="hidden" in your parent layout in the xml file. also in case it has layoutAbove property then try to remove it.
Try to add the following in manifest file:
android:windowSoftInputMode="stateAlwaysHidden|adjustResize"
Also remove the following from fragment:
getActivity().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
I have 4 lines of input fields in my Layout.
The problem is that when i click the uppermost EditText, soft keyboard appears and hides the lowermost EditText.
My Goal is to keep every EditText fields visible in screen when soft keyboard pops up.
I tried windowSoftInputMode in Manifest but that's not what I wanted, it just keeps the 'focused' input field visible.
Is there any flags for View type to prevent hiding from soft keyboard, so that the screen always scrolls to show the specified Views?
You can put 4 lines of input fields into a linearlayout view then put this linearlayout into a scrollview, when soft keyboard appears, you scroll the scrollview to the last EditText view position.
int[] loc = new int[2];
lastEditTextView.getLocationOnScreen(loc);
the method to detect keyboard appears:
private ViewTreeObserver.OnGlobalLayoutListener mOnGlobalLayoutListener = new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
if (activityRootView == null) {
return;
}
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) {
// keyboard show
scrollView.smoothScrollTo(loc[0],loc[1]);
} else {
// keyboard hidden
}
}
};
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(mOnGlobalLayoutListener);
just like the picture, when the keyboard is show ,it hide the logo.
the question is how to listen the keyboard show/hide even? have some sample?
add this attribute to your activity in manifest
android:windowSoftInputMode="adjustResize"
Get a reference to the layout which you want to hide when keyboard pops up. You can set the visibility of that to GONE when keyboard is shown and to VISIBLE otherwise. So your task now is to detect whether the keyboard is shown or hidden. For that you can use ViewTreeObserver.OnGlobalLayoutListener().
rootView = getWindow().getDecorView().getRootView();
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect rect = new Rect();
rootView.getWindowVisibleDisplayFrame(rect);
int screenHeight = rootView.getHeight();
int keyboardHeight = screenHeight - (rect.bottom - rect.top);
if(keyboardHeight > screenHeight / 3){
//hide the layout
}
else{
//show the layout
}
}
});
I have an activity that hosts a fragment, and several others.
This activity already has fitsSystemWindows=false so that it will extend it's boundaries towards the very bottom of the screen, I am hiding the Navigation Bar btw.
The problem is, it has a fragment that has a RichEditor view inside it which means users should be able to put multiple lines of text inside that fragment. SO I made that fragment fitsSystemWindows=true so that when users start clicking on the RichEditor and types, the adjustResize flag, which I have set in the styles, will fire up and allow the user to scroll the page.
It resizes and allows the user to scroll yes.
The only problem is, when the fragment resizes, it gets additional padding on the top and bottom and since I have a custom toolbar on the main activity, it is very awkwardly visibly being added on screen. The funny thing is, the top padding is exactly the same as the height of the device's status bar and the bottom padding is exactly the same as the height of the Navigation Bar.
I tried making the clipToPadding attribute in the fragment's layout into true and false but it does not stop the fragment from getting the additional paddings. I also searched around but I can't get anything useful to get around this as most of the questions I encounter are about how to ALLOW the resize to happen and not anything about how to remove or stop the padding.
Is there even any way to know when the fragment has been resized or the resize has been triggered? If ever I can use that listener to remove the additional paddings as needed.
Any thoughts on this?
EDIT
I already tried putting fitsystemwindows=true in the main activity, but it gets some padding on the bottom for the navigation bar. And I need the activity to fill the whole screen as the navigation bar is hidden
I have solved this by following Matej Snoha's comment. This method attaches a listener to the fragment and when it sees the heightDifference, which is the size of the keyboard as well, is more than the height of the navigation bar, it puts a padding to the bottom of the view thus allowing it to scroll while the keyboard is open.
public class FragmentResizeUtil {
public static void setListenerforResize(final View root, Fragment fragment) {
final int navigationbarHeight = getNavigationBarHeight(fragment);
root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
Rect r = new Rect();
root.getWindowVisibleDisplayFrame(r);
int screenHeight = root.getRootView().getHeight();
int heightDifference = screenHeight - (r.bottom - r.top);
if (heightDifference > navigationbarHeight) {
root.setPadding(0, 0, 0, heightDifference);
} else {
root.setPadding(0, 0, 0, 0);
}
}
});
}
private static int getNavigationBarHeight(Fragment fragment) {
Resources resources = fragment.getResources();
int resourceId = resources.getIdentifier("navigation_bar_height", "dimen", "android");
if (resourceId > 0) {
return resources.getDimensionPixelSize(resourceId);
}
return 0;
}
}
I'm trying to force the EditText control to lose focus when the user presses the back button to hide the keyboard. There are many questions similar to this already, but after several hours, I haven't been able to make it work.
First, just a little bit of context. I have a ListView with custom items. Each item has several TextViews and one EditText. I have an AfterTextChanged() method saving edited values. I have a style set up to highlight the field if it has focus. Unfortunately, it is now much more obvious that the EditText doesn't actually lose focus when you hide the (soft) keyboard, and I think it's confusing. I would like the EditText to not be focused if there's no keyboard.
The solution that seemed the most reasonable is to override OnBackPressed() in the activity as described here. Unfortunately, it doesn't appear that my method is being called. I.e. the field is still focused, and a breakpoint in the function doesn't fire.
Similarly, an OnKeyUp() listener on the activity doesn't fire, and Xamarin doesn't appear to support the OnKeyUp handler for the EditText control.
I'm not trying to suppress the keyboard on creation, or anything, so using any of the invisible control tricks don't help either.
It's obvious that a lot of people have this problem. I'm sure one of you has solved it! Can you please share your solution?
Thank you so much!
-Karen
P.S. I do not need to know how to hide the keyboard. I need to take an action when the user hides the keyboard with the back button. Thanks :)
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.
Below is a kind of "hacky" way to know when the keyboard is shown/hidden by monitoring the change in the view size. You must also set the Activity to android:windowSoftInputMode="adjustResize" in the AndroidManifest.xml.
final View activityRootView = findViewById("Your main View");
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
activityRootView.getWindowVisibleDisplayFrame(r);
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) { // if more than 100 pixels, its probably a keyboard...
//Keyboard is shown
}
if(heightDiff <= 100) {
//Keybaord not shown
}
}
});
With sincere thanks to #Shadesblade (and Xamarin's sample code), my EditTexts now unfocus! Here's the Xamarin-ized solution:
To your activity, add this class:
class GlobalLayoutListener : Java.Lang.Object, ViewTreeObserver.IOnGlobalLayoutListener
{
Action on_global_layout;
public GlobalLayoutListener (Action onGlobalLayout)
{
on_global_layout = onGlobalLayout;
}
public void OnGlobalLayout ()
{
on_global_layout ();
}
}
Add a class variable to hold the View so that the delegate can access it:
View _rootview;
In your OnCreate() add:
GlobalLayoutListener gll = new GlobalLayoutListener(
delegate {
Android.Graphics.Rect r = new Android.Graphics.Rect();
_rootView.GetWindowVisibleDisplayFrame(r);
int heightDiff = _rootView.RootView.Height - (r.Bottom - r.Top);
if (heightDiff < 100)
{
if (Window.CurrentFocus != null)
Window.CurrentFocus.ClearFocus();
}
});
_rootView = FindViewById<View>(Resource.Id.relativeLayoutOrder);
_rootView.ViewTreeObserver.AddOnGlobalLayoutListener(gll);
I expect to need to dork around with the heightDiff level and/or have to add some rotation checking, but I haven't done any rotation support at this point, so I can punt that until later.
Thank you again! *happy dance*
adding on to Shadesblade's answer, if you are using a scrollview, his answer needs a change to work, because not all of the scrollview is showing on screen.
so instead of doing
int heightDiff = activityRootView.getRootView().getHeight() - (r.bottom - r.top);
you should do
int heightDiff = Utils.getScreenHeight(SearchActivity.this) - (r.bottom - r.top);
where Utils.getScreenHeight is this:
public static int getScreenHeight(Context c) {
if (screenHeight == 0) {
WindowManager wm = (WindowManager) c.getSystemService(Context.WINDOW_SERVICE);
Display display = wm.getDefaultDisplay();
Point size = new Point();
display.getSize(size);
screenHeight = size.y;
screenWidth = size.x;
}
return screenHeight;
}