我有一个输入字段的动态列表。那些。根据用户对某些值的选择,将显示输入字段列表。因此,我必须以编程方式将这些字段 (EditText) 添加到我专门放置在标记中的空 LinearLayout。代码是在某个时刻添加的(当用户在微调器中选择了所需的类别时)。创建输入字段的代码如下:
fun generateAdditionalFields(fields: ArrayList<String>) {
tempAdditionalFields.removeAll(tempAdditionalFields)
var isFirst = true
fields.forEach {
tempAdditionalFields.add(makeEditText(it, it, isFirst))
isFirst = false
}
additional_fields_block.removeAllViews()
tempAdditionalFields.forEach {
additional_fields_block.addView(it)
}
}
fun makeEditText(hint: String, name: String, first: Boolean) : EditText {
val editText = EditText(ContextThemeWrapper(this, R.style.MainInput), null, 0)
editText.hint = hint
editText.layoutParams = LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
editText.tag = name
if (!first) editText.setMargin(topMargin = dpToPix(5f))
return editText
}
我还有一个列表来保存这些输入字段tempAdditionalFields
,以便以后可以保存用户的输入。
当用户切换类别时,旧的输入字段将被删除(从列表和从LinearLayout
)并重新生成。
一切正常,没有问题。但我决定在设备屏幕旋转时修改代码并保存数据,并在重新创建活动后恢复它们。通常这个操作没有问题。但是在这种情况下,在重新创建活动之后,我恢复了列表tempAdditionalFields
,然后将其中的所有内容添加EditText
回 LinearLayout,但随后它给出了一个错误,他们说这些视图已经包含在那里。然后我在添加之前打电话additional_fields_block.removeAllViews()
,仍然得到同样的错误。然后我删除了添加。但是屏幕上会显示全新的输入字段。是的,这些是重新创建活动之前的输入字段,但没有输入的文本。虽然在恢复列表中tempAdditionalFields
输入的文字是。为了实验,我一般去掉这个列表的恢复。甚至在恢复后删除了这些输入字段的添加。结果是一样的。最有趣的是,该方法additional_fields_block.removeAllViews()
在重新创建后不起作用。或者更确切地说,如果我在onCreate
or onStart
or中调用它onResume
。但是,如果您创建一个按钮并在其上挂一个侦听器,并且当您单击它时,设置相同的视图删除,那么当您单击它时,它会按原样删除。
我没有发布所有代码,因为它有很多。但这里有一些部分。
创建:
var step = 1
var avatar_id : Int? = null
var tempAdditionalFields = LinkedList<EditText>()
var companies = LinkedList<Company>()
val companiesAdapter = CompaniesShortListAdapter(companies)
var dontSwitchFields = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_create_user)
if (!restoreFromSavedInstance(savedInstanceState)){
GetCompaniesListTask().execute()
} else dontSwitchFields = true
switchUserInfoBtnsVis()
switchFirmNameBtnsVis()
switchChooseRoleBtnsVis()
setListeners()
switchStep(step)
}
保存和恢复方法(这里我为实验更改并注释掉了一些行):
override fun onSaveInstanceState(outState: Bundle?) {
outState?.let {bundle ->
bundle.putInt("step", step)
avatar_id?.let { bundle.putInt("avatar_id", it) }
bundle.putSerializable("tempAdditionalFields", tempAdditionalFields)
bundle.putSerializable("companies", companies)
}
super.onSaveInstanceState(outState)
}
fun restoreFromSavedInstance(savedInstanceState: Bundle?): Boolean {
savedInstanceState?.let { bundle ->
step = bundle.getInt("step")
switchStep(step)
avatar_id = bundle.getInt("avatar_id")
// tempAdditionalFields.removeAll(tempAdditionalFields)
// tempAdditionalFields.addAll(bundle.getSerializable("tempAdditionalFields") as Collection<EditText>)
/* tempAdditionalFields.forEach {
Log.e("TEST_EDIT", "edit: ${it.text}")
}*/
additional_fields_block.removeAllViews()
companies.removeAll(companies)
companies.addAll(bundle.getSerializable("companies") as Collection<Company>)
companiesAdapter.notifyDataSetChanged()
return true
}
return false
}
我了解系统本身会恢复这些添加的视图。但是在什么阶段呢?而且我知道在这种情况下,可以采用另一种方式,只需保存在字段中输入的数据,然后再次填写。但我想追根究底,为什么我不能删除在旋转屏幕之前添加的视图(EditText
)
总的来说,事实证明,问题同时出现在两个地方。简而言之,它们是:
现在更详细地说,突然有人会派上用场。
1. 微调器
我的微调器上有一个监听器,我在其中处理
onItemSelected
. 当这个方法被触发时,我调用了输入字段的生成。那些。根据选择的类别,他们创建了自己的EditText
并且每次都创建了编号。但我不认为这种方法在初始化微调器中的类别列表时也有效。那些。一旦类别列表(或适配器)连接到微调器,就会触发此方法。当我意识到这一点时,我改变了我的方法。我创建了一个变量,存储被选中的类别在微调器中的位置,默认为0,当方法被触发时,
onItemSelected
我检查这个变量的值是否与新的位置匹配。如果不匹配,那么用户已经改变了类别,然后我们将新的位置保存在这个变量中并执行我们需要的方法来生成输入字段。我在屏幕更改时保存并恢复此变量。我在微调器恢复之前从 onCreate 方法执行此操作。因此,当微调器恢复并设置旧位置时,它将匹配恢复的变量并且不会生成输入字段。
这解决了从头开始重新创建视图的问题。但是错误问题仍然存在。
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
2. 删除视图
如果我直接访问包含输入字段的视图并调用
removeAllViews()
,然后尝试添加输入字段,则会发生相同的错误。起初我将它的文本理解为“父视图已经包含这个子视图,首先将它从父视图中删除”。但是意思却有些不同“子视图已经有了父视图。首先,removeView()
在子视图的父视图上调用方法。”虽然这两个句子看起来意思相同,但这并不完全正确。起初,我尝试直接在视图上调用此方法,将每个
EditText
恢复的数组分别传递给它。但它没有用。但事实证明,显然
EditText
's 属于旧的additional_fields_block
(视图LinearLayout
),而新的没有子视图。因此,他们的父母不是新的additional_fields_block
,而是旧的。旧的additional_fields_block
仍然存在于内存中,因为在EditText
. 然后我创建了这个方法:那些。我从每个文本字段中获取祖先并从那里删除该文本字段,然后将其添加到新的
additional_fields_block
. 至少我是这么理解的。也许我的猜测是错误的,那么请纠正我。了解这种机制实际上是如何工作的很有趣。没错,从 onCreate 我的方法不起作用(没有发生错误,但也没有效果)。然后我把他的电话放到这个方法中:
我不太明白为什么会这样。有人可以解释吗?