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.
Related
It seems like the official way, to listen to keyboard height change, is to use WindowInsetsCompat based on https://developer.android.com/develop/ui/views/layout/sw-keyboard
( An unofficial is to have an invisible PopupWindow to monitor keyboard height change. But, this is not a reliable method due to today numerous devices with different notch, split screen mode, ... - Is there any way in Android to get the height of virtual keyboard of device )
We try to experiment, to see how we can monitor keyboard height correctly.
Before applying WindowInsetsCompat
After applying WindowInsetsCompat with the following code.
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View keyboardView = findViewById(R.id.keyboard_view);
ViewCompat.setOnApplyWindowInsetsListener(getWindow().getDecorView().getRootView(), (v, insets) -> {
boolean imeVisible = insets.isVisible(WindowInsetsCompat.Type.ime());
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom;
android.util.Log.i("CHEOK", "imeVisible = " + imeVisible + ", imeHeight = " + imeHeight);
ViewGroup.LayoutParams params = keyboardView.getLayoutParams();
params.height = imeHeight;
keyboardView.setLayoutParams(params);
return insets;
});
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<EditText
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top" />
<LinearLayout
android:id="#+id/bottom_linear_layout"
android:layout_width="match_parent"
android:layout_height="48dp"
android:orientation="horizontal"
android:background="#22000000">
<ImageButton
android:id="#+id/image_button_0"
android:layout_width="48dp"
android:layout_height="48dp"
android:background="?attr/actionBarItemBackground"
android:src="#drawable/ic_baseline_alarm_on_24" />
</LinearLayout>
<View
android:id="#+id/keyboard_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:background="#ff0000" />
</LinearLayout>
Outcome of WindowInsetsCompat (When keyboard is not visible)
Outcome of WindowInsetsCompat (When keyboard is visible)
Here's the observation
For testing purpose, we do not use android:windowSoftInputMode="adjustResize".
After applying WindowInsetsCompat, the top status bar and bottom soft key background become white!
The returned keyboard's height isn't correct. (If the keyboard height is correct, we shouldn't see the red color keyboardView, because we have set the height of keyboardView to be same as keyboard's height)
May I know, when using WindowInsetsCompat to monitor keyboard's height, how can I
Avoid status bar and bottom soft key background from becoming white?
Get the correct keyboard's height?
The following is the workable demo - https://github.com/yccheok/wediary-sandbox/tree/master/keyboard-bottom-sheet-integration
Thanks.
Avoid status bar and bottom soft key background from becoming white?
This requires to readjust the insets to WindowInsetsCompatby returning ViewCompat.onApplyWindowInsets(v, insets) instead of insets from the setOnApplyWindowInsetsListener() callback.
Get the correct keyboard's height?
The insets are calculated based on the far ends of the screen (it makes sense as they're named as window insets, not the activity insets).
So, in case of the ime/keyboard we are interested in the bottom inset which equals to the top edge of the keyboard to the bottom edge of the screen; this includes the system navigation insets (i.e., the height of the navigation bar).
The red view is shown because it's drawn on top of the navigation bar; it doesn't take the navBar height into consideration.
So, to get the exact height of the keyboard, we need to subtract the navBarHeight from the imeHeight:
int imeHeight = insets.getInsets(WindowInsetsCompat.Type.ime()).bottom
- insets.getInsets(WindowInsetsCompat.Type.systemBars()).bottom;
Please notice the quote of the documentation about ime insets:
When running on devices with API Level 29 and before, the returned
insets are an approximation based on the information available. This
is especially true for the IME type, which currently only works when
running on devices with SDK level 23 and above.
In my application I have the following layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
<EditText
android:id="#+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="#color/text"
android:gravity="center_horizontal"
android:hint="#string/input_hint"
android:inputType="textVisiblePassword"
android:padding="#dimen/margin"
android:singleLine="true"
android:textColor="#color/input_text"
android:textColorHint="#color/input_hint">
</EditText>
</LinearLayout>
When EditText is clicked, the keyboard pushes the screen up including the ActionBar so that EditText is just above the keyboard. What I want is for only EditText to come up sitting on the keyboard, but the FragmentContainer that is above remains intact "as background". I got some of this by using android: windowSoftInputMode = "adjustResize" in the AndroidManifest.xml file, but my application is fullscreen and this tag apparently does not work with fullscreen.
As far as I am aware, there's no way to achieve what you're describing.
These are the choices available to you for windowSoftInputMode:
adjustNothing: When the keyboard appears, the window is not adjusted at all. This will stop your fragment_container from being moved or resizing, but will also cause the keyboard to cover your text input field.
adjustResize: When the keyboard appears, the window is "shrunk" vertically. This will cause your fragment_container to occupy less space than when the keyboard is closed, potentially affecting your fragment layout.
adjustPan: When the keyboard appears, the screen is "pushed" upwards. This will cause your fragment_container's top portion to be clipped by the edge of the screen.
adjustUnspecified: Allow the system to choose between the above three options.
Below are some pictures that (hopefully) help illustrate these attributes.
The layout with keyboard closed
From left to right: adjustNothing, adjustResize, and adjustPan
I have a grid view and want to show a keyboard when the user clicks on a cell. The input of the keyboard will then appear in the cell. I've tried the code below which I got from Android: show soft keyboard automatically when focus is on an EditText and other similar questions. I can't get the keyboard to show up, though.
Any ideas on this?
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(AdapterView<?> parent, View v,int position, long id) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.showSoftInput(gridView, InputMethodManager.SHOW_IMPLICIT);
}
});
I have tried InputMethodManager.SHOW_FORCED but that didn't help either.
Thanks
EDIT
The cell layout for the grid is below. I changed it to an EditText but still no keyboard. Having it as EditText doesn't look too good for me from a UI point of view. Ideally, I was the TextView and then perhaps when the user clicks it becomes an EditText so the user can enter something.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp" >
<EditText
android:id="#+id/grid_item_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/celllabel"
android:textSize="15px" >
</EditText>
My idea is that it stays as a TextView. The keyboard opens and when the user clicks one letter the keyboard closes and that a letter appears in the GridCell. Is my idea possible?
Thanks
One suggestion:
Create a custom EditText, you can hide the cursor if you want (make it looks like a TextView).
Optimize your cell layout by removing the LinearLayout
Code:
<?xml version="1.0" encoding="utf-8"?>
<YourCustomEditText xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/grid_item_label"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="#+id/celllabel"
android:maxLength="1"
android:textSize="15px" >
</YourCustomEditText>
Use TextWatcher or focus interface to control the show/hide of the keyboard.
Hope this help!
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)
I'm having some trouble with detecting screen clicks on the GUI. Works in portrait but fails in landscape, see below.
I have a GUI (Fragment) which contains some instructions + images. The user is required to tap anywhere on the screen to proceed. In order capture the click/tap event, I have put in a View(topview) that fill the entire screen and sits onto of other elements, I then listen for clicks on this view and it works fine.
The problem is when in landscape mode, the text and images take up to much room. So the whole thing is now wrapped in a ScrollView. This is where the problem begins. When the ScrollView is active, (i.e. you can scroll/scroll bars are visible), my view on top (topview) disappears. It seems that when in landscape mode the height of content in a ScrollView is being changed. As an experiment I replaced the View with a Button and the Button goes from filling the screen in portrait to being normal height in landscape mode when the ScrollView is usable.
Is there a way of me detecting the user tapping on the screen, which works with the ScrollView control as the top element. I've tried rearranging the GUI in several ways but without success, and I've tried adding onClick event handlers to the ScrollView, also without success.
My Layout is below, note my top view is semi-transparent red, so I could see the area it covered.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:clickable="true" >
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical" >
<TextView
android:id="#+id/txtInstructions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:padding="10dp"
android:text="#string/instructions"
android:textColor="#color/blue"
android:textSize="20sp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:adjustViewBounds="true"
android:maxWidth="250dp"
android:padding="20dp"
android:src="#drawable/main_camera" />
</LinearLayout>
<View
android:id="#+id/view_to_listen_for_touch"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#88FF0000"
android:clickable="true" />
</RelativeLayout>
One thing that works(although looks like more like a hack(pretty ugly)) is to programatically add the special View in code(in the onCreate method) and set its dimensions based on the parent RelativeLayout's exact dimensions. Here is a snippet of code:
//...
final RelativeLayout parent = (RelativeLayout) findViewById(R.id.ff);
final View layer = new View(this);
layer.setBackgroundColor(Color.parseColor("#88FF0000"));
// the ScrollView really doesn't like this View ,using this without the
// runnable will not work
layer.setLayoutParams(new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.MATCH_PARENT));
layer.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getApplicationContext(), "SDFD",
Toast.LENGTH_SHORT).show();
}
});
parent.addView(layer);
// this is required because if we use directly the getWidth/getHeight we
// will get 0/0
layer.post(new Runnable() {
#Override
public void run() {
layer.setLayoutParams(new RelativeLayout.LayoutParams(parent
.getWidth(), parent.getHeight()));
}
});
//...