How to draw view on top of soft keyboard like WhatsApp? - android

I want to know how it's possible to add View on top of Keyboard like WhatsApp and Hangout. In chat screen, they insert emoticons view on top of the opened soft keyboard.
Does anyone know how to achieve this behavior?

Well, I have created a sample keyboard for chatting here...
Here, I use popup window for showing popup window and height of popup is calculated dynamically by height of keyboard
// Initially defining default height of keyboard which is equal to 230 dip
final float popUpheight = getResources().getDimension(
R.dimen.keyboard_height);
changeKeyboardHeight((int) popUpheight);
// Creating a pop window for emoticons keyboard
popupWindow = new PopupWindow(popUpView, LayoutParams.MATCH_PARENT,
(int) keyboardHeight, false);
and height is calculated using this function :
/**
* Checking keyboard height and keyboard visibility
*/
int previousHeightDiffrence = 0;
private void checkKeyboardHeight(final View parentLayout) {
parentLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
parentLayout.getWindowVisibleDisplayFrame(r);
int screenHeight = parentLayout.getRootView()
.getHeight();
int heightDifference = screenHeight - (r.bottom);
if (previousHeightDiffrence - heightDifference > 50) {
popupWindow.dismiss();
}
previousHeightDiffrence = heightDifference;
if (heightDifference > 100) {
isKeyBoardVisible = true;
changeKeyboardHeight(heightDifference);
} else {
isKeyBoardVisible = false;
}
}
});
}
Using all these stuff i am able to make a perfect overlapping keyboard....
then i inflate popup window with viewpager and gridview for emoticons.
Also, i use spannable string for showing these emoticons in listview and chat window

After a heavy time of research and try-and-error, I've found another solution similar to the one of Chirag Jain above, but using a custom Dialog.
mDialogKeyboard = new Dialog(this,android.R.style.Theme_NoTitleBar);
mDialogKeyboard.setContentView(R.layout.your_custom_layout);
mDialogKeyboard.getWindow().setFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
mDialogKeyboard.getWindow().setFlags(WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH);
mDialogKeyboard.getWindow().setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
mDialogKeyboard.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
WindowManager.LayoutParams lp=mDialogKeyboard.getWindow().getAttributes();
lp.width=WindowManager.LayoutParams.MATCH_PARENT;
lp.height=mSoftKeyboardHeight;
lp.gravity=Gravity.BOTTOM | Gravity.LEFT;
lp.dimAmount=0;
Despite the fact that Chirag Jain answer seems to be more clean, I'll post this here for have an alternative method.

As far as I know you can draw on other applications, yes. I myself have designed such an app. As for drawing on an application such as the keyboard or any other application in specific, I guess, you'll have to define a layout with a height that's exactly that of the keyboard. So, that would vary from device to device. So, this isn't possible.
I still stick to my notion that WhatsApp merely dismisses the soft keyboard on pressing the smiley button and calls it's own fragment.
If you would still like to pursue this, here's how you draw a "window" over other applications. These should be it's layout parameters.
params = new WindowManager.LayoutParams(
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN,
PixelFormat.TRANSLUCENT);
Albeit, your width will change to an absolute pixel value since you'd like the activity to be over the keyboard only.
If I've misunderstood the question, please correct me.

what my thinking is they have created their own keypad for smiles, and on click of smile icon or keypad icon they are hiding smile keypad and showing the normal keypad. there are two scenarios in whats app case 1) if you don't focus 1st time of editext then you can not see the show keypad button,and the height of smile keypad is exactly same as normal keypad,we will get the keypad height only after our view layout is changed, means only after the keypad is shown, that means they are creating their own keypad.. 2) if you focus the edittext and click of smile button then it will show the option of show keypad button Please correct me if i am not right on this

I recently had to implement a view which would be above a soft keyboard. #Chirag Jain's solution is almost right, but it does not count with the system buttons in the bottom of the screen! This will make the keyboard height incorrect on some devices like NEXUS 6. This solution should work across all devices:
1) create layout which contains your view
<RelativeLayout
android:id="#+id/keyboard_info_container"
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_alignParentBottom="true"
android:background="#color/C12"
android:padding="10dp"
android:visibility="invisible">
....
</RelativeLayout>
2) Bind view
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootview = inflater.inflate(R.layout.notifications_email_settings_fragment, container, false);
ButterKnife.bind(this, rootview);
checkKeyboardHeight(rootview);
3) keyboard check and view margin settings
private void checkKeyboardHeight(final View parentLayout) {
parentLayout.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
int previousHeightDiffrence = 0;
int systemBarHigh = 999999;
#Override
public void onGlobalLayout() {
Rect r = new Rect();
parentLayout.getWindowVisibleDisplayFrame(r);
int screenHeight = parentLayout.getRootView()
.getHeight();
int keyboardHeight = screenHeight - (r.bottom);
if(systemBarHigh > keyboardHeight) {
systemBarHigh = keyboardHeight;
}
if (keyboardHeight > 250) {
int keyboardHightWithoutSystemBar = keyboardHeight - systemBarHigh;
// no need to update when the keyboard goes down
if (previousHeightDiffrence != keyboardHightWithoutSystemBar) { // if (Math.abs(previousHeightDiffrence - keyboardHeight) > 10) {
adjustKeyboard(keyboardHightWithoutSystemBar);
}
keyboardInfoContainer.setVisibility(View.VISIBLE);
isKeyBoardVisible = true;
previousHeightDiffrence = keyboardHightWithoutSystemBar;
} else {
isKeyBoardVisible = false;
if (keyboardInfoContainer != null) {
keyboardInfoContainer.setVisibility(View.INVISIBLE);
}
}
}
});
}
private void adjustKeyboard(int keyboardHeight) {
RelativeLayout.LayoutParams lp = (RelativeLayout.LayoutParams) keyboardInfoContainer.getLayoutParams();
lp.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM);
lp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
lp.bottomMargin = keyboardHeight;
keyboardInfoContainer.requestLayout();
}

#jirkarrr, Why don't you add the keyboardInfoContainer like this:
WindowManager wm = getWindowManager();
WindowManager.LayoutParams lps = new WindowManager.LayoutParams();
lps.x = 0; lps.y = keyboardHeight;
wm.addView(keyboardInfoContainer, lps);
I do as your code, but it cannot show out the keyboardInfoContainer.

I use a popup to put view over the keyboard:
public void showPopUpKeyboard() {
mIsPopupVisible = true;
// Initialize a new instance of LayoutInflater service
LayoutInflater inflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);
// Inflate the custom layout/view
View customView = inflater.inflate(R.layout.popup_in_keyboard, null);
mScrollView = (ScrollView) customView.findViewById(R.id.keyboard_layout_view);
// Initialize a new instance of popup window
mPopupWindow = new PopupWindow(
customView,
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT
);
setSizeForSoftKeyboard();
// Get a reference for the custom view close button
Button closeButton = (Button) customView.findViewById(R.id.ib_close);
// Set a click listener for the popup window close button
closeButton.setOnClickListener((View view) -> {
// Dismiss the popup window
mIsPopupVisible = false;
mPopupWindow.dismiss();
});
mPopupWindow.showAtLocation(mParentLayout, Gravity.CENTER, 0, 0);
}
Then I try to know keyboard's height:
mParentLayout.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
Rect r = new Rect();
mParentLayout.getWindowVisibleDisplayFrame(r);
int heightDiff = mParentLayout.getRootView().getHeight() - (r.bottom - r.top);
if (heightDiff > 100) {
//enter your code here
if (mIsPopupVisible) {
keepKeyboard();
mIsPopupVisible = false;
mPopupWindow.dismiss();
}
} else {
//enter code for hid
}
});
You can check this tutorial and this example in GitHub

