I am inflating a few EditTexts
and adding them to a LinearLayout
:
private void setupCommentViews(){
int i = 0;
Iterator<CommentInformation> iterator = commentInformations.iterator();
while(iterator.hasNext()) {
i++;
View v = LayoutInflater.from(context).inflate(R.layout.comment_row_item, commentsContainer, false);
EditText commentField = (EditText)v.findViewById(R.id.comment);
CommentInformation commentInformation = iterator.next();
final String uniqueId = commentInformation.getUniqueId();
String currentCommentText = comments.get(uniqueId);
if(currentCommentText != null) {
Log.v("into","Setting old text: "+currentCommentText);
commentField.setText(currentCommentText);
} else {
Log.v("into","Setting empty text: "+i);
commentField.setText("Setting empty text: "+i);
}
commentField.addTextChangedListener(new TextWatcher() {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void afterTextChanged(Editable s) {
comments.put(uniqueId, s.toString().trim());
}
});
commentsContainer.addView(v);
}
}
The logs after the first run look like this:
04-01 17:40:41.244: V/into(28770): Setting empty text: 1
04-01 17:40:41.254: V/into(28770): Setting empty text: 2
04-01 17:40:41.254: V/into(28770): Setting empty text: 3
And all of the EditTexts have the correct text in them ("Setting empty text: #")
-
I am adding a TextChangedListener
to my EditText
that I inflate and when the text is changed I update a Map<String, String>
and put the uniqueId as the key and the comment text as the value.
The problem occurs when I rotate the phone after changing one of the comments. I save the comments Map
inside of onSaveInstanceState
:
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(COMMENTS, (Serializable)comments);
}
and I then retrieve them inside of onActivityCreated
if(savedInstanceState != null) {
Log.v("into","Retrieving...");
comments = (Map<String, String>)savedInstanceState.getSerializable(COMMENTS);
}
I also iterate through that Map
and verify that there is one entry with the correct uniqueId and correct changed comment text.
-
Next I call setupCommentViews()
again. This time the logs look right, but the EditTexts
ALL have the same text as the final one (the last inflated one).
04-01 17:42:49.664: V/into(28770): Setting empty text: 1
04-01 17:42:49.664: V/into(28770): Setting old text: Changed the second textview
04-01 17:42:49.674: V/into(28770): Setting empty text: 3
But all the EditTexts
now say: "Setting empty text: 3"
Using a ListView
is NOT the solution I'm looking for as there are lots of bugs with EditTexts
inside of ListView
with AdjustResize
softInputMode
-
Can anyone shed any light on what is going on here?
I met this problem too, but I have to restore the sate before. So i create a View named FixedEditText
and override both dispatchSaveInstanceState()
and dispatchRestoreInstanceState()
two method. just like this:
override fun dispatchSaveInstanceState(container: SparseArray<Parcelable>) {
try {
val key = initKey()
if (key != null && isSaveEnabled) {
val state = onSaveInstanceState()
if (state != null) {
//don't use the id as the key
container.put(key.hashCode(), state)
}
}
} catch (e: Exception) {
super.dispatchSaveInstanceState(container)
e.printStackTrace()
}
}
override fun dispatchRestoreInstanceState(container: SparseArray<Parcelable>) {
try {
val key = initKey()
if (key != null) {
val state = container.get(key.hashCode())
if (state != null) {
onRestoreInstanceState(state)
}
}
} catch (e: Exception) {
super.dispatchRestoreInstanceState(container)
e.printStackTrace()
}
}
private fun initKey(): Any? {
(parent as? View)?.run {
//My ViewGroup has diff id.
return "${this.javaClass.simpleName}$${this.id}"
}
return null
}
By default, when onSaveInstanceState
method of an activity is called, it also goes through the view hierarchy and try to save the state of any view if possible and restore those views state if onRestoreInstanceState
method of activity is called later. When each View is called to save the state, we can see this method in the View class:
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
mPrivateFlags &= ~PFLAG_SAVE_STATE_CALLED;
Parcelable state = onSaveInstanceState();
if ((mPrivateFlags & PFLAG_SAVE_STATE_CALLED) == 0) {
throw new IllegalStateException(
"Derived class did not call super.onSaveInstanceState()");
}
if (state != null) {
// Log.i("View", "Freezing #" + Integer.toHexString(mID)
// + ": " + state);
container.put(mID, state);
}
}
}
EditText inherits from TextView, when check the code of TextView we see the onSaveInstanceState
method return current text and onRestoreInstanceState
method get back the text and display.
Now, back to your code. You save the text content yourself and restore it when onCreate
is called, but EditText itself also save the text and restore when onRestoreInstanceState
method of Activity is call, which is called after onCreate
for sure, one more thing, all your EditText have same id, so when the state of view hierarchy is saved in to a SparseArray, the last EditText state (in this case is text content) will overwrite all the previous. And that's why your problem happen.
Solutions:
onRestoreInstanceState
of activity and not call super method so that you can prevent the views to auto-restore its state, but I don't think this is good idea since there might be other things need to be restored by default, not only your EditTextI had the same problem. In my custom LinearLayout I Override these methods.
@Override
protected Parcelable onSaveInstanceState() {
Parcelable superState = super.onSaveInstanceState();
return new SavedState(superState, txtData.getText().toString());
}
@Override
protected void onRestoreInstanceState(Parcelable state) {
SavedState savedState = (SavedState) state;
super.onRestoreInstanceState(savedState.getSuperState());
txtData.setText(savedState.data);
}
@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
super.dispatchFreezeSelfOnly(container);
}
@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
super.dispatchThawSelfOnly(container);
}
private class SavedState extends BaseSavedState {
public final Creator<SavedState> CREATOR = new Creator<SavedState>() {
@Override
public SavedState createFromParcel(Parcel in) {
return new SavedState(in);
}
@Override
public SavedState[] newArray(int size) {
return new SavedState[size];
}
};
private String data;
public SavedState(Parcel source) {
super(source);
data = source.readString();
}
public SavedState(Parcelable superState, String data) {
super(superState);
this.data = data;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NonNull Parcel dest, int flags) {
dest.writeString(data);
}
}
Hope this helps.
EditText restores text based on resId. If your edittexts don't have resIds or all have the same resIds you'll have restore problems.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With