I'm using TextInputLayout
in my application and when I add a TextWatcher
to it's TextInputEditText
that sets an error
on the TextInputLayout
, my app crashes when I trigger a configuration change.
editText.addTextChangedListener(object : TextWatcher {
override fun afterTextChanged(text: Editable) {
// no-op
}
override fun beforeTextChanged(text: CharSequence, start: Int, count: Int, after: Int) {
// no-op
}
override fun onTextChanged(text: CharSequence, start: Int, before: Int, count: Int) {
textInputLayout.error = if (text.isBlank()) {
getString(R.string.error_text)
} else {
null
}
}
})
The stacktrace is
java.lang.NullPointerException: Attempt to read from field 'int android.view.View.mViewFlags' on a null object reference at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3863) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at com.google.android.material.textfield.TextInputLayout.dispatchRestoreInstanceState(TextInputLayout.java:2053) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at android.view.ViewGroup.dispatchRestoreInstanceState(ViewGroup.java:3864) at android.view.View.restoreHierarchyState(View.java:19808) at androidx.fragment.app.Fragment.restoreViewState(Fragment.java:539) at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:907) at androidx.fragment.app.FragmentManagerImpl.moveFragmentToExpectedState(FragmentManagerImpl.java:1238) at androidx.fragment.app.FragmentManagerImpl.moveToState(FragmentManagerImpl.java:1303) at androidx.fragment.app.FragmentManagerImpl.dispatchStateChange(FragmentManagerImpl.java:2656) at androidx.fragment.app.FragmentManagerImpl.dispatchActivityCreated(FragmentManagerImpl.java:2610) at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:246) at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:542) at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:201) at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1425) at android.app.Activity.performStart(Activity.java:7825) at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3294) at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221) at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201) at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173) at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2016) at android.os.Handler.dispatchMessage(Handler.java:107) at android.os.Looper.loop(Looper.java:214) at android.app.ActivityThread.main(ActivityThread.java:7356) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
It seems like you might need to override onViewStateRestored
and remove/add your TextWatcher
from there.
You should also save your TextWatcher
somewhere that can survive a configuration change such as an Android ViewModel
.
Here's a quick example:
// Inside of onViewStateRestored
if (viewModel.textWatcher != null) {
editText.removeTextChangedListener(viewModel.textWatcher)
viewModel.textWatcher = null
}
// Create and add TextWatcher back