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.
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
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)
}
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"
/>
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