Android 自定义编辑文本值已被另一个自定义编辑文本更改
Android custom edit text value is changed by another custom edit text
简介
在我的一个项目中,我尝试使用 header 和一些自定义验证创建自定义 EditText。当我用屏幕旋转和 activity 娱乐测试这个自定义视图时,我遇到了一个奇怪的问题。
有什么问题
娱乐前
当应用程序启动时,所有编辑文本都有正确的值,这些值是从 activity 静态设置的。如下图:
娱乐后
在我旋转屏幕或重新创建后 activity EditText 的值将被弄乱。 CustomEditText 值设置为 XML 中最后编辑文本的值。简单(基本 Android EditText)编辑文本值设置正常。
代码
我从出现这个问题的项目中复制了代码。
主要活动
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
first_custom_edit_text.header = "First header"
first_custom_edit_text.setText("First text")
third_custom_edit_text.header = "Third header"
third_custom_edit_text.setText("Third text")
first_simple_edit_text.setText("First simple - Not affected")
second_custom_edit_text.header = "Second header"
second_custom_edit_text.setText("Second text")
second_simple_edit_text.setText("Second simple - Not affected")
}
}
CustomEditText
class CustomEditText : LinearLayout {
fun setText(value: String?){
this.input_edit_text.text = Editable.Factory.getInstance().newEditable(value ?: "")
}
fun getText(): String {
return this.input_edit_text.text.toString()
}
var header: String?
get() = this.header_text_view.text.toString()
set(value) {
this.header_text_view.text = Editable.Factory.getInstance().newEditable(value ?: "")
}
constructor(context: Context) : super(context){
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs){
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
inflate(context, R.layout.ui_custom_edit_text, this)
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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=".MainActivity"
android:orientation="vertical">
<com.example.customedittextbug.CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/first_custom_edit_text"/>
<com.example.customedittextbug.CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_custom_edit_text"/>
<EditText
tools:hint="input@hint.example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:inputType="text"
android:id="@+id/first_simple_edit_text"/>
<com.example.customedittextbug.CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/third_custom_edit_text"/>
<EditText
tools:hint="input@hint.example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:inputType="text"
android:id="@+id/second_simple_edit_text"/>
</LinearLayout>
ui_custom_edit_text.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
tools:text="Input header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textStyle="bold"
android:textSize="17sp"
android:id="@+id/header_text_view"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/validations_errors_holder"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/common_input_holder">
<EditText
tools:hint="input@hint.example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:inputType="text"
android:id="@+id/input_edit_text"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/input_edit_text"
android:layout_centerVertical="true"
android:layout_marginEnd="4dp"
android:layout_marginStart="4dp"
android:gravity="end"
android:orientation="horizontal"
android:id="@+id/right_view_holder"/>
</RelativeLayout>
</LinearLayout>
更新
在我的问题得到解答后,我发现这两个指南很好地解释了如何解决这个问题。
状态恢复以 ID 为键,您所有的自定义视图都有一个具有相同 ID 的子视图:input_edit_text
。因此,他们都恢复到相同的状态,因为他们都得到了最后一个保存在该 ID 下的状态。
您可以通过在 EditText
上设置 android:saveEnabled="false"
来避免这种情况(尽管您可能想要在 CustomEditText
中自己执行实例状态的 save/restore) .
我厌倦了搜索,但这对我有用。
添加到 CustomEditText class
companion object {
private const val SPARSE_STATE_KEY = "SPARSE_STATE_KEY"
private const val SUPER_STATE_KEY = "SUPER_STATE_KEY"
}
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
dispatchFreezeSelfOnly(container)
}
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
dispatchThawSelfOnly(container)
}
override fun onSaveInstanceState(): Parcelable? {
Log.i("ByHand", "onSaveInstanceState")
return Bundle().apply {
Log.i("ByHand", "Writing children state to sparse array")
putParcelable(SUPER_STATE_KEY, super.onSaveInstanceState())
putSparseParcelableArray(SPARSE_STATE_KEY, saveChildViewStates())
}
}
override fun onRestoreInstanceState(state: Parcelable?) {
Log.i("ByHand", "onRestoreInstanceState")
var newState = state
if (newState is Bundle) {
Log.i("ByHand", "Reading children children state from sparse array")
val childrenState = newState.getSparseParcelableArray<Parcelable>(SPARSE_STATE_KEY)
childrenState?.let { restoreChildViewStates(it) }
newState = newState.getParcelable(SUPER_STATE_KEY)
}
super.onRestoreInstanceState(newState)
}
fun ViewGroup.saveChildViewStates(): SparseArray<Parcelable> {
val childViewStates = SparseArray<Parcelable>()
children.forEach { child -> child.saveHierarchyState(childViewStates) }
return childViewStates
}
fun ViewGroup.restoreChildViewStates(childViewStates: SparseArray<Parcelable>) {
children.forEach { child -> child.restoreHierarchyState(childViewStates) }
}
简介
在我的一个项目中,我尝试使用 header 和一些自定义验证创建自定义 EditText。当我用屏幕旋转和 activity 娱乐测试这个自定义视图时,我遇到了一个奇怪的问题。
有什么问题
娱乐前
当应用程序启动时,所有编辑文本都有正确的值,这些值是从 activity 静态设置的。如下图:
娱乐后
在我旋转屏幕或重新创建后 activity EditText 的值将被弄乱。 CustomEditText 值设置为 XML 中最后编辑文本的值。简单(基本 Android EditText)编辑文本值设置正常。
代码
我从出现这个问题的项目中复制了代码。
主要活动
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
first_custom_edit_text.header = "First header"
first_custom_edit_text.setText("First text")
third_custom_edit_text.header = "Third header"
third_custom_edit_text.setText("Third text")
first_simple_edit_text.setText("First simple - Not affected")
second_custom_edit_text.header = "Second header"
second_custom_edit_text.setText("Second text")
second_simple_edit_text.setText("Second simple - Not affected")
}
}
CustomEditText
class CustomEditText : LinearLayout {
fun setText(value: String?){
this.input_edit_text.text = Editable.Factory.getInstance().newEditable(value ?: "")
}
fun getText(): String {
return this.input_edit_text.text.toString()
}
var header: String?
get() = this.header_text_view.text.toString()
set(value) {
this.header_text_view.text = Editable.Factory.getInstance().newEditable(value ?: "")
}
constructor(context: Context) : super(context){
init(context, null)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs){
init(context, attrs)
}
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
init(context, attrs)
}
private fun init(context: Context, attrs: AttributeSet?) {
inflate(context, R.layout.ui_custom_edit_text, this)
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
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=".MainActivity"
android:orientation="vertical">
<com.example.customedittextbug.CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/first_custom_edit_text"/>
<com.example.customedittextbug.CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/second_custom_edit_text"/>
<EditText
tools:hint="input@hint.example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:inputType="text"
android:id="@+id/first_simple_edit_text"/>
<com.example.customedittextbug.CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/third_custom_edit_text"/>
<EditText
tools:hint="input@hint.example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:inputType="text"
android:id="@+id/second_simple_edit_text"/>
</LinearLayout>
ui_custom_edit_text.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
tools:text="Input header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:textStyle="bold"
android:textSize="17sp"
android:id="@+id/header_text_view"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:id="@+id/validations_errors_holder"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/common_input_holder">
<EditText
tools:hint="input@hint.example"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="-4dp"
android:layout_marginRight="-4dp"
android:textColor="@android:color/black"
android:textSize="18sp"
android:inputType="text"
android:id="@+id/input_edit_text"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/input_edit_text"
android:layout_centerVertical="true"
android:layout_marginEnd="4dp"
android:layout_marginStart="4dp"
android:gravity="end"
android:orientation="horizontal"
android:id="@+id/right_view_holder"/>
</RelativeLayout>
</LinearLayout>
更新
在我的问题得到解答后,我发现这两个指南很好地解释了如何解决这个问题。
状态恢复以 ID 为键,您所有的自定义视图都有一个具有相同 ID 的子视图:input_edit_text
。因此,他们都恢复到相同的状态,因为他们都得到了最后一个保存在该 ID 下的状态。
您可以通过在 EditText
上设置 android:saveEnabled="false"
来避免这种情况(尽管您可能想要在 CustomEditText
中自己执行实例状态的 save/restore) .
我厌倦了搜索,但这对我有用。
添加到 CustomEditText class
companion object {
private const val SPARSE_STATE_KEY = "SPARSE_STATE_KEY"
private const val SUPER_STATE_KEY = "SUPER_STATE_KEY"
}
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
dispatchFreezeSelfOnly(container)
}
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
dispatchThawSelfOnly(container)
}
override fun onSaveInstanceState(): Parcelable? {
Log.i("ByHand", "onSaveInstanceState")
return Bundle().apply {
Log.i("ByHand", "Writing children state to sparse array")
putParcelable(SUPER_STATE_KEY, super.onSaveInstanceState())
putSparseParcelableArray(SPARSE_STATE_KEY, saveChildViewStates())
}
}
override fun onRestoreInstanceState(state: Parcelable?) {
Log.i("ByHand", "onRestoreInstanceState")
var newState = state
if (newState is Bundle) {
Log.i("ByHand", "Reading children children state from sparse array")
val childrenState = newState.getSparseParcelableArray<Parcelable>(SPARSE_STATE_KEY)
childrenState?.let { restoreChildViewStates(it) }
newState = newState.getParcelable(SUPER_STATE_KEY)
}
super.onRestoreInstanceState(newState)
}
fun ViewGroup.saveChildViewStates(): SparseArray<Parcelable> {
val childViewStates = SparseArray<Parcelable>()
children.forEach { child -> child.saveHierarchyState(childViewStates) }
return childViewStates
}
fun ViewGroup.restoreChildViewStates(childViewStates: SparseArray<Parcelable>) {
children.forEach { child -> child.restoreHierarchyState(childViewStates) }
}