0

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>
Ted Henry
  • 1,442
  • 1
  • 14
  • 34

4 Answers4

1

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).

Gabe Sechan
  • 90,003
  • 9
  • 87
  • 127
  • "How to do what you want- you can't." Wow! Really?! WOW! Thanks. – Ted Henry Jan 25 '19 at 03:27
  • @TedHenry You might be able to get something close to it by showing different layouts based on screen heights entirely using resource overrides. Sort of like you can have layout-sw320dp for smallest width of 320 dp, you can do layout-hxxxdp for height. Its a really rare usecase, but it will get you close to what you want, just without the keyboard detection – Gabe Sechan Jan 25 '19 at 03:30
  • I don’t really need to know that the keyboard has appeared. I really need to know the he window height has changed and adjust the layout accordingly. I can try different layout resources but if the activity is not recreated, I’m skeptical the layout resource will be changed. – Ted Henry Jan 25 '19 at 04:55
  • The layout wouldn't be around changing the layout when the keyboard comes out, but of not putting in the extra elements at all if the screen is too small. The problem with detecting size changes is that there's a dozen other things that could change the size, not just a keyboard coming up. – Gabe Sechan Jan 25 '19 at 04:57
  • It’s interesting to compare Facebook login on iOS and Android. On iOS, when focusing the username input, there is a nice animation to shrink the branding image and change it to a smaller image. On Android, when focusing the username input, there is a janky and unpleasantly abrupt layout change. – Ted Henry Jan 25 '19 at 05:22
  • It seems reasonable to expect there would be anAndroid API for detecting a window size change. As you wrote, there could be many reasons why it could happen and there could be many things apps need to do in response. – Ted Henry Jan 25 '19 at 05:24
  • What about addOnLayoutChangeListener on the rootView of the screen? My rootView is a ConstraintLayout with match_parent for width and height. https://stackoverflow.com/a/34446944/7034640 – Ted Henry Jan 25 '19 at 05:28
  • `ConstraintLayout` is implemented in Java. A `ConstraintLayout` knows how to move its children around the screen when the `ConstraintLayout`'s size changes. `ConstraintLayout extends ViewGroup`. The documentation for `ViewGroup` shows an example of a custom layout manager. If all this is possible, it must be possible to define a custom `ViewGroup` subclass that can do what I want to do. – Ted Henry Jan 25 '19 at 17:56
1

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

S. Czop
  • 602
  • 1
  • 6
  • 17
0

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.....

Karæthon
  • 33
  • 8
-2

"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.

Karæthon
  • 33
  • 8
  • 1
    There is no API to tell the keyboard appears/disappears. There are a series of hacks people try to use, and those can work some of the time, or get you some of the functionality, but there is no general solution, and the hacks shouldn't be relied on because they all have weaknesses. Also, this isn't an answer, it should be a comment on my answer. – Gabe Sechan Jan 25 '19 at 03:54
  • Yeah, I know it's not an answer but the add comment wasn't appearing. I know there's no built in way but as the PayPal app shows it can be reliably done. Good programmers don't have imposibles, just ways that didn't work, there has to be a way to get it to do what the programmer wants. – Karæthon Jan 25 '19 at 04:10
  • ANd PayPal's app doesn't prove it can be done- it proves some subset of it can be done, under some cicrumstances. But there is NO api to actually tell when a keyboard appears and disappears. It doesn't exist. There's things you can do to get parts of it, but they have dozens of failure cases, and aren't assured to work on future versions of the OS. So they shouldn't be used or encouraged (unless the subset that is possible is enough for the usecase). And many of the hacks that were used 2 or 3 years ago don't work anymore, leading to broken apps. – Gabe Sechan Jan 25 '19 at 04:14
  • Your right good programmers DEAL with impossibles all the time. Except it's not can't be done at all but can't be done this way. These things are tools and we the human programmers TELL them what to do, we just sometimes have to figure out the right way to tell it. – Karæthon Jan 25 '19 at 04:19
  • And sometimes the answer is it can't be done, not reliably. Which is absolutely the case here. And there are plenty of things that just can't be done- if you haven't run into them you either work on really trivial stuff or you're an absolute newbie to the field. – Gabe Sechan Jan 25 '19 at 04:21
  • I'll give you the not reliably, I've had code that works really well in one case but if I move it to another program it barfs. I need to tweak it, that's kinda my point, we can't give a "this WILL work" answer but we can say "this is what has worked before, start here and experiment". Yes I am only a hobbyist programmer but 30+ years of tinkering has taught me that I can find a way, maybe that only works in that case but I can find A way. – Karæthon Jan 25 '19 at 04:27
  • Yes I agree 100% if it's getting too difficult you are probably going about it the wrong way. – Karæthon Jan 25 '19 at 04:40
  • I'm starting to think what I want to do can be done on Android by creating a custom subclass of ViewGroup. It seems to be something that people do. – Ted Henry Jan 26 '19 at 02:24