Related

Android How To hide/show some view when keyboard show/hide?

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

Getting the dimensions of the soft keyboard

Is there a way to know the size of the keyboard that is shown in the screen?
I am using Cocos2dx for programming, but I want to know the height of the keyboard shown in screen in the part of Android or the part of Cocos, it does not matter.
I know that Keyboard has a getHeight() method but I don't want to create new keyboards, i want to use the default one.
We did it with this
myLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
parent.getWindowVisibleDisplayFrame(r);
int screenHeight = parent.getRootView().getHeight();
int heightDifference = screenHeight - (r.bottom - r.top);
Log.d("Keyboard Size", "Size: " + heightDifference);
}
});
We only resize views with the keyboard, so we could use this.
Rect r = new Rect();
View rootview = this.getWindow().getDecorView(); // this = activity
rootview.getWindowVisibleDisplayFrame(r);
Result of this is the amount of space your application uses on screen (works even when activity is not resized). Obviously remaining screen space will be used by the keyboard ( if its visible)
Found id up here: https://github.com/freshplanet/ANE-KeyboardSize/blob/master/android/src/com/freshplanet/ane/KeyboardSize/getKeyboardY.java
if your activity is not fullscreen, using code below:
content.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
// TODO Auto-generated method stub
if (keyBoardHeight <= 100) {
Rect r = new Rect();
content.getWindowVisibleDisplayFrame(r);
int screenHeight = content.getRootView()
.getHeight();
int heightDifference = screenHeight
- (r.bottom - r.top);
int resourceId = getResources()
.getIdentifier("status_bar_height",
"dimen", "android");
if (resourceId > 0) {
heightDifference -= getResources()
.getDimensionPixelSize(resourceId);
}
if (heightDifference > 100) {
keyBoardHeight = heightDifference;
}
Log.d("Keyboard Size", "Size: " + heightDifference);
}
// boolean visible = heightDiff > screenHeight / 3;
}
});
If you want to calculate the Virtual Keyboard height while your activity does not change in size (adjustPan) then you can use this sample:
https://github.com/siebeprojects/samples-keyboardheight
It uses a hidden window in order to calculate the height difference between the window and the root view of the activity.
You can't tell. No, really: you simply can't tell.
The keyboard does not need to be any particular shape. It does not have to be placed at the bottom of the screen (many of the most popular options are not), it does not have to keep its current size when you change text fields (almost none do depending on the flags). It does not even have to be rectangular. It may also just take over the entire screen.
I know this is an old post, but I noticed that the chosen solution for me did not work on all devices. There seemed to be a discrepancy and so I implemented this and it seems to be a catch all:
final int[] discrepancy = new int[1];
discrepancy[0] = 0;
// this gets the height of the keyboard
content.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
View rootview = activity.getWindow().getDecorView(); // this = activity
rootview.getWindowVisibleDisplayFrame(r);
int screen_height = rootview.getRootView().getHeight();
int keyboard_height = screen_height - (r.bottom + r.top) - discrepancy[0];
if (discrepancy[0] == 0) {
discrepancy[0] = keyboard_height;
if (keyboard_height == 0) discrepancy[0] = 1;
}
int margin_bottom = keyboard_height + Helper.getDp(10, activity);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) carousel_container.getLayoutParams();
params.setMargins(0, 0, 0, margin_bottom);
//boolean visible = heightDiff > screenHeight / 3;
}
});
When the listener is first called it measures the screen without a keyboard and if there is a discrepancy I account for it the next time around. If there is no discrepancy I set the discrepancy to 1 just so it is no longer 0.
After 2020, if your min SDK large or equal then 21, you can check the visibility and height of IME by below functions:
fun isKeyboardVisible(attachedView: View): Boolean {
val insets = ViewCompat.getRootWindowInsets(attachedView)
return insets?.isVisible(WindowInsetsCompat.Type.ime()) ?: false
}
fun getKeyboardHeight(attachedView: View): Int {
val insets = ViewCompat.getRootWindowInsets(attachedView)
return insets?.getInsets(WindowInsetsCompat.Type.ime())?.bottom ?: 0
}
Ref: Animating your keyboard (part 1). New WindowInsets APIs for checking theā€¦ | by Chris Banes | Android Developers | Medium
in cocos2d-x we have got CCEditBox.
Inside Extensions->GUI->CCEditBox, you can find the class CCEditBox.
The beauty is that it hides the keyboard of tapping somewhere else on the scene. and automatically moves the keyboard up incase your edit box was placed too low on the scene.
If you are using cocos2d-x v2.1.3 then you can navigate to sample Project by going to
samples->cpp->TestCpp->Classes->ExtensionTest->EditBoxTest.
I'm just going to use it instead of CCTextField from now on. just came across it yesterday :)
After hours of searching I found a solution if you want to set windowSoftInput="adjustPan"
Here is the code snippet:
final View root = findViewById(android.R.id.content);
root.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
Rect r = new Rect();
{
root.getWindowVisibleDisplayFrame(r);
}
#Override
public void onGlobalLayout() {
Rect r2 = new Rect();
root.getWindowVisibleDisplayFrame(r2);
int keyboardHeight = r.height() - r2.height();
if (keyboardHeight > 100) {
root.scrollTo(0, keyboardHeight);
}
else {
root.scrollTo(0, 0);
}
}
});
In this code, after I found the keyboard height I scroll the view up to not covered by the keyboard which is the main reason for finding the keyboard height.
According to the docs :
void getWindowVisibleDisplayFrame(Rect outRect) : Retrieve the overall visible display size in which the window this view is attached to has been positioned in.
The ROOT_VIEW of an android display screen can be visualized as being a single screen view with VISIBLE DISPLAY FRAME which displays your activity's view.
This VISIBLE DISPLAY FRAME is adjusted when SOFT KEYBOARD is displayed or hidden from the screen.
NOTE : Please look at the two images by clicking on the links given below for better understanding
So the ROOT VIEW of a display screen can be visualized as :
RootView of display screen
The adjustment of VISIBLE DISPLAY FRAME with the opening and closing of SOFT KEYBOARD can be visualized as :
VISIBLE_DISPLAY_SCREEN adjustment
This adjustment of the VISUAL DISPLAY FRAME can be very well used to find out the height of the keyboard as :
(when the soft keyboard is open)
SOFT_KEYBOARD_HEIGHT = ROOT_VIEW_HEIGHT - (VISUAL_DISPLAY_FRAME_HEIGHT + EXTRA_SCREEN_HEIGHT)
The code to achieve the above is :
int mExtraScreenHeight=-1, mKeyboardHeight=-1;
boolean mKeyboardOpen;
rootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int rootViewHeight, visibleDisplayFrameHeight, fakeHeight;
/* (rootViewHeight - visibleDisplayFrameHeight) is not the real height of the keyboard
it is the fake height as it also consist of extra screen height
so FAKE_HEIGHT = KEYBOARD_HEIGHT + EXTRA_SCREEN_HEIGHT
To get keyboard height extra screen height must be removed from fake height
*/
Rect rect = new Rect();
rootView.getWindowVisibleDisplayFrame(rect);
rootViewHeight = rootView.getRootView().getHeight();
visibleDisplayFrameHeight = rect.height();
fakeHeight = rootViewHeight-visibleDisplayFrameHeight;
if (mExtraScreenHeight == -1){
mExtraScreenHeight=fakeHeight;
}
/* Suppose the soft keyboard is open then the VISIBLE_DISPLAY_FRAME is in reduced size
due to the space taken up by extra screen and the keyboard but when the soft keyboard closes
then KEYBOARD_HEIGHT=0 and thus FAKE_HEIGHT = EXTRA_SCREEN_HEIGHT
*/
else if (fakeHeight <= mExtraScreenHeight){
mExtraScreenHeight=fakeHeight;
mKeypadOpen=false;
}
else if (fakeHeight > mExtraScreenHeight){
mKeypadHeight=fakeHeight-mExtraScreenHeight;
mKeypadOpen=true;
}
}
});
NOTE : The onGlobalLayout() function will be called only when the global layout changes like when the soft keyboard opens. So the soft keyboard must be open at least once to get the soft keyboard height.
It worked for me ;)
Sorry for not being able to comment, two or three of the answers helped me solve my issue and they were related to using the AddOnGlobalLayoutListener and then determining the remaining height before and after a keyboard showed up.
The solution I used was based off of Rudy_TM's answer.
HOWEVER, one thing that I had to find was that in order for that method to work, you must have the following line somewhere
Window.SetSoftInputMode(SoftInput.AdjustResize);
Before I had SoftInput.AdjustNothing (or something like that) and it would not work. Now it works perfect. Thanks for the answers!
Complete answer & worked perfectly for me:
Rect r = new Rect();
View rootview = this.getWindow().getDecorView(); // this = activity
rootview.getWindowVisibleDisplayFrame(r);
int keyboardHeight = rootview.getHeight() - r.bottom;

