Android EditText Keyboard making Fragment disappear - android

In one of the fragments in my app I have an edittext, and whenever I click on it the keyboard comes up and all the content in the fragment just disappears, including the edittext. I can still type on the keyboard, and after I press the back button, everything comes back and whatever I type is showing on the edittext, but how do I stop everything from disappearing?
Below is the code for the fragment and its layout.
Fragment:
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class PaymentsFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_payments, container, false);
}
}
Layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText
android:id="#+id/editText2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="Enter Something"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toLeftOf="#+id/button2"
android:layout_marginLeft="16dp"
android:layout_marginTop="16dp"
android:layout_marginRight="16dp" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintBaseline_toBaselineOf="#+id/editText2"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toRightOf="#+id/editText2"
android:layout_marginLeft="0dp"
android:layout_marginRight="16dp" />
</android.support.constraint.ConstraintLayout>

Reference : Developer documentation
android:windowSoftInputMode
The adjustment made to the activity's main window — whether it is
resized smaller to make room for the soft keyboard or whether its
contents pan to make the current focus visible when part of the window
is covered by the soft keyboard.
adjustResize
The activity's main window is always resized to make room for the soft
keyboard on screen.
adjustPan
The activity's main window is not resized to make room for the soft
keyboard. Rather, the contents of the window are automatically panned
so that the current focus is never obscured by the keyboard and users
can always see what they are typing. This is generally less desirable
than resizing, because the user may need to close the soft keyboard to
get at and interact with obscured parts of the window.
Use this way in manifest :
<activity android:windowSoftInputMode="adjustResize"> </activity>

I still reproduce it in the case of a "Multiline EditText in Scrollview in fragment in ConstraintLayout" even on Android 8. Using a SingleLine does not create any issue.
It is happening because the dynamic height of a EditText does not work well in this situation.
This solution was hackish, but it does work like a charm if you can afford to fix the number of lines:
Create a custom class LabelEditView extending EditText
Add an attribute
<declare-styleable name="LabelEditView">
<attr name="label_edit_line_count" format="integer" />
</declare-styleable>
In the constructor call:
setLineCount(attributes.getInt(R.styleable.LabelEditView_label_edit_line_count,
1));
Then, finally, the interesting piece of code:
/**
* Set the number of visible lines. Does not support to switch between modes or font
*/
public void setLineCount(int lineCount) {
if (lineCount <= 1) {
setSingleLine(true);
} else {
setSingleLine(false);
setImeOptions(EditorInfo.IME_ACTION_DONE);
setRawInputType(InputType.TYPE_CLASS_TEXT);
// Fix around EditText in Scrollview in fragment in ConstraintLayout
Paint.FontMetrics fm = getPaint().getFontMetrics();
int textHeight = (int) ((fm.bottom - fm.top) * (lineCount + 0.5));
setMaxHeight(textHeight);
setHeight(textHeight);
}
}

Put in AndroidManifest.xml (in your activity):
<activity android:name="(activity name)" android:windowSoftInputMode="adjustPan">

Related

Soft Keyboard pushed button up above it. How to fix it?

I have ConstraintLayout with ScorllView and Button below it (attached to bottom of the screen. When I am editing EditText input inside ScrollView. Then appearing keyboard is moving my ScrollView content up (desired behaviour, so I can scroll to the end of it) but it also pushing button up (undesired behaviour).
I think I can change windowAdjustMode, maybe I could detect keyboard showing and then hide this button? But this two solutions aren't perfect.
XML:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ScrollView
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="#id/submitButton"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_margin="0dp">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<EditText /> goes here
</android.support.constraint.ConstraintLayout>
</ScrollView>
<Button
android:id="#+id/submitButton"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_margin="0dp"
android:text="#string/wizard_singup_step_submit_button"
style="#style/FormSubmitButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent" />
</android.support.constraint.ConstraintLayout>
This might help, haven't tried it myself, try adding the below code to your activity tag inside your manifest
Edit - added stateHidden to achieve what you're looking for, the button will be at the bottom and the elements inside the scroll view can be scrolled.
android:windowSoftInputMode="adjustPan|stateHidden"
From Android Documentation -
adjustPan - The activity's main window is not resized to make room for the soft keyboard. Rather, the contents of the window are automatically panned so that the current focus is never obscured by the keyboard and users can always see what they are typing. This is generally less desirable than resizing, because the user may need to close the soft keyboard to get at and interact with obscured parts of the window.
Edit 2 - Code for calculating the height of the Keyboard
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);
}
});
Add that heightDifference by creating a View Programmatically and setting it's height.
Edit 3 -
Use this to hide the keyboard
public static void hideKeyboardFrom(Context context, View view) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Activity.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(view.getWindowToken(), 0);
}
Let me know if this works.

