Android adjust view when keyboard appears - android

I want to create a dynamic login screen that includes a decorative branding image when the screen is sufficiently tall but excludes the image on short screens. When the keyboard appears, it's likely the image will need to be removed. When the keyboard is hidden, the image can come back.
With a web page, I'd just use CSS media queries for the device height, show or hide the image appropriately, and it would all work nicely. I don't think anything that simple and clean is possible for an Android view, is it? So I figure I need to know the height of the window when the activity is created and create the view appropriately.
In the manifest, I've set my main activity to adjustResize when the keyboard appears. When the keyboard appears, my view does resize but my activity is surprisingly not recreated. When the screen is rotated, the activity is recreated.
The documentation says the view will be recreated when the keyboard availability changes. The first paragraph from https://developer.android.com/guide/topics/resources/runtime-changes
Some device configurations can change during runtime (such as screen orientation, keyboard availability, and when the user enables multi-window mode). When such a change occurs, Android restarts the running Activity ( onDestroy() is called, followed by onCreate()). The restart behavior is designed to help your application adapt to new configurations by automatically reloading your application with alternative resources that match the new device configuration.
My questions are
What's the best way to handle my design goal?
Why is my activity not recreated when the keyboard appears?
Below are the relevant parts of my test app. There is no image in this as I didn't even get that far before running into what seems like behavior contradicting the documentation.
AndroidManifest.xml
<activity
android:name=".MainActivity"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Log.d("MainActivity", "onCreate")
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}
activity_main.xml
<?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:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/intro"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:text="Top"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:background="#color/colorAccent" />
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#id/intro"
android:singleLine="true" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:text="Bottom"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:background="#color/colorAccent" />
</android.support.constraint.ConstraintLayout>

The activity isn't recreated when the keyboard appears because that isn't how Android works. Its not supposed to be. (Although if you have an old fashioned device with a slide out physical keyboard it will be recreated when you slide it out, because its treated as a hardware configuration change). Keyboards being shown/hidden is done without recreation. Which is a good thing, that many recreate events would be expensive given how many people just shove a ton of logic into onCreate.
How to do what you want- you can't. There is no API to detect when the keyboard is opened. There are commonly used hacks that attempt to discover it, but they're all flawed (they can have problems with split screen mode, picture in picture mode, multiple screens, and keyboards which are too small, because they all work based on guessing based on height changes).

The method OnFocusChangeListener() will detect whenever a View gains or loses focus. EditText makes the keyboard show up whenever it gains focus. Therefore, you should attach a OnFocusChangeListener() to those EditText that you want:
EditText myET = (EditText)findViewById(R.id.myET);
myET.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if(hasFocus) {
//Hide image
} else {
//Reveal image
}
}
});
Moreover, to hide and reveal the image you should use the property called visibility.
The methods are:
myImageView.setVisibility(View.VISIBLE); //This will show the Image
myImageView.setVisibility(View.INVISIBLE); //This will make the Image invisible
myImageView.setVisibility(View.GONE); //This will make the Image invisible, and also it will collapse the layout, occupying no space at all.
Don't try using INVISIBLE and GONE simultaneously, since that could cause some trouble. In your case, from what I understand you might want to use GONE and VISIBLE.
The only problem with this approach is that if you have multiple EditText, you would have to set up many times the same code. To solve that, please refer to the following link:
EditText setOnFocusChangeListener on all EditTexts

I've been searching for an answer and I came across this:
https://developer.android.com/training/keyboard-input/visibility#java
It says android DOES do what you want.
I just tried it, add 1 line, ONE LINE!, to your manifest and it works.
Oh wait... you want the brand to disappear... hmmm well with this it wouldn't have to?
DOH! You already knew about that.....

"What you want to do you can't"...
I don't know how to do it, but I disagree that it can't be done. I have the PayPal app and when the keyboard appears the login button and the rest of the login screen resize so that the login button isn't covered by the keyboard. How they're doing it, I don't know, but obviously somehow the app knows the keyboard appears and adjusts accordingly.
I've been searching for an answer and I came across this:
https://developer.android.com/training/keyboard-input/visibility#java
It says android DOES resize the screen when then soft keyboard appears/disappears.
I just tried it, add 1 line, ONE LINE!, to your manifest and it works.

Related

Why doesn't my layout move up enough with "adjustPan"?