How to restore the popup window's position when changes the orientation in android?

I am trying to show popup windows wherever user touchs the screen. I am able to display popup windows at desired location. But problem is when popup window is in portrait mode if i changes the orientation in landscape mode that popup window is displaying in same position , because of that popup window is overlapping with views in landscape mode and same problem occurs when we change orientation landscape to portrait . My requirements are below
1. Dont want to dismiss popup window when changes the orientation.
2. change the positions of the all popup windows dynamically, whenever orientation changes so that views are not overlapped(popup window doesnt overlaps with image). Example when i change the orientation portrait to landscape popup windows position will move to bit upwards.
private void showPopup(final Activity context, Point p) {
int popupWidth = 200;
int popupHeight = 150;
boolean showEditText = true;
// Inflate the popup_layout.xml
LinearLayout viewGroup = (LinearLayout) context
.findViewById(R.id.popup);
LayoutInflater layoutInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout = layoutInflater.inflate(R.layout.popup_layout, viewGroup);
// Creating the PopupWindow
final PopupWindow popup = new PopupWindow(context);
popup.setContentView(layout);
popup.setWidth(popupWidth);
popup.setHeight(popupHeight);
popup.setFocusable(false);
popup.setOutsideTouchable(false);
// Some offset to align the popup a bit to the right, and a bit down,
// relative to button's position.
int OFFSET_X = 5;
int OFFSET_Y = 5;
// Clear the default translucent background
popup.setBackgroundDrawable(new BitmapDrawable());
// Displaying the popup at the specified location, + offsets.
popup.showAtLocation(layout, Gravity.NO_GRAVITY, p.x + OFFSET_X, p.y
+ OFFSET_Y);
final Button addname = (Button) layout.findViewById(R.id.addName);
}
I know it's not a good solution but we can do it like this
make popup as class level object
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
new Handler().postDelayed(new Runnable() {
#Override
public void run() {
if (popupWindow != null && popupWindow.isShowing()) {
popupWindow.dismiss();
showPopup(...);
}
}
},500);
}

Android How to adjust layout in Full Screen Mode when softkeyboard is visible