Using adjustResize with fragments and requestFocus

I'm trying to create an activity with a single RelativeLayout used as a fragment container, where each fragment has a separate background color that bleeds under a translucent status bar, and where the activity as a whole accounts for the keyboard size.
The layout for the activity is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
The fragments are wrapped with:
<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:animateLayoutChanges="true" />
And the activity is defined as:
<activity android:screenOrientation="portrait"
android:name=".login.LoginActivity"
android:windowSoftInputMode="adjustResize" />
Everything is working fine for the first fragment. The activity resizes the RelativeLayout, the fragment contained within it resizes to adjust for the keyboard, and everything is fine. However, when replacing one fragment with another that uses <requestFocus /> on its <EditText />, the keyboard stays up but the second fragment that is pushed on takes the full screen size (under the keyboard). Closing and re-opening the keyboard causes the activity to re-measure and then everything is fine. Similarly, if the keyboard changes height during the fragment transition (when moving from a field that doesn't support autocorrection to one that does, so the keyboard gets taller), the fragment measures correctly. So it only appears to be a problem when transitioning between two fragments where the keyboard size is constant.
Adding fitsSytemWindows=true to the wrapping RelativeLayout fixes the problem, but also prevents the fragment's view from appearing under the status bar, which isn't what I want. Is there some way to force the fragment to re-measure or something that I'm missing? This is using native fragments, not app-compat.

windowSoftInputMode="adjustResize" causes problems with custom keyboard layout

I'm developing a custom keyboard and would like to add a TextView above the keyboard to show what the user already has typed or suggestions for words he could want to type.
To do that I have the following layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<android.inputmethodservice.KeyboardView
android:id="#+id/keyboard"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:keyPreviewLayout="#layout/preview"
android:keyBackground="#drawable/key_background"
android:background="#color/color_primary"
/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_above="#id/keyboard"
android:padding="8dp"
android:gravity="center"
android:background="#color/color_primary_dark"
android:textColor="#android:color/white"
android:text="some sample text"
/>
</RelativeLayout>
and the following code at the InputMethodService:
public class FancyInputMethodService extends InputMethodService {
#Override
public View onCreateInputView() {
final RelativeLayout layout = (RelativeLayout) getLayoutInflater().inflate(R.layout.keyboard_layout, null);
final KeyboardView keyboardView = (KeyboardView) layout.findViewById(R.id.keyboard);
final Keyboard keyboard = new Keyboard(this, R.xml.qwerty);
keyboardView.setKeyboard(keyboard);
return layout;
}
}
At a normal EditText at the top of the screen the keyboard looks fine and works well:
But if the EditText is in an Activity which uses the flag android:windowSoftInputMode="adjustResize" in the manifest, the keyboard view seems to cover the actual view instead of being transparent.
The left image shows the actual view with the soft keyboard closed, the image in the middle shows the weird behavior when the keyboard is open and the image on the right shows the default keyboard with the behavior I would expect.
I already tried to set the layouts background to transparent, but that didn't help.
The problem appears at several apps, e.g. WhatsApp, Hangouts, Facebook etc...Am I missing something or what's wrong?
tl;dr
You are not using InputMethodService as intended, use the CandidateView framework instead.
Full answer:
Your keyboard layout should not include the text suggestions.
Override the InputMethodService#onCreateCandidatesView. It should look like this:
public View onCreateCandidatesView() {
mYourView = getLayoutInflater().inflate(R.layout.your_view, null);
return mYourView;
}
3. When you want to show/hide your candidate view use setCandidatesViewShown(boolean show)

Android intent for software

Thata code insted
keyboardview or one of its extended class. You have not shared your RelativeLayout but i think The exception may be because of some problem with your RelativeLayout.
You can put a button to the right of Keyboard like this(res/layout/input.xml):
< com.android.example.LatinKeyboardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/keyboard"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_height="wrap_content"
android:keyBackground="#drawable/kb_exagon_base"
You can use any View/ViewGroup as a keyboard layout, Infact you can even set the keyboard layout to a view which is not an instance of keyboardview or one of its extended class. You have not shared your RelativeLayout but i think The exception may be because of some problem with your RelativeLayout.
You can put a button to the right of Keyboard like this(res/layout/input.xml):
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/keyboardLayout"
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
>
<android.inputmethodservice.KeyboardView
android:id="#+id/keyboardView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<ImageView
android:id="#+id/imageView"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:background="#drawable/ic_launcher"/>
And then return an instance of this layout from OnCreateInputView.
Update:
The statement mInputView = (KeyboardView) getLayoutInflater().inflate( R.layout.input, null); in your below comment will throw ClassCastException as the inflated view is not an instance of KeyboardView.
you need to change it like this:
public View onCreateInputView() {
mInputView = (View)getLayoutInflater().inflate( R.layout.input, null);
mKeyboardView = (KeyboardView)mInputView.findViewById(R.id.keyboardView);
mKeyboardView.setOnKeyboardActionListener(this);
mKeyboardView.setKeyboard(mQwertyKeyboard);
return mInputView;
}
the keyboard pops up from the bottom and you can define the behavior in your manifest using the following line android:windowSoftInputMode:
<activity
android:name=".NAME"
android:label="#string/appName"
android:launchMode="singleTop"
android:screenOrientation="portrait"
android:theme="#style/Theme.HOLOorAnyThemeYouWant"
android:windowSoftInputMode="stateAlwaysHidden" >
from documentation :
android:windowSoftInputMode
How the main window of the activity interacts with the window containing the on-screen soft keyboard. The setting for this attribute affects two things:
The state of the soft keyboard — whether it is hidden or visible — when the activity becomes the focus of user attention.
The adjustment made to the activity's main window — whether it is resized smaller to make room for the soft keyboard or whether its contents pan to make the current focus visible when part of the window is covered by the soft keyboard.

android:windowSoftInputMode="adjustResize" doesn't make any difference?

I have a faux dialog which uses this layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/containerPageConatiner">
<FrameLayout android:id="#+id/dialogHolder"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="15dp"
android:layout_gravity="center"
android:background="#drawable/panel_picture_frame_bg_focus_blue"/>
</FrameLayout>
I place a fragment inside the <FrameLayout> depending on the dialog which is opening - The activity controlling the Dialog looks like this:
<activity
android:label="#string/app_name"
android:name=".activity.DialogActivity"
android:theme="#style/CustomTheme.Screen.Transparent"
android:windowSoftInputMode="adjustResize">
Unfortunately when you click on an edit text inside of the dialog, no resizing takes place. The windowSoftInputMode literally makes no difference as the functionality is the same as pan mode.
Documentation says "This of course only works for applications that have a resizeable area that can be reduced to make enough space" but doesn't tell you what it means by "a resizeable area" and makes me think that in some way I don't have a resizeable area?
If anyone knows what's up can they help me out?
EDIT
Surrounding the dialog like so doesn't change anything:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/containerPageConatiner"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
<FrameLayout
android:id="#+id/dialogHolder"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:padding="15dp"
android:layout_gravity="center"
android:background="#drawable/panel_picture_frame_bg_focus_blue"/>
<View
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
EDIT2
Scrollview as parent doesn't help either:
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/containerPageConatiner"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/dialogHolder"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:padding="15dp" />
</ScrollView>
I created a new project in order to try and get the basic features working for window resizing and the slowly moved it towards the target peice of my project. Doing this I tracked the problem down to this:
In my theme hierarchy I had this property:
<item name="android:windowFullscreen">true</item>
which was burried at the level of Theme.Black.NoTitleBar.FullScreen - An ancestor of my custom theme.
The documentation suggests that this is a "Flag indicating whether this window should fill the entire screen". That sounds like a good thing to have if you have an app which takes up the whole screen... Except it still takes up the whole screen without the flag.
In fact, once you've taken this out, there is absolutely no change in the app at all... apart from adjustResize now works perfectly.
A while back i also had the same issue in a library i've created. (MaterialDrawer)
As far as i can see all the provided answers don't solve the main issue, they just point to remove the fullscreen flag (android:windowFullscreen), which is no solution for many out there.
The above mentioned "issue" only appears in Android versions starting with API Level 19 (KITKAT), because they changed the behavior. To be correct it is no issue, it is working as intended. See the comment by a Android employee (Android-Issue).
So i started digging around the Android source and came to the following solution by using the ViewTreeObserver.OnGlobalLayoutListener and reacting if the Keyboard gets shown / or hidden. If the Keyboard gets shown i add the padding to the bottom of the container view which will then emulate the same as the adjustResize would do.
Solution
To simplify the usage i've wrapped the whole thing in a simple KeyboardUtil helper class.
/**
* Created by mikepenz on 14.03.15.
* This class implements a hack to change the layout padding on bottom if the keyboard is shown
* to allow long lists with editTextViews
* Basic idea for this solution found here: http://stackoverflow.com/a/9108219/325479
*/
public class KeyboardUtil {
private View decorView;
private View contentView;
public KeyboardUtil(Activity act, View contentView) {
this.decorView = act.getWindow().getDecorView();
this.contentView = contentView;
//only required on newer android versions. it was working on API level 19 (Build.VERSION_CODES.KITKAT)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
public void enable() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
decorView.getViewTreeObserver().addOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
public void disable() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
decorView.getViewTreeObserver().removeOnGlobalLayoutListener(onGlobalLayoutListener);
}
}
//a small helper to allow showing the editText focus
ViewTreeObserver.OnGlobalLayoutListener onGlobalLayoutListener = 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);
}
}
}
};
/**
* Helper to hide the keyboard
*
* #param act
*/
public static void hideKeyboard(Activity act) {
if (act != null && act.getCurrentFocus() != null) {
InputMethodManager inputMethodManager = (InputMethodManager) act.getSystemService(Activity.INPUT_METHOD_SERVICE);
inputMethodManager.hideSoftInputFromWindow(act.getCurrentFocus().getWindowToken(), 0);
}
}
}
You can then use it in your activity or fragment by doing the following:
//initialize the KeyboardUtil (you can do this global)
KeyboardUtil keyboardUtil = new KeyboardUtil(activity, getContent().getChildAt(0));
//enable it
keyboardUtil.enable();
//disable it
keyboardUtil.disable();
The whole util class is used in the above mentioned library MaterialDrawer and can be found here KeyboardUtil. This will always contain the latest version. (if there are improvements)
It seems that the problem is with FrameLayout, as it behaves that way, that each child occupying visible space of that frame, therefore no need to resize to fit children.
Try to use RelativeLayout. It should work.
Without using a ScrollView as my parent I just added android:fitsSystemWindows="true" to my parent view (which was a RelativeLayout) and adjustResize to the Manifest for the activity and it worked.
Try to put your LinearLayout on a ScrollView, that worked for me once..
I've had to set
<item name="android:windowFullscreen">false</item>
Despite, I've never set it to true and app actually wasn't full screen.
As original poster discovered when the Fullscreen Flag is assigned to an activity the android:windowFullscreen attribute will not work and so your window will not resize when the soft keyboard is visible and it won't be scrollable.
Simply removing the Fullscreen flag and not using a Fullscreen theme will allow scrolling when the soft keyboard is visible.
I don't know why, but if you have <item name="android:windowTranslucentNavigation">true</item> in your theme, change it to false. And it will start working. Really strange.
Ensure you set windowTranslucentStatus to false in your styles.

Categories

Resources