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.
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:
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):
ADVICE: If you use ConstraintLayout
try not to hard code sizes and rely on constraints if possible.