I had some issues using adjustPan in my app, as it is not working as it is supposed to do. Some other users also experienced this problem, so I think it's important to share this issue (Layout doesn't move up enough when clicking EditText).
Note: I am using Android Studio and I'm using an "Instant App", so the location of the files are going to be a bit different.
When I use adjustPan in my layout, my activity does not move up enough to the point where the EditText appears above the Soft Keyboard (See Image For Details). The same result still appears no matter if you use it in a Manifest file or a Java file.
Not only that, but the weirdest part is that Status Bar becomes transparent and the dark blue bar which is supposed to go behind the notification icons goes down a little bit (See Image For Details).
I have tried making several attempts of solving this issue, like using adjustResize and setOnTouchListener but both of these answers that I received didn't solve this issue.
I still can't figure out the cause of this issue, so any answers or comments regarding how to discover why this issue is happening is also fine.
Here is some of my code:
Manifest File:
<activity
android:name=".Input_Activity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" />
Java File:
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.WindowManager;
public class Input_Activity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_input__activity);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
}
}
XML File:
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".Input_Activity">
<EditText
android:layout_width="214dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:inputType="number|numberDecimal"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.585"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.725" />
All feedback would be appreciated.
create an object like InputMethodManager methodManager at class level.
add these lines to your onCreate() method,
methodManager = (InputMethodManager) this.getSystemService(this.INPUT_METHOD_SERVICE);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE);
and in your onClick() method, show keyboard like,
methodManager.toggleSoftInputFromWindow(
your_view_name.getApplicationWindowToken(),
InputMethodManager.SHOW_FORCED, 0);
If this word getApplicationWindowToken throws an error then replace it with getWindowToken
hope it helps.
android:windowSoftInputMode
Above attribute can be used to specify what happens with activity contents.
Below image can clear your doubts more
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.
adjustResize: The activity’s main window is always resized to make
room for the soft keyboard on screen.
I think there is no need add below code which you have added.
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
Also need to add following code in your onTouch event so that when soft keyboard appears it will go up. Example:
editText.setTransformationMethod(WordBreakUtil.getInstance());
editText.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE | WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
return false;
}
});
I'm not sure about the answer. but try replacing
<activity
android:name=".Input_Activity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustPan" />
with
<activity
android:name=".Input_Activity"
android:screenOrientation="portrait"
android:windowSoftInputMode="adjustResize" />
in your android manifest.
Read here
Hope this helps!

Stop EditText from bringing up the keyboard on Activity start?

I have an Edit Text in one of my App layouts, and I want this EditText to only open the keyboard (I believe this is called being focused on?) when it is actually touched.
As of now, the keyboard opens with the EditText whenever the app opens, which isn't what I want.
I have tried many different XML tags to fix this:
android:focusable="false" <--- Prevents keyboard from opening at all.
android:focusable="true"
android:focusableInTouchMode = "true" <--- These tags give me the same result as no tags (keybaord will open on activity start)
android:focusedByDefault = "true" <--- Only available in API >= 23
What I am asking is, why is it so hard to disable default focus on an EditText? Surely I am missing an easy way to do this.
EDIT: Adding this line to my AndroidManifest fixed the issue:
android:windowSoftInputMode="stateHidden"
However, I don't like this solution. It seems like since this is in the Manifest, it will affect more UI elements than the single EditText I need to change.
Alternatively you can set the focus to the root layout element:
android:focusable="true"
android:focusableInTouchMode="true"
Example:
<?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"
android:focusable="true"
android:focusableInTouchMode="true">
<EditText
android:inputType="text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Use android:windowSoftInputMode="stateHidden"
If you dig deep into the Theme you are using for your Activity, you will find that the default value of windowSoftInputMode is stateUnspecified|adjustPan. And from the documentation:
stateUnspecified: Not specified, use what the system thinks is best. This is the default.
So depending on the the android device you run, your results will vary. I tried reproducing your case in API-26 emulator and the keyboard doesn't show up.You can use stateHidden to ensure that when an activity starts, the soft keyboard doesn't show up when the EditText gets focused on itself.
The other way to solve this is to requestFocus to some other element in the UI, making sure the EditText is not the first UI element to get focused. In my experience this is kind of a hack and it messes up the accessibility. The safest and clean way to accomplish is actually to use stateHidden.
stateHidden: Make the soft input area hidden when normally appropriate (when the user is navigating forward to your window).
Note that this will not affect any other UI elements. You can use adjustPan also to this, based on the screen background.

android adjustresize adjustpan famous issue

