Search code examples
androidkotlinuser-interface

Button unresponsive after click on text field


I'm just getting started with Android app development and have run into a strange error while implementing an OnClickListener. I started from an "Empty Views Activity" in Android Studio. I added, among other UI elements, a Button, an EditText, and a ScrollView containing a TextView. I set an OnClickListener on the button so that a logcat message is emitted.

When I click on the button, it emits the logcat message as expected. However, as soon as I click on the text field, the button becomes unresponsive for the remainder of the app's lifetime. What's strange is that everything works perfectly if I remove the ScrollView.

Here's my MRE:

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/startErrorText"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <Button
        android:id="@+id/startButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="160dp"
        android:layout_marginTop="30dp"
        android:layout_marginEnd="160dp"
        android:text="Start"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.5"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/textField" />

    <EditText
        android:id="@+id/textField"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="80dp"
        android:layout_marginTop="48dp"
        android:layout_marginEnd="80dp"
        android:ems="10"
        android:hint="Enter some text"
        android:inputType="text"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ScrollView
        android:id="@+id/scrollView"
        android:layout_width="409dp"
        android:layout_height="585dp"
        android:layout_marginStart="1dp"
        android:layout_marginTop="20dp"
        android:layout_marginEnd="1dp"
        android:layout_marginBottom="2dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHorizontal_bias="0.0"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/startButton"
        app:layout_constraintVertical_bias="1.0">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView
                android:id="@+id/logView"
                android:layout_width="match_parent"
                android:layout_height="wrap_content" />
        </LinearLayout>
    </ScrollView>

</androidx.constraintlayout.widget.ConstraintLayout>

MainActivity.kt

package com.example.test

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.view.View
import android.view.View.OnClickListener
import android.widget.Button

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        val button: Button = findViewById(R.id.startButton)
        button.setOnClickListener(object : OnClickListener {
            override fun onClick(v: View?) {
                Log.i("MyApp", "Button pressed")
            }
        })
    }
}

Here's the logcat output:

ziparchive              com.example.test                     W  Unable to open '/data/data/com.example.test/code_cache/.overlay/base.apk/classes3.dm': No such file or directory
ziparchive              com.example.test                     W  Unable to open '/data/app/~~6ohH-jaL6kb5-3S5sLz1rw==/com.example.test-21xtpoSa2I8lnFAMirvZRA==/base.dm': No such file or directory
ziparchive              com.example.test                     W  Unable to open '/data/app/~~6ohH-jaL6kb5-3S5sLz1rw==/com.example.test-21xtpoSa2I8lnFAMirvZRA==/base.dm': No such file or directory
nativeloader            com.example.test                     D  Configuring clns-6 for other apk /data/app/~~6ohH-jaL6kb5-3S5sLz1rw==/com.example.test-21xtpoSa2I8lnFAMirvZRA==/base.apk. target_sdk_version=34, uses_libraries=, library_path=/data/app/~~6ohH-jaL6kb5-3S5sLz1rw==/com.example.test-21xtpoSa2I8lnFAMirvZRA==/lib/arm64, permitted_path=/data:/mnt/expand:/data/user/0/com.example.test
GraphicsEnvironment     com.example.test                     V  Currently set values for:
GraphicsEnvironment     com.example.test                     V    angle_gl_driver_selection_pkgs=[]
GraphicsEnvironment     com.example.test                     V    angle_gl_driver_selection_values=[]
GraphicsEnvironment     com.example.test                     V  ANGLE GameManagerService for com.example.test: false
GraphicsEnvironment     com.example.test                     V  com.example.test is not listed in per-application setting
GraphicsEnvironment     com.example.test                     V  Neither updatable production driver nor prerelease driver is supported.
libEGL                  com.example.test                     D  loaded /vendor/lib64/egl/libEGL_emulation.so
AppCompatDelegate       com.example.test                     D  Checking for metadata for AppLocalesMetadataHolderService : Service not found
libEGL                  com.example.test                     D  loaded /vendor/lib64/egl/libGLESv1_CM_emulation.so
libEGL                  com.example.test                     D  loaded /vendor/lib64/egl/libGLESv2_emulation.so
om.example.test         com.example.test                     W  Accessing hidden method Landroid/view/View;->computeFitSystemWindows(Landroid/graphics/Rect;Landroid/graphics/Rect;)Z (unsupported, reflection, allowed)
om.example.test         com.example.test                     W  Accessing hidden method Landroid/view/ViewGroup;->makeOptionalFitsSystemWindows()V (unsupported, reflection, allowed)
Compatibil...geReporter com.example.test                     D  Compat change id reported: 210923482; UID 10191; state: ENABLED
Compatibil...geReporter com.example.test                     D  Compat change id reported: 171228096; UID 10191; state: ENABLED
Compatibil...geReporter com.example.test                     D  Compat change id reported: 237531167; UID 10191; state: DISABLED
OpenGLRenderer          com.example.test                     W  Unknown dataspace 0
OpenGLRenderer          com.example.test                     W  Failed to choose config with EGL_SWAP_BEHAVIOR_PRESERVED, retrying without...
OpenGLRenderer          com.example.test                     W  Failed to initialize 101010-2 format, error = EGL_SUCCESS
Gralloc4                com.example.test                     I  mapper 4.x is not supported
OpenGLRenderer          com.example.test                     E  Unable to match the desired swap behavior.
AutofillManager         com.example.test                     D  notifyViewEnteredForFillDialog:1073741824
EGL_emulation           com.example.test                     D  app_time_stats: avg=145.48ms min=0.87ms max=1433.47ms count=10
EGL_emulation           com.example.test                     D  app_time_stats: avg=181.58ms min=13.65ms max=2822.69ms count=17
MyApp                   com.example.test                     I  Button pressed
ProfileInstaller        com.example.test                     D  Installing profile for com.example.test
EGL_emulation           com.example.test                     D  app_time_stats: avg=39.13ms min=0.77ms max=1255.29ms count=48
Compatibil...geReporter com.example.test                     D  Compat change id reported: 163400105; UID 10191; state: ENABLED
ImeTracker              com.example.test                     I  com.example.test:39ecb7d6: onRequestShow at ORIGIN_CLIENT_SHOW_SOFT_INPUT reason SHOW_SOFT_INPUT
InputMethodManager      com.example.test                     D  showSoftInput() view=androidx.appcompat.widget.AppCompatEditText{4a6450d VFED..CL. .F.P..ID 250,132-830,256 #7f0801f9 app:id/textField aid=1073741824} flags=0 reason=SHOW_SOFT_INPUT
AssistStructure         com.example.test                     I  Flattened final assist data: 1492 bytes, containing 1 windows, 8 views
InsetsController        com.example.test                     D  show(ime(), fromIme=true)
InteractionJankMonitor  com.example.test                     D  Build configuration failed!
                                                                                                    java.lang.IllegalArgumentException: Must pass in a valid surface control if only instrument surface; 
                                                                                                        at com.android.internal.jank.InteractionJankMonitor$Configuration.validate(InteractionJankMonitor.java:1259)
                                                                                                        at com.android.internal.jank.InteractionJankMonitor$Configuration.<init>(InteractionJankMonitor.java:1217)
                                                                                                        at com.android.internal.jank.InteractionJankMonitor$Configuration.<init>(Unknown Source:0)
                                                                                                        at com.android.internal.jank.InteractionJankMonitor$Configuration$Builder.build(InteractionJankMonitor.java:1197)
                                                                                                        at com.android.internal.jank.InteractionJankMonitor.begin(InteractionJankMonitor.java:611)
                                                                                                        at android.view.inputmethod.ImeTracker$ImeJankTracker.onRequestAnimation(ImeTracker.java:717)
                                                                                                        at android.view.InsetsController$InternalAnimationControlListener$2.onAnimationStart(InsetsController.java:448)
                                                                                                        at android.animation.Animator$AnimatorListener.onAnimationStart(Animator.java:695)
                                                                                                        at android.animation.Animator$AnimatorCaller$$ExternalSyntheticLambda0.call(Unknown Source:4)
                                                                                                        at android.animation.Animator.callOnList(Animator.java:669)
                                                                                                        at android.animation.Animator.notifyListeners(Animator.java:608)
                                                                                                        at android.animation.Animator.notifyStartListeners(Animator.java:625)
                                                                                                        at android.animation.ValueAnimator.startAnimation(ValueAnimator.java:1334)
                                                                                                        at android.animation.ValueAnimator.start(ValueAnimator.java:1149)
                                                                                                        at android.animation.ValueAnimator.start(ValueAnimator.java:1173)
                                                                                                        at android.view.InsetsController$InternalAnimationControlListener.onReady(InsetsController.java:470)
                                                                                                        at android.view.InsetsAnimationThreadControlRunner.lambda$new$0(InsetsAnimationThreadControlRunner.java:129)
                                                                                                        at android.view.InsetsAnimationThreadControlRunner.$r8$lambda$3zGKYd3XPzPnvMO2hiF8a88M6T0(Unknown Source:0)
                                                                                                        at android.view.InsetsAnimationThreadControlRunner$$ExternalSyntheticLambda2.run(Unknown Source:6)
                                                                                                        at android.os.Handler.handleCallback(Handler.java:958)
                                                                                                        at android.os.Handler.dispatchMessage(Handler.java:99)
                                                                                                        at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                        at android.os.Looper.loop(Looper.java:294)
                                                                                                        at android.os.HandlerThread.run(HandlerThread.java:67)
ImeTracker              com.example.test                     I  com.example.test:39ecb7d6: onShown
EGL_emulation           com.example.test                     D  app_time_stats: avg=213.12ms min=0.85ms max=500.33ms count=5
EGL_emulation           com.example.test                     D  app_time_stats: avg=500.68ms min=499.36ms max=502.83ms count=3

That java.lang.IllegalArgumentException only occurs sometimes but the overall bug occurs every time.


Solution

  • When you click on EditText, the keyboard pops up and there's less space for ScrollView. Because you ScrollView has fixed size, it overlaps button. You can see it on Layout Inspector I've made a screenshot for you:

    enter image description here

    SOLUTION: You need to set ScrollView height to 0dp. ConstraintLayout interprets it as "fill all available space". And when keyboard pops up, it will take only available space and not overlap the button.

    Just copy past this xml and it will work well:

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.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:id="@+id/startErrorText"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <Button
            android:id="@+id/startButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="160dp"
            android:layout_marginTop="30dp"
            android:layout_marginEnd="160dp"
            android:text="Start"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/textField" />
    
        <EditText
            android:id="@+id/textField"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="80dp"
            android:layout_marginTop="48dp"
            android:layout_marginEnd="80dp"
            android:ems="10"
            android:hint="Enter some text"
            android:inputType="text"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <ScrollView
            android:id="@+id/scrollView"
            android:layout_width="409dp"
            android:layout_height="0dp"
            android:layout_marginStart="1dp"
            android:layout_marginTop="20dp"
            android:layout_marginEnd="1dp"
            android:layout_marginBottom="2dp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.0"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/startButton">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical">
    
                <TextView
                    android:id="@+id/logView"
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content" />
            </LinearLayout>
        </ScrollView>
    
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    Here is how your ScrollView looks like now (check the space it uses): enter image description here

    ADVICE: If you use ConstraintLayout try not to hard code sizes and rely on constraints if possible.