I have researched a lot to adjust the layout when softkeyboard is active and I have successfully implemented it but the problem comes when I use android:theme="#android:style/Theme.NoTitleBar.Fullscreen" this in my activity tag in manifest file.
For this I have used android:windowSoftInputMode="adjustPan|adjustResize|stateHidden" with different options but no luck.
After that I implemented FullScreen programmatically and tried various layout to work with FullScreen but all in vain.
I referred these links and have looked many posts here related to this issue:
http://android-developers.blogspot.com/2009/04/updating-applications-for-on-screen.html
http://davidwparker.com/2011/08/30/android-how-to-float-a-row-above-keyboard/
Here is xml code:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="#+id/masterContainerView"
android:layout_width="fill_parent" android:layout_height="fill_parent"
android:orientation="vertical" xmlns:android="http://schemas.android.com/apk/res/android"
android:background="#ffffff">
<ScrollView android:id="#+id/parentScrollView"
android:layout_width="fill_parent" android:layout_height="wrap_content">
<LinearLayout android:layout_width="fill_parent"
android:layout_height="fill_parent" android:orientation="vertical">
<TextView android:id="#+id/setup_txt" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:text="Setup - Step 1 of 3"
android:textColor="#color/top_header_txt_color" android:textSize="20dp"
android:padding="8dp" android:gravity="center_horizontal" />
<TextView android:id="#+id/txt_header" android:layout_width="fill_parent"
android:layout_height="40dp" android:text="AutoReply:"
android:textColor="#color/top_header_txt_color" android:textSize="14dp"
android:textStyle="bold" android:padding="10dp"
android:layout_below="#+id/setup_txt" />
<EditText android:id="#+id/edit_message"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:text="Some text here." android:textSize="16dp"
android:textColor="#color/setting_editmsg_color" android:padding="10dp"
android:minLines="5" android:maxLines="6" android:layout_below="#+id/txt_header"
android:gravity="top" android:scrollbars="vertical"
android:maxLength="132" />
<ImageView android:id="#+id/image_bottom"
android:layout_width="fill_parent" android:layout_height="wrap_content"
android:layout_below="#+id/edit_message" />
</LinearLayout>
</ScrollView>
<RelativeLayout android:id="#+id/scoringContainerView"
android:layout_width="fill_parent" android:layout_height="50px"
android:orientation="vertical" android:layout_alignParentBottom="true"
android:background="#535254">
<Button android:id="#+id/btn_save" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_alignParentRight="true"
android:layout_marginTop="7dp" android:layout_marginRight="15dp"
android:layout_below="#+id/edit_message"
android:text = "Save" />
<Button android:id="#+id/btn_cancel" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_marginTop="7dp"
android:layout_marginRight="10dp" android:layout_below="#+id/edit_message"
android:layout_toLeftOf="#+id/btn_save" android:text = "Cancel" />
</RelativeLayout>
</RelativeLayout>
I want the bottom 2 buttons should go upward when the softkeyboard comes in picture.
Based on yghm's workaround, I coded up a convenience class that allows me to solve the problem with a one-liner (after adding the new class to my source code of course). The one-liner is:
AndroidBug5497Workaround.assistActivity(this);
And the implementation class is:
public class AndroidBug5497Workaround {
// For more information, see https://issuetracker.google.com/issues/36911528
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static void assistActivity (Activity activity) {
new AndroidBug5497Workaround(activity);
}
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
});
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
Since the answer has already been picked and problem known to be a bug, I thought I would add a "Possible Work Around".
You can toggle fullScreen mode when soft keyboard is shown. This allows the "adjustPan" to work correctly.
In other words, I still use #android:style/Theme.Black.NoTitleBar.Fullscreen as part of the application theme and stateVisible|adjustResize as part of the activity window soft input mode but to get them to work together I must toggle fullscreen mode before the keyboard comes up.
Use the following Code:
Turn Off full screen mode
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
Turn On full screen mode
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
Note - inspiration came from: Hiding Title in a Fullscreen mode
I tried the solution from Joseph Johnson, but like others I ran into the gap-between-content-and-keyboard problem. The problem occurs because the soft input mode is always pan when using full-screen mode. This panning interferes with Joseph's solution when you activate an input field that would be hidden by the soft input.
When the soft input appears, the content is first panned based on its original height, and then resized by the layout requested by the Joseph's solution. The resizing and subsequent layout do not undo the panning, which results in the gap. The full order of events is:
Global layout listener
Panning
Layout of content (= actual resizing of content)
It is not possible to disable panning, but it is possible to force the pan offset to be 0 by changing the height of the content. This can be done in the listener, because it is run before panning takes place. Setting the content height to the available height results in a smooth user experience, i.e. no flickering.
I also made these changes. If any of these introduce issues, let me know:
Switched determination of available height to use getWindowVisibleDisplayFrame. The Rect is cached to prevent a little bit of unneeded garbage.
Allow the listener to be removed too. This is useful when you reuse an activity for different fragments having different full-screen requirements.
Do not distinguish between keyboard shown or hidden, but always set the content height to the visible display frame height.
It has been tested on a Nexus 5, and emulators running API levels 16-24 with screen sizes ranging from tiny to big.
The code has been ported to Kotlin, but porting my changes back to Java is simple. Let me know if you need help:
class AndroidBug5497Workaround constructor(activity: Activity) {
private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
private val rootView = contentContainer.getChildAt(0)
private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
private val viewTreeObserver = rootView.viewTreeObserver
private val listener = ViewTreeObserver.OnGlobalLayoutListener { possiblyResizeChildOfContent() }
private val contentAreaOfWindowBounds = Rect()
private var usableHeightPrevious = 0
// I call this in "onResume()" of my fragment
fun addListener() {
viewTreeObserver.addOnGlobalLayoutListener(listener)
}
// I call this in "onPause()" of my fragment
fun removeListener() {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
private fun possiblyResizeChildOfContent() {
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
// Change the bounds of the root view to prevent gap between keyboard and content, and top of content positioned above top screen edge.
rootView.layout(contentAreaOfWindowBounds.left, contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom)
rootView.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
I just found a simple and reliable solution if you are using the system UI approach (https://developer.android.com/training/system-ui/immersive.html).
It works in the case when you are using View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN, e.g. if you are using CoordinatorLayout.
It won't work for WindowManager.LayoutParams.FLAG_FULLSCREEN (The one you can also set in theme with android:windowFullscreen), but you can achieve similar effect with SYSTEM_UI_FLAG_LAYOUT_STABLE (which "has the same visual effect" according to the docs) and this solution should work again.
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_FULLSCREEN
| View.SYSTEM_UI_FLAG_HIDE_NAVIGATION /* If you want to hide navigation */
| View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_STABLE)
I've tested it on my device running Marshmallow.
The key is that soft keyboards are also one of the system windows (such as status bar and navigation bar), so the WindowInsets dispatched by system contains accurate and reliable information about it.
For the use case such as in DrawerLayout where we are trying to draw behind the status bar, We can create a layout that ignores only the top inset, and applies the bottom inset which accounts for the soft keyboard.
Here is my custom FrameLayout:
/**
* Implements an effect similar to {#code android:fitsSystemWindows="true"} on Lollipop or higher,
* except ignoring the top system window inset. {#code android:fitsSystemWindows="true"} does not
* and should not be set on this layout.
*/
public class FitsSystemWindowsExceptTopFrameLayout extends FrameLayout {
public FitsSystemWindowsExceptTopFrameLayout(Context context) {
super(context);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
public FitsSystemWindowsExceptTopFrameLayout(Context context, AttributeSet attrs,
int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public WindowInsets onApplyWindowInsets(WindowInsets insets) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setPadding(insets.getSystemWindowInsetLeft(), 0, insets.getSystemWindowInsetRight(),
insets.getSystemWindowInsetBottom());
return insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(), 0, 0);
} else {
return super.onApplyWindowInsets(insets);
}
}
}
And to use it:
<com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- Your original layout here -->
</com.example.yourapplication.FitsSystemWindowsExceptTopFrameLayout>
This should theoretically work for any device without insane modification, much better than any hack that tries to take a random 1/3 or 1/4 of screen size as reference.
(It requires API 16+, but I'm using fullscreen only on Lollipop+ for drawing behind the status bar so it's the best solution in this case.)
Please note that android:windowSoftInputMode="adjustResize" does not work when WindowManager.LayoutParams.FLAG_FULLSCREENis set for an activity. You've got two options.
Either disable fullscreen mode for your activity. Activity is not re-sized in fullscreen mode. You can do this either in xml (by changing the theme of the activity) or in Java code. Add the following lines in your onCreate() method.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);`
OR
Use an alternative way to achieve fullscreen mode. Add the following code in your onCreate() method.
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
View decorView = getWindow().getDecorView();
// Hide the status bar.
int uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN;
decorView.setSystemUiVisibility(uiOptions);`
Please note that method-2 only works in Android 4.1 and above.
I had to face this problem too and had a work around which i checked on HTC one, galaxy s1, s2, s3, note and HTC sensation.
put a global layout listener on the root view of your layout
mRootView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener(){
public void onGlobalLayout() {
checkHeightDifference();
}
});
and in there i checked the height difference and if the height difference of the screen is bigger then a third on the screen height then we can assume the keyboard is open.
took it from this answer.
private void checkHeightDifference(){
// get screen frame rectangle
Rect r = new Rect();
mRootView.getWindowVisibleDisplayFrame(r);
// get screen height
int screenHeight = mRootView.getRootView().getHeight();
// calculate the height difference
int heightDifference = screenHeight - (r.bottom - r.top);
// if height difference is different then the last height difference and
// is bigger then a third of the screen we can assume the keyboard is open
if (heightDifference > screenHeight/3 && heightDifference != mLastHeightDifferece) {
// keyboard visiblevisible
// get root view layout params
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
// set the root view height to screen height minus the height difference
lp.height = screenHeight - heightDifference;
// call request layout so the changes will take affect
.requestLayout();
// save the height difference so we will run this code only when a change occurs.
mLastHeightDifferece = heightDifference;
} else if (heightDifference != mLastHeightDifferece) {
// keyboard hidden
PFLog.d("[ChatroomActivity] checkHeightDifference keyboard hidden");
// get root view layout params and reset all the changes we have made when the keyboard opened.
FrameLayout.LayoutParams lp = (FrameLayout.LayoutParams) mRootView.getLayoutParams();
lp.height = screenHeight;
// call request layout so the changes will take affect
mRootView.requestLayout();
// save the height difference so we will run this code only when a change occurs.
mLastHeightDifferece = heightDifference;
}
}
this is probably not bullet proof and maybe on some devices it will not work but it worked for me and hope it will help you too.
Add android:fitsSystemWindows="true" to the layout, and this layout will resize.
I implemented Joseph Johnson solution and it worked well, I noticed after using this solution sometimes the drawer on the application will not close properly.
I added a functionality to remove the listener removeOnGlobalLayoutListener when the user closes the fragment where are edittexts located.
//when the application uses full screen theme and the keyboard is shown the content not scrollable!
//with this util it will be scrollable once again
//http://stackoverflow.com/questions/7417123/android-how-to-adjust-layout-in-full-screen-mode-when-softkeyboard-is-visible
public class AndroidBug5497Workaround {
private static AndroidBug5497Workaround mInstance = null;
private View mChildOfContent;
private int usableHeightPrevious;
private FrameLayout.LayoutParams frameLayoutParams;
private ViewTreeObserver.OnGlobalLayoutListener _globalListener;
// For more information, see https://code.google.com/p/android/issues/detail?id=5497
// To use this class, simply invoke assistActivity() on an Activity that already has its content view set.
public static AndroidBug5497Workaround getInstance (Activity activity) {
if(mInstance==null)
{
synchronized (AndroidBug5497Workaround.class)
{
mInstance = new AndroidBug5497Workaround(activity);
}
}
return mInstance;
}
private AndroidBug5497Workaround(Activity activity) {
FrameLayout content = (FrameLayout) activity.findViewById(android.R.id.content);
mChildOfContent = content.getChildAt(0);
frameLayoutParams = (FrameLayout.LayoutParams) mChildOfContent.getLayoutParams();
_globalListener = new ViewTreeObserver.OnGlobalLayoutListener()
{
#Override
public void onGlobalLayout()
{
possiblyResizeChildOfContent();
}
};
}
public void setListener()
{
mChildOfContent.getViewTreeObserver().addOnGlobalLayoutListener(_globalListener);
}
public void removeListener()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
mChildOfContent.getViewTreeObserver().removeOnGlobalLayoutListener(_globalListener);
} else {
mChildOfContent.getViewTreeObserver().removeGlobalOnLayoutListener(_globalListener);
}
}
private void possiblyResizeChildOfContent() {
int usableHeightNow = computeUsableHeight();
if (usableHeightNow != usableHeightPrevious) {
int usableHeightSansKeyboard = mChildOfContent.getRootView().getHeight();
int heightDifference = usableHeightSansKeyboard - usableHeightNow;
if (heightDifference > (usableHeightSansKeyboard/4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
} else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
mChildOfContent.requestLayout();
usableHeightPrevious = usableHeightNow;
}
}
private int computeUsableHeight() {
Rect r = new Rect();
mChildOfContent.getWindowVisibleDisplayFrame(r);
return (r.bottom - r.top);
}
}
uses the class where is my edittexts located
#Override
public void onStart()
{
super.onStart();
AndroidBug5497Workaround.getInstance(getActivity()).setListener();
}
#Override
public void onStop()
{
super.onStop();
AndroidBug5497Workaround.getInstance(getActivity()).removeListener();
}
I'm currently using this approach and it works like a charm. The trick is we get keyboard height from different methods on 21 above and below and then use it as the bottom padding of our root view in our activity. I assumed your layout does not need a top padding (goes below status bar) but in case you do, inform me to update my answer.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout mainLayout = findViewById(R.id.main_layout);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
ViewCompat.setOnApplyWindowInsetsListener(mainLayout , new OnApplyWindowInsetsListener() {
#Override
public WindowInsetsCompat onApplyWindowInsets(View v, WindowInsetsCompat insets) {
v.setPadding(0, 0, 0, insets.getSystemWindowInsetBottom());
return insets;
}
});
} else {
View decorView = getWindow().getDecorView();
final View contentView = mainLayout;
decorView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Rect r = new Rect();
//r will be populated with the coordinates of your view that area still visible.
decorView.getWindowVisibleDisplayFrame(r);
//get screen height and calculate the difference with the useable area from the r
int height = decorView.getContext().getResources().getDisplayMetrics().heightPixels;
int diff = height - r.bottom;
//if it could be a keyboard add the padding to the view
if (diff != 0) {
// if the use-able screen height differs from the total screen height we assume that it shows a keyboard now
//check if the padding is 0 (if yes set the padding for the keyboard)
if (contentView.getPaddingBottom() != diff) {
//set the padding of the contentView for the keyboard
contentView.setPadding(0, 0, 0, diff);
}
} else {
//check if the padding is != 0 (if yes reset the padding)
if (contentView.getPaddingBottom() != 0) {
//reset the padding of the contentView
contentView.setPadding(0, 0, 0, 0);
}
}
}
});
}
}
...
}
Don't forget to address your root view with an id:
activity_main.xml
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/main_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
Hope it helps someone.
To get it to work with FullScreen:
Use the ionic keyboard plugin. This allows you to listen for when the keyboard appears and disappears.
OnDeviceReady add these event listeners:
// Allow Screen to Move Up when Keyboard is Present
window.addEventListener('native.keyboardshow', onKeyboardShow);
// Reset Screen after Keyboard hides
window.addEventListener('native.keyboardhide', onKeyboardHide);
The Logic:
function onKeyboardShow(e) {
// Get Focused Element
var thisElement = $(':focus');
// Get input size
var i = thisElement.height();
// Get Window Height
var h = $(window).height()
// Get Keyboard Height
var kH = e.keyboardHeight
// Get Focused Element Top Offset
var eH = thisElement.offset().top;
// Top of Input should still be visible (30 = Fixed Header)
var vS = h - kH;
i = i > vS ? (vS - 30) : i;
// Get Difference
var diff = (vS - eH - i);
if (diff < 0) {
var parent = $('.myOuter-xs.myOuter-md');
// Add Padding
var marginTop = parseInt(parent.css('marginTop')) + diff - 25;
parent.css('marginTop', marginTop + 'px');
}
}
function onKeyboardHide(e) {
// Remove All Style Attributes from Parent Div
$('.myOuter-xs.myOuter-md').removeAttr('style');
}
Basically if they difference is minus then that is the amount of pixels that the keyboard is covering of your input. So if you adjust your parent div by this that should counteract it.
Adding timeouts to the logic say 300ms should also optimise performance (as this will allow keyboard time to appear.
I tried Joseph Johnson's class, and it worked, but didn't quite meet my needs. Rather than emulating android:windowSoftInputMode="adjustResize", I needed to emulate android:windowSoftInputMode="adjustPan".
I am using this for a full screen webview. To pan the content view to the correct position, I need to use a javascript interface which provides details on the position of the page element which has focus and thus is receiving the keyboard input. I have omitted those details, but provided my rewrite of Joseph Johnson's class. It will provide a very solid base for you to implement a custom pan vs. his resize.
package some.package.name;
import some.package.name.JavaScriptObject;
import android.app.Activity;
import android.graphics.Rect;
import android.view.View;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout;
//-------------------------------------------------------
// ActivityPanner Class
//
// Convenience class to handle Activity attributes bug.
// Use this class instead of windowSoftInputMode="adjustPan".
//
// To implement, call enable() and pass a reference
// to an Activity which already has its content view set.
// Example:
// setContentView( R.layout.someview );
// ActivityPanner.enable( this );
//-------------------------------------------------------
//
// Notes:
//
// The standard method for handling screen panning
// when the virtual keyboard appears is to set an activity
// attribute in the manifest.
// Example:
// <activity
// ...
// android:windowSoftInputMode="adjustPan"
// ... >
// Unfortunately, this is ignored when using the fullscreen attribute:
// android:theme="#android:style/Theme.NoTitleBar.Fullscreen"
//
//-------------------------------------------------------
public class ActivityPanner {
private View contentView_;
private int priorVisibleHeight_;
public static void enable( Activity activity ) {
new ActivityPanner( activity );
}
private ActivityPanner( Activity activity ) {
FrameLayout content = (FrameLayout)
activity.findViewById( android.R.id.content );
contentView_ = content.getChildAt( 0 );
contentView_.getViewTreeObserver().addOnGlobalLayoutListener(
new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() { panAsNeeded(); }
});
}
private void panAsNeeded() {
// Get current visible height
int currentVisibleHeight = visibleHeight();
// Determine if visible height changed
if( currentVisibleHeight != priorVisibleHeight_ ) {
// Determine if keyboard visiblity changed
int screenHeight =
contentView_.getRootView().getHeight();
int coveredHeight =
screenHeight - currentVisibleHeight;
if( coveredHeight > (screenHeight/4) ) {
// Keyboard probably just became visible
// Get the current focus elements top & bottom
// using a ratio to convert the values
// to the native scale.
float ratio = (float) screenHeight / viewPortHeight();
int elTop = focusElementTop( ratio );
int elBottom = focusElementBottom( ratio );
// Determine the amount of the focus element covered
// by the keyboard
int elPixelsCovered = elBottom - currentVisibleHeight;
// If any amount is covered
if( elPixelsCovered > 0 ) {
// Pan by the amount of coverage
int panUpPixels = elPixelsCovered;
// Prevent panning so much the top of the element
// becomes hidden
panUpPixels = ( panUpPixels > elTop ?
elTop : panUpPixels );
// Prevent panning more than the keyboard height
// (which produces an empty gap in the screen)
panUpPixels = ( panUpPixels > coveredHeight ?
coveredHeight : panUpPixels );
// Pan up
contentView_.setY( -panUpPixels );
}
}
else {
// Keyboard probably just became hidden
// Reset pan
contentView_.setY( 0 );
}
// Save usabale height for the next comparison
priorVisibleHeight_ = currentVisibleHeight;
}
}
private int visibleHeight() {
Rect r = new Rect();
contentView_.getWindowVisibleDisplayFrame( r );
return r.bottom - r.top;
}
// Customize this as needed...
private int viewPortHeight() { return JavaScriptObject.viewPortHeight(); }
private int focusElementTop( final float ratio ) {
return (int) (ratio * JavaScriptObject.focusElementTop());
}
private int focusElementBottom( final float ratio ) {
return (int) (ratio * JavaScriptObject.focusElementBottom());
}
}
1) Create KeyboardHeightHelper:
public class KeyboardHeightHelper {
private final View decorView;
private int lastKeyboardHeight = -1;
public KeyboardHeightHelper(Activity activity, View activityRootView, OnKeyboardHeightChangeListener listener) {
this.decorView = activity.getWindow().getDecorView();
activityRootView.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
int keyboardHeight = getKeyboardHeight();
if (lastKeyboardHeight != keyboardHeight) {
lastKeyboardHeight = keyboardHeight;
listener.onKeyboardHeightChange(keyboardHeight);
}
});
}
private int getKeyboardHeight() {
Rect rect = new Rect();
decorView.getWindowVisibleDisplayFrame(rect);
return decorView.getHeight() - rect.bottom;
}
public interface OnKeyboardHeightChangeListener {
void onKeyboardHeightChange(int keyboardHeight);
}
}
2) Let your activity be full screen:
activity.getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN);
3) Listen for keyboard height changes and add bottom padding for your view:
View rootView = activity.findViewById(R.id.root); // your root view or any other you want to resize
KeyboardHeightHelper effectiveHeightHelper = new KeyboardHeightHelper(
activity,
rootView,
keyboardHeight -> rootView.setPadding(0, 0, 0, keyboardHeight));
So, each time keyboard will appear on the screen - bottom padding for your view will change, and content will be rearranged.
Indeed the soft keyboard appearance doesn't seem to affect the Activity in any way no matter what windowSoftInputMode I select in the FullScreen mode.
Though I couldn't find much documentation on this property, I think that the FullScreen mode was designed for gaming application which do not require much use of the soft keyboard. If yours is an Activity which requires user interaction through soft keyboard, please reconsider using a non-FullScreen theme. You could turn off the TitleBar using a NoTitleBar theme. Why would you want to hide the notification bar?
Just keep as android:windowSoftInputMode="adjustResize". Because it is given to keep only one out of "adjustResize" and "adjustPan"(The window adjustment mode is specified with either adjustResize or adjustPan. It is highly recommended that you always specify one or the other). You can find it out here:
http://developer.android.com/resources/articles/on-screen-inputs.html
It works perfectly for me.
only use android:windowSoftInputMode="adjustResize|stateHidden as you use AdjustPan then it disable the resizing property
I used Joseph Johnson created AndroidBug5497Workaround class but getting black space between softkeyboard and the view. I referred this link Greg Ennis. After doing some changes to the above this is my final working code.
public class SignUpActivity extends Activity {
private RelativeLayout rlRootView; // this is my root layout
private View rootView;
private ViewGroup contentContainer;
private ViewTreeObserver viewTreeObserver;
private ViewTreeObserver.OnGlobalLayoutListener listener;
private Rect contentAreaOfWindowBounds = new Rect();
private FrameLayout.LayoutParams rootViewLayout;
private int usableHeightPrevious = 0;
private View mDecorView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sign_up);
mDecorView = getWindow().getDecorView();
contentContainer =
(ViewGroup) this.findViewById(android.R.id.content);
listener = new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
possiblyResizeChildOfContent();
}
};
rootView = contentContainer.getChildAt(0);
rootViewLayout = (FrameLayout.LayoutParams)
rootView.getLayoutParams();
rlRootView = (RelativeLayout) findViewById(R.id.rlRootView);
rlRootView.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
int heightDiff = rlRootView.getRootView().getHeight() - rlRootView.getHeight();
if (heightDiff > Util.dpToPx(SignUpActivity.this, 200)) {
// if more than 200 dp, it's probably a keyboard...
// Logger.info("Soft Key Board ", "Key board is open");
} else {
Logger.info("Soft Key Board ", "Key board is CLOSED");
hideSystemUI();
}
}
});
}
// This snippet hides the system bars.
protected void hideSystemUI() {
// Set the IMMERSIVE flag.
// Set the content to appear under the system bars so that the
content
// doesn't resize when the system bars hide and show.
mDecorView.setSystemUiVisibility(
View.SYSTEM_UI_FLAG_LAYOUT_STABLE | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
}
#Override
protected void onPause() {
super.onPause();
if (viewTreeObserver.isAlive()) {
viewTreeObserver.removeOnGlobalLayoutListener(listener);
}
}
#Override
protected void onResume() {
super.onResume();
if (viewTreeObserver == null || !viewTreeObserver.isAlive()) {
viewTreeObserver = rootView.getViewTreeObserver();
}
viewTreeObserver.addOnGlobalLayoutListener(listener);
}
#Override
protected void onDestroy() {
super.onDestroy();
rootView = null;
contentContainer = null;
viewTreeObserver = null;
}
private void possiblyResizeChildOfContent() {
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
int usableHeightNow = contentAreaOfWindowBounds.height();
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow;
rootView.layout(contentAreaOfWindowBounds.left,
contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
rootView.requestLayout();
usableHeightPrevious = usableHeightNow;
} else {
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
}
}
based on https://stackoverflow.com/a/19494006/1815624 and desire to make it happen...
updated idea
combining answers from
https://stackoverflow.com/a/19494006/1815624
https://stackoverflow.com/a/10952394/1815624
Relevant code:
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
} else {
// keyboard probably just became hidden
if(usableHeightPrevious != 0) {
frameLayoutParams.height = usableHeightSansKeyboard;
activity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
activity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
}
Full Source at https://github.com/CrandellWS/AndroidBug5497Workaround/blob/master/AndroidBug5497Workaround.java
old idea
Create a static value of the containers height before opening the keyboard
Set the container height based on usableHeightSansKeyboard - heightDifference when the keyboard opens and set it back to the saved value when it closes
if (heightDifference > (usableHeightSansKeyboard / 4)) {
// keyboard probably just became visible
frameLayoutParams.height = usableHeightSansKeyboard - heightDifference;
int mStatusHeight = getStatusBarHeight();
frameLayoutParams.topMargin = mStatusHeight;
((MainActivity)activity).setMyMainHeight(usableHeightSansKeyboard - heightDifference);
if(BuildConfig.DEBUG){
Log.v("aBug5497", "keyboard probably just became visible");
}
} else {
// keyboard probably just became hidden
if(usableHeightPrevious != 0) {
frameLayoutParams.height = usableHeightSansKeyboard;
((MainActivity)activity).setMyMainHeight();
}
frameLayoutParams.topMargin = 0;
if(BuildConfig.DEBUG){
Log.v("aBug5497", "keyboard probably just became hidden");
}
}
Methods in MainActivity
public void setMyMainHeight(final int myMainHeight) {
runOnUiThread(new Runnable() {
#Override
public void run() {
ConstraintLayout.LayoutParams rLparams = (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
rLparams.height = myMainHeight;
myContainer.setLayoutParams(rLparams);
}
});
}
int mainHeight = 0;
public void setMyMainHeight() {
runOnUiThread(new Runnable() {
#Override
public void run() {
ConstraintLayout.LayoutParams rLparams = (ConstraintLayout.LayoutParams) myContainer.getLayoutParams();
rLparams.height = mainHeight;
myContainer.setLayoutParams(rLparams);
}
});
}
Example Container XML
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<android.support.constraint.ConstraintLayout
android:id="#+id/my_container"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintHeight_percent=".8">
similarly margins can be added if needed...
Another consideration is use padding an example of this can be found at:
https://github.com/mikepenz/MaterialDrawer/issues/95#issuecomment-80519589
private void resizeWindowOnKeyboardVisible() {
RelativeLayout rootLayout;
rootLayout = findViewById(R.id.rootLayout);
this.getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
ViewGroup.LayoutParams layoutParams = rootLayout.getLayoutParams();
int height ;
#Override
public void onGlobalLayout() {
Rect r = new Rect();
getWindow().getDecorView().getWindowVisibleDisplayFrame(r);
int screenHeight = rootLayout.getContext().getResources().getDisplayMetrics().heightPixels;
int heightDiff = screenHeight - r.bottom;
if (heightDiff > screenHeight*0.15)
{
height = screenHeight - heightDiff;
layoutParams.height=height;
rootLayout.setLayoutParams(layoutParams);
}else{
height=ViewGroup.LayoutParams.MATCH_PARENT;
if( height!=layoutParams.height) {
layoutParams.height = height;
rootLayout.setLayoutParams(layoutParams);
}
}
}
});
}
Using android:windowSoftInputMode="adjustResize|stateHidden might not work in all cases and also android:fitsSystemWindows="true doesn't help when you use SYSTEM_UI_FLAG_FULLSCREEN tags. To make view/window/webview adjustable when Keyboard visible do the following things.
Use RelativeLayout as root layout.
Declare the above method resizeWindowOnKeyboardVisible() in an activity & call it after setContentView() in onCreate() method.
It works in Android 11 (API 30) also.
Based on #Sdghasemi's solution, here's my Kotlin code, without the deprecated insets.getSystemWindowInsetBottom(). Also I added a padding animation to make the keyboard opening smoother.
val rootLayout = findViewById<RelativeLayout>(R.id.your_root_layout)
ViewCompat.setOnApplyWindowInsetsListener(rootLayout) { v, insets ->
val animator = ValueAnimator.ofInt(0, insets.getInsets(WindowInsetsCompat.Type.ime()).bottom))
animator.addUpdateListener {
valueAnimator -> v.setPadding(0, 0, 0, valueAnimator.animatedValue as? Int ?: 0)
}
animator.duration = 200
animator.start()
insets
}
Call it from the onCreate() method of your Activity.
In my case, this snippet works better than setting android:windowSoftInputMode="adjustPan" in the AndroidManifest.xml
You want the bottom bar to stick to the bottom of the view, but when the keyboard is displayed, they should move up to be placed above the keyboard, right?
You can try this code snippet:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
...>
<RelativeLayout
android:id="#+id/RelativeLayoutTopBar"
...>
</RelativeLayout>
<LinearLayout
android:id="#+id/LinearLayoutBottomBar"
android:layout_alignParentBottom = true
...>
</LinearLayout>
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="390dp"
android:orientation="vertical"
android:layout_above="#+id/LinearLayoutBottomBar"
android:layout_below="#+id/RelativeLayoutTopBar">
<ScrollView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp"
android:id="#+id/ScrollViewBackground">
...
</ScrollView>
</LinearLayout>
</RelativeLayout>
The BottomBar will stick to the bottom of the view and the LinearLayout containing the ScrollView will take what's left of the view after the top/bottom bar and the keyboard are displayed. Let me know if it works for you as well.
Thank you Joseph for your answer. However, in the method possiblyResizeChildOfContent(), the portion
else {
// keyboard probably just became hidden
frameLayoutParams.height = usableHeightSansKeyboard;
}
was not working for me, as the lower portion of view became hidden.
So I had to take a global variable restoreHeight, and in the constructor, I inserted the last line
restoreHeight = frameLayoutParams.height;
and then I replaced the former mentioned part with
else {
// keyboard probably just became hidden
frameLayoutParams.height = restoreHeight;
}
But I have no idea why your code didn't work for me. It would be of great help, if someone can shed light on this.
I was only using full screen mode to hide the status bar. However, I want the app to resize when keyboard is shown. All of the other solutions (likely due to age of post) were complicated or not possible for my use (want to avoid change Java code for sack of PhoneGap Build).
Instead of using Full screen, I modified my configure for Android to be non-fullscreen:
<preference name="fullscreen" value="false" />
And added the cordova-plugin-statusbar, via command line:
cordova plugin add cordova-plugin-statusbar
When app has loaded, I simple call a method on the plugin to hide itself, like:
if (window.cordova && window.cordova.platformId == 'android' && window.StatusBar)
window.StatusBar.hide();
This works like a charm. Only real downside is that the status bar is breifly visible while the app loads. For my needs, that wasn't an issue.
I have tried out all the possible answers from stackOverflow, finally i solved after a week Long search .
I have used the coordinate layout and i changed this with linearLayout and my problem is fixed. I dont know possibly the coordinate layout has bugs or anything my mistake.
I tried many solutions include Joseph Johnson's and Johan Stuyts's. But as a result I got a white space between content and keyboard on some devices (like Lenovo s820) in all cases.
So I made some changes to their codes and finally got working solution.
My idea based on adding margin to top of content when keyboard is showing.
contentContainer.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds);
int usableHeightNow = contentAreaOfWindowBounds.height();
if (usableHeightNow != usableHeightPrevious) {
int difference = usableHeightNow - usableHeightPrevious;
if (difference < 0 && difference < -150) {
keyboardShowed = true;
rootViewLayout.topMargin -= difference + 30;
rootViewLayout.bottomMargin += 30;
}
else if (difference < 0 && difference > -150){
rootViewLayout.topMargin -= difference + 30;
}
else if (difference > 0 && difference > 150) {
keyboardShowed = false;
rootViewLayout.topMargin = 0;
rootViewLayout.bottomMargin = 0;
}
rootView.requestLayout();
Log.e("Bug Workaround", "Difference: " + difference);
usableHeightPrevious = usableHeightNow;
}
As you can see, I add 30 px to difference because there is a small white space between top of the screen and content zone with margin. And I dont know whence it appears so I decided just make margins smaller and now it works exactly how I needed.
Today not working adjustResize on full screen issue is actual for android sdk.
From answers i found:
the solution - but solution has this showing on picture issue :
Than i found the solution and remove the one unnecessary action:
this.getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
So, see my fixed solution code on Kotlin:
class AndroidBug5497Workaround constructor(val activity: Activity) {
private val content = activity.findViewById<View>(android.R.id.content) as FrameLayout
private val mChildOfContent = content.getChildAt(0)
private var usableHeightPrevious: Int = 0
private val contentContainer = activity.findViewById(android.R.id.content) as ViewGroup
private val rootView = contentContainer.getChildAt(0)
private val rootViewLayout = rootView.layoutParams as FrameLayout.LayoutParams
private val listener = {
possiblyResizeChildOfContent()
}
fun addListener() {
mChildOfContent.apply {
viewTreeObserver.addOnGlobalLayoutListener(listener)
}
}
fun removeListener() {
mChildOfContent.apply {
viewTreeObserver.removeOnGlobalLayoutListener(listener)
}
}
private fun possiblyResizeChildOfContent() {
val contentAreaOfWindowBounds = Rect()
mChildOfContent.getWindowVisibleDisplayFrame(contentAreaOfWindowBounds)
val usableHeightNow = contentAreaOfWindowBounds.height()
if (usableHeightNow != usableHeightPrevious) {
rootViewLayout.height = usableHeightNow
rootView.layout(contentAreaOfWindowBounds.left,
contentAreaOfWindowBounds.top, contentAreaOfWindowBounds.right, contentAreaOfWindowBounds.bottom);
mChildOfContent.requestLayout()
usableHeightPrevious = usableHeightNow
}
}
}
My bug fixing implement code:
class LeaveDetailActivity : BaseActivity(){
private val keyBoardBugWorkaround by lazy {
AndroidBug5497Workaround(this)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onResume() {
keyBoardBugWorkaround.addListener()
super.onResume()
}
override fun onPause() {
keyBoardBugWorkaround.removeListener()
super.onPause()
}
}
There is another way, without creating own helper classes or functions that calculate the height of the screen. Instead use ViewCompat.setOnApplyWindowInsetsListener.
With the listener you can check if the keyboard is open and set the bottom padding based on the keyboard height.
// the root view of your webview, e.g FrameLayout or LinearLayout
rootView = view.findViewById(R.id.whatever);
ViewCompat.setOnApplyWindowInsetsListener(rootView, (webView, insets) -> {
// checks if keyboard is visible, the Type.ime() stands for Input Method
boolean isKeyboardVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
// get the keyboard height and use the height as bottom padding for your view
int bottomKeyboardPadding = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
if (isKeyboardVisible) { webView.setPadding(0, 0, 0, bottomKeyboardPadding); }
else { webView.setPadding(0, 0, 0, 0); }
return insets;
});
If you want to really support full screen with soft input:
private fun View.setStatusBarTransparent() {
this#MainActivity.apply {
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = ContextCompat.getColor(this, R.color.transparent)
this#setStatusBarTransparent.fitsSystemWindows = true
WindowCompat.setDecorFitsSystemWindows(window, false)
ViewCompat.setOnApplyWindowInsetsListener(this#setStatusBarTransparent) { root, windowInset ->
val inset = windowInset.getInsets(WindowInsetsCompat.Type.systemBars())
val inset2 = windowInset.getInsets(WindowInsetsCompat.Type.ime())
root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
leftMargin = inset.left
bottomMargin = maxOf(inset.bottom, inset2.bottom)
rightMargin = inset.right
}
WindowInsetsCompat.CONSUMED
}
}
}
Don't use:
getWindow().addFlags(WindowManager.LayoutParams.FLAG_FORCE_NOT_FULLSCREEN);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN);
because works bad.
Instead of that, use:
fun setFullScreen(fullScreen: Boolean) {
val decorView = getWindow().getDecorView()
val uiOptions : Int
if(fullScreen){
uiOptions = View.SYSTEM_UI_FLAG_FULLSCREEN // this hide statusBar
toolbar.visibility = View.GONE // if you use toolbar
tabs.visibility = View.GONE // if you use tabLayout
} else {
uiOptions = View.SYSTEM_UI_FLAG_VISIBLE // this show statusBar
toolbar.visibility = View.VISIBLE
tabs.visibility = View.VISIBLE
}
decorView.setSystemUiVisibility(uiOptions)
}
In my case, this issue started happening once I added Crosswalk to my Cordova application. My app is not used in fullscreen and android:windowSoftInputMode="adjustPan".
I already had the ionic keyboard plugin in the application, so detecting if the keyboard was up or down was easy thanks to it:
// Listen for events to when the keyboard is opened and closed
window.addEventListener("native.keyboardshow", keyboardUp, false);
window.addEventListener('native.keyboardhide', keyboardDown, false);
function keyboardUp()
{
$('html').addClass('keyboardUp');
}
function keyboardDown()
{
$('html').removeClass('keyboardUp');
}
I tried all of the fixes above but the simple line that ended up doing it for me was this bit of css:
&.keyboardUp {
overflow-y: scroll;
}
Hope this saves you the few days I spent on this. :)