I'm not looking for code, either I won't post any, just an explanation, because I'm kind of lost.
There is this main issue about the resizing when softkeyboard appear.
In my case
I have a listView feeded with 2 editText and many textView with database content using a custom cursorAdapter.
1) AdjustPan
It's pretty simple. When I use the adjustPan property, everything works quite good, except the fact that when I press an editText in my listView and if the listview is bigger than the screensize, I can't scroll. This is actually the normal behaviour and I can understand it.
2) AdjustResize
Here I can scroll as much as I wish.
This property is the one I want to use. But I'm facing 2 issues :
When I press on one of the two editText, I just can't write in. Impossible, even thought it has the focus. I'm forcing the softkeyboard to appear, I try to type some letters in (remember that this editText is focused) but nothing happens.
Again, when I press one of the two editText, it just reorganize (apparently randomly) listview's items. Even thought it's working perfectly with adjustPan, with adjustResize, it's messing with items of the listView.
Any information about one of the 2 issues would be helpful. You can even ask for code, but one more time, I'm just looking for a general explanation that could help. Thanks.
Here this mention issue is same facing me in my app and here some change in my code is this working fine in my app. please try this...
<activity android:name="com.MainActivity"
android:configChanges="orientation|screenSize"/>
<ListView
android:id="#+id/fields_list_view"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:descendantFocusability="afterDescendants"
android:focusableInTouchMode="true"
android:focusable="true"
/>
<EditText
android:id="#+id/input_edit_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:cursorVisible="true"
android:gravity="top"
android:inputType="none"
android:textColor="#color/black"/>

setText, resets and locks when entering landscape

Hey all thanks for looking this through.
I really can't understand why this happends, though it might have something to do with my background thread which is processing audioinput.
All works fine until i tilt the phone, havn't yet put an landscape portrait or stuff like that in. So it should automatic just stretch my portrait layout.
But it does just stretch it, it also resets my label, and makes me unable to use my setText features on them. I also noticed that it pauses my background thread.
Here is code to set the textViews:
private void reactOnAudioMessage(Message msg) {
TextView txtVwLastValue = (TextView) findViewById(R.id.txtVwLastValue);
String result=null;
DataFile newData=null;
Bundle bndl=msg.getData();
// ** newData getting created, with the correcy value etc this is not the problem.
if(newData!=null){
txtVwLastValue.setText("Last Value Received:" +newData.getValue());
mDataList.add(newData);
drawGraph(mImageView);
}
}
Here is my xml file
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="#+id/layout"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
>
<Button
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Record"
android:id="#+id/btnRecord">
</Button>
<TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="Last Value Received:" android:id="#+id/txtVwLastValue"></TextView>
<TextView android:text="TextView" android:layout_height="wrap_content" android:layout_width="fill_parent" android:id="#+id/txtVwType"></TextView>
<ImageView android:src="#drawable/icon" android:layout_width="fill_parent" android:id="#+id/imgViewGraph" android:layout_height="300px"></ImageView>
</LinearLayout>
So it should automatic just stretch my portrait layout.
This is not entirely true, as you have also seen. The expected behaviour for an unhandled orientation change is to recreate the Activity (which means going through the entire Activity lifecycle, onDestroy(), onCreate() and so forth - obviously the reason your label is being reset). This can be handled in various ways:
http://developer.android.com/guide/topics/resources/runtime-changes.html &
http://developer.android.com/resources/articles/faster-screen-orientation-change.html
I "handle" orientation changes in 2 rather simple ways in my application. One activity is locked to portrait (as it makes no sense to view it in Landscape) by setting the screenOrientation flag in the Manifest:
<activity android:name=".SettingsActivity" android:screenOrientation="portrait">
The 2nd Activity that I use needs to be viewed in both ways as it contains a graph that looks best when viewed in Landscape. This basically means that I make sure that my Activity closes down gracefully (including any threads or handlers that I use) and is remade to draw the graph filling the screen in Landscape orientation.
You need to think about how you wish to handle the orientation change. What makes the most sense in your application? There's a lot of good resources around that talks about orientation changes through Google.
I imagine the trouble you are having with the background thread is due to handlers that you have in the Activity which is destroyed and remade.
When the device is rotated, it effectively destroys and re-creates your activity in the new orientation. If you are kicking off your thread as part of your activity, that would explain some of the behaviour that you're seeing. If you want your thread to survive between the two activity instances, then you'll need to implement it as a Service rather than a simple thread.
The way to prove this is to set a breakpoint or add some logging in your onCreate method, and you'll see this being hit both when the app starts up and the activity is first displayed, and on device rotations.
Each time you tilt your phone, i.e each time you change between portrait mode and landscape mode, the activity restarts, so onCreate() is executed.

