在多个膨胀的 EditTexts 上设置文本会导致旋转后全部填充相同的文本

Posted

技术标签:

【中文标题】在多个膨胀的 EditTexts 上设置文本会导致旋转后全部填充相同的文本【英文标题】:Setting text on multiple inflated EditTexts causes all to be populated with same text after rotation 【发布时间】:2014-05-13 00:52:58 【问题描述】:

我正在夸大一些EditTexts 并将它们添加到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);
        

第一次运行后的日志如下所示:

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

并且所有的 EditText 中都有正确的文本(“设置空文本:#”)

-

我正在向我的EditText 添加一个TextChangedListener,然后我会在更改文本时更新Map&lt;String, String&gt;,并将uniqueId 作为键,将注释文本作为值。

在更换其中一个 cmets 后旋转手机时会出现问题。我将 cmets Map 保存在 onSaveInstanceState 内:

@Override
public void onSaveInstanceState(Bundle outState) 
    super.onSaveInstanceState(outState);
    outState.putSerializable(COMMENTS, (Serializable)comments);

然后我在 onActivityCreated 中检索它们

if(savedInstanceState != null) 
        Log.v("into","Retrieving...");
        comments = (Map<String, String>)savedInstanceState.getSerializable(COMMENTS);

我还遍历 Map 并验证是否有一个条目具有正确的 uniqueId 和正确更改的评论文本。

-

接下来我再次拨打setupCommentViews()。这次日志看起来正确,但 EditTexts ALL 与最后一个(最后一个膨胀的)具有相同的文本。

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

但是所有EditTexts 现在都说:“设置空文本:3”

使用ListView 不是我正在寻找的解决方案,因为ListView 内部有很多带有EditTexts 的错误AdjustResize softInputMode

-

谁能解释这里发生了什么?

【问题讨论】:

我收集到您的每个 EditText 组件都有自己的唯一 ID。如果是这样,并且由于您将 ID 保存到地图中,您不能只遍历地图,获取唯一 ID,将其与您的每个 EditText 匹配,然后将结果(即setText)写入每个 EditText 吗? 每个EditText 都有相同的ID (R.id.comment)。由于我们分别对它们进行充气,我认为这没关系,因为我们使用新充气的View v (v.findViewById(R.id.comment)) 获得对它们的引用 即使您单独对它们进行膨胀,当您执行 setText 时,android 也会存储为该 ID 设置的值,因此无论是最后设置的值,它都会显示给您的所有 EditText相同的 ID。 嗯,我不确定,因为第一遍不会导致所有EditTexts 都将“设置空文本:3”作为值吗?而是第一次正确地具有“设置空文本:1”“设置空文本:2”和“设置空文本:3” 在您设置 EditText 的位置插入调试断点并找出答案。 【参考方案1】:

默认情况下,当调用一个activity的onSaveInstanceState方法时,它也会遍历视图层次结构,如果可能的话,尝试保存任何视图的状态,如果稍后调用activity的onRestoreInstanceState方法,则恢复这些视图状态.当调用每个View保存状态时,我们可以在View类中看到这个方法:

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 继承自 TextView,当检查 TextView 的代码时,我们看到 onSaveInstanceState 方法返回当前文本,onRestoreInstanceState 方法返回文本并显示。

现在,回到您的代码。您自己保存文本内容并在调用onCreate时恢复它,但EditText本身也保存文本并在调用Activity的onRestoreInstanceState方法时恢复它肯定是在onCreate之后调用的,还有一件事,所有您的 EditText 具有相同的 id,因此当视图层次结构的状态保存到 SparseArray 中时,最后一个 EditText 状态(在这种情况下是文本内容)将覆盖所有以前的状态。这就是你的问题发生的原因。

解决方案:

    您可以覆盖活动的方法onRestoreInstanceState 而不是调用超级方法,这样您就可以防止视图自动恢复其状态,但我认为这不是一个好主意,因为可能需要做其他事情默认恢复,不仅是你的 EditText 可以将EditText的saveEnabled属性设置为false,这样就不会自己恢复状态了,我推荐这个。

【讨论】:

非常感谢。 android:saveEnabled="false" 做得很好。【参考方案2】:

我遇到了同样的问题。 在我的自定义 LinearLayout 中,我重写了这些方法。

   @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);
    

希望这会有所帮助。

【讨论】:

【参考方案3】:

EditText 根据 resId 恢复文本。如果您的编辑文本没有 resId 或都具有相同的 resId,您将遇到恢复问题。

【讨论】:

【参考方案4】:

我也遇到了这个问题,但是我必须恢复之前的状态。所以我创建了一个名为FixedEditText 的视图并覆盖了dispatchSaveInstanceState()dispatchRestoreInstanceState() 两种方法。就像这样:

 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

【讨论】:

以上是关于在多个膨胀的 EditTexts 上设置文本会导致旋转后全部填充相同的文本的主要内容,如果未能解决你的问题,请参考以下文章

为 UITextView 设置文本会导致重置为默认字体和颜色问题

我将视图膨胀了 5 次,膨胀的视图有一个 EditText,现在我在屏幕上有 5 个 EditText,如何从这些 EditTexts 中获取文本

在 recyclerview 上退出文本会导致焦点问题并修复它会破坏键盘隐藏 EditText 可见性

在 UITableview 内的自动调整大小的标签上设置非常长的文本会破坏 NSLayoutConstraint

更改 TextFormatter 中的文本会导致递归调用

向 PayPal 购物车发送文本会导致出现奇怪的字符