How to show PopupWindow at special location?

I need to show PopupWindow under one Views shown on the screen.
How can I calculate coordinates of needed View and place PopupWindow under it? Code example are more than welcome. Thanks.
Locating an already displayed view is fairly easy - here's what I use in my code:
public static Rect locateView(View v)
{
int[] loc_int = new int[2];
if (v == null) return null;
try
{
v.getLocationOnScreen(loc_int);
} catch (NullPointerException npe)
{
//Happens when the view doesn't exist on screen anymore.
return null;
}
Rect location = new Rect();
location.left = loc_int[0];
location.top = loc_int[1];
location.right = location.left + v.getWidth();
location.bottom = location.top + v.getHeight();
return location;
}
You could then use code similar to what Ernesta suggested to stick the popup in the relevant location:
popup.showAtLocation(parent, Gravity.TOP|Gravity.LEFT, location.left, location.bottom);
This would show the popup directly under the original view - no guarantee that there would be enough room to display the view though.
you have getLeft() and getBottom() to get the exact position of the view in the layout. You also have getWidth() and getHeight() to know the exact space occupied by the view. If you want to position your popup window below a view.
You setLeft() and setTop() methods of the view to position the new popup Window.
To get size of the main application screen without stuff like title and notification bars, override the following method in the class generating the screen in question (sizes are measured in pixels):
#Override
protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
}
To get the bottom coordinate of the view under which you want to show the popup:
View upperView = ...
int coordinate = upperView.getBottom();
Now as long as height - coordinate is large enough for your popup view, you can simply place the popup like this:
PopupWindow popup = new PopupWindow();
Button button = new Button(this);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
popup.showAtLocation(parent, Gravity.CENTER, 0, coordinate);
}
});
Here, showAtLocation() takes the parent view as an argument together with gravity and location offsets.

Categories

Resources