adjustPan not preventing keyboard from covering EditText

I'm trying to create a pretty basic chat screen with a ListView displaying the text and an EditText at the bottom and a "Send" button to the right of the EditText. Everything is functional, but when I click the EditText, the virtual keyboard covers it. The screen pans up a little but not enough to become visible above the keyboard. I've got the "adjustPan" tag in my manifest and have also tried the "adjustResize" tag to no avail. I'm guessing it has something to do with the way my layout is set up, but I honestly have no clue. Please help!
Current Layout...
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<ListView android:id="#+id/android:list"
android:layout_height="0dip"
android:layout_width="fill_parent"
android:layout_weight="1"
android:stackFromBottom="true">
</ListView>
<LinearLayout android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText android:id="#+id/sendMessageBox"
android:focusable="true"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:scrollbars="vertical"
android:maxLines="4"
android:text=""
android:inputType="textShortMessage|textAutoCorrect|textCapSentences|textMultiLine"
android:maxLength="1000"
android:hint="Type your message..."
android:imeOptions="actionSend"/>
<Button android:id="#+id/sendMessageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:text="Send"/>
</LinearLayout>
After doing a lot of searching apparently it's what I'm calling a bug. If you use the fullscreen tag (to remove the status bar from the activity) you can't use "adjustResize" without wrapping the activity in a ScrollView. Unfortunately for me I'm using a ListView which would create yet another problem. I'm sick of messing with it and will probably just abandon the fullscreen on that activity.
In your manifest file, you need to set the appropriate android:windowSoftInputMode property. This attribute is valid since API 3.
<activity
...
android:windowSoftInputMode="adjustPan" >
</activity>
Options are: http://developer.android.com/guide/topics/manifest/activity-element.html#wsoft
stateUnspecified The state of the soft keyboard (whether it is hidden or visible) is not specified. The system will choose an
appropriate state or rely on the setting in the theme. This is the
default setting for the behavior of the soft keyboard.
stateUnchanged The soft keyboard is kept in whatever state it was last in, whether visible or hidden, when the activity comes to the
fore.
"stateHidden" The soft keyboard is hidden when the user chooses the activity — that is, when the user affirmatively navigates forward
to the activity, rather than backs into it because of leaving another
activity.
stateAlwaysHidden The soft keyboard is always hidden when the activity's main window has input focus.
stateVisible The soft keyboard is visible when that's normally appropriate (when the user is navigating forward to the activity's
main window).
stateAlwaysVisible The soft keyboard is made visible when the user chooses the activity — that is, when the user affirmatively
navigates forward to the activity, rather than backs into it because
of leaving another activity.
adjustUnspecified It is unspecified whether the activity's main window resizes to make room for the soft keyboard, or whether the
contents of the window pan to make the current focus visible
on-screen. The system will automatically select one of these modes
depending on whether the content of the window has any layout views
that can scroll their contents. If there is such a view, the window
will be resized, on the assumption that scrolling can make all of the
window's contents visible within a smaller area. This is the default
setting for the behavior of the main window.
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.
if you set android:windowSoftInputMode="adjustResize" for an activity in the manifest, then a ScrollView (or other collapsable ViewGroups) will shrink to accommodate the soft keyboard. But, if you set android:windowFullscreen="true" in the activity’s theme, then the ScrollView won’t shrink because it’s forced to fill the whole screen. However, setting android:fitsSystemWindows="false" in your theme also causes adjustResize not to work
I had this in my AndroidManifest. It caused the adjustPan to stop working correctly. I removed the block below and everything works fine again.
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:xlargeScreens="false"
android:anyDensity="false" />
Here is one workaround I have found. Open the problematic editText and hit the RETURN key. Notice it shifts the editText closer to the position you're shooting for.
So although hacky, you can essentially please a newline at the top of the edittext.
This also seems to work using a newline at the bottom but you'd have to use a delay to not add the newline until AFTER the soft keyboard has animated into position.
Note I only have this problem on certain phones (DroidX).
if (android.os.Build.MODEL.equals("DROIDX")) {
inputEt.setOnFocusChangeListener(new OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
String text = inputEt.getText().toString();
text = "\n\n" + text.trim();
inputEt.setText(text);
inputEt.setSelection(text.length());
}
});
}
Try adding android:windowSoftInputMode="adjustResize|stateVisible|stateAlwaysHidden" in your manifest.
You can try the following settings:
<supports-screens
android:anyDensity="true"/>

Categories

Resources