问题
I have a strange issue, I have 2 fragment, in first fragment, I have some custom EditText
, and a button to replace this by second fragment (addToBackStack
= true), then, in second fragment, I try to using popBackStack()
to back to first fragment, the issue occur, all custom EditText
have same value.
Below is all my code
FirstFragment
class FirstFragment : BaseFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_first, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_next.setOnClickListener {
val transaction = requireActivity().supportFragmentManager!!.beginTransaction()
.replace(R.id.contentFrame, SecondFragment(), "")
commitTransaction(transaction, true, -1)
}
}
}
SecondFragment
class SecondFragment : BaseFragment() {
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
return inflater.inflate(R.layout.fragment_second, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
btn_back.setOnClickListener {
requireActivity().supportFragmentManager.popBackStack()
}
}
}
fragment_first.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
>
<com.sogia.replacefragmentdemo.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<com.sogia.replacefragmentdemo.CustomView
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
<Button
android:id="@+id/btn_next"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="next"
/>
</LinearLayout>
fragment_second.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<Button
android:id="@+id/btn_back"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="back"
/>
</LinearLayout>
CustomView
class CustomView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
init {
inflate(context, R.layout.layout_custom_view, this)
}
}
layout_custom_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<EditText
android:id="@+id/edt"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="input something"
/>
</LinearLayout>
Any response would be appreciated.
回答1:
You're not adding to the back stack, you're replacing, try this to open the second fragment, this will keep the state of FirstFragment intact and in stack
btn_next.setOnClickListener {
val transaction = requireActivity().supportFragmentManager!!.beginTransaction()
.add(R.id.contentFrame, SecondFragment(), "")
commitTransaction(transaction, true, -1)
}
Also both of your EditText
s have the same ids android:id="@+id/edt"
thus the synthesizer function which runs findViewById
will point to the same object of EditText
回答2:
Finally I find out the root cause and the solution, Thanks @abhradeep ghosh, @Eselfar, @MadScientist and another one else who response this post.
Cause: view IDs should be unique! Otherwise your state will be overwritten by another view with the same ID. In my case I have 2 views with id @id/edt
, so my states container holds only 1 instance of it - whichever came last during state store process.
Here is my solution, (from this post),
First, create new class for save sate of view
class SavedState(superState: Parcelable) : View.BaseSavedState(superState) {
var childrenStates: SparseArray<Any>? = null
override fun writeToParcel(out: Parcel, flags: Int) {
super.writeToParcel(out, flags)
childrenStates?.let {
out.writeSparseArray(it)
}
}
}
And in my CustomView
@Suppress("UNCHECKED_CAST")
public override fun onSaveInstanceState(): Parcelable? {
val superState = super.onSaveInstanceState()
val ss = SavedState(superState)
ss.childrenStates = SparseArray()
for (i in 0 until childCount) {
getChildAt(i).saveHierarchyState(ss.childrenStates as SparseArray<Parcelable>)
}
return ss
}
@Suppress("UNCHECKED_CAST")
public override fun onRestoreInstanceState(state: Parcelable) {
val ss = state as SavedState
super.onRestoreInstanceState(ss.superState)
for (i in 0 until childCount) {
getChildAt(i).restoreHierarchyState(ss.childrenStates as SparseArray<Parcelable>)
}
}
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
dispatchFreezeSelfOnly(container)
}
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
dispatchThawSelfOnly(container)
}
回答3:
I would try adding a different ID to each of your custom views in the layout. Like :
com.sogia.replacefragmentdemo.CustomView
android:id="@+id/custom1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
com.sogia.replacefragmentdemo.CustomView
android:id="@+id/custom2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
/>
回答4:
Having same id can be a problem as managing state could get messy. Check this one for an solution: Restore fragment with two views having the same id
来源:https://stackoverflow.com/questions/52268043/all-editexts-be-replace-by-same-value-after-pop-a-fragment