简体   繁体   English

Android Compound Custom View恢复保存状态

[英]Android Compound Custom View restore save state

I created compound custom view which contains TextView and EditText called LabledEditText , since I will have a lot of EditText fields in a fragment. 我创建了复合自定义视图,其中包含名为LabledEditText TextViewEditText ,因为我在片段中会有很多EditText字段。

I have created an XML file that holds the following 我创建了一个包含以下内容的XML文件

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/linearLayoutLabeledEditText"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <TextView
        android:id="@+id/label_textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="@string/label"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <EditText
        android:id="@+id/value_editText"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:freezesText="true"
        android:saveEnabled="true"/>
</LinearLayout>

and in the View class is as following 在View类中如下

public class LabeledEditText extends LinearLayout {
    private EditText editText;
    private TextView label;

    public LabeledEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        inflate(context,R.layout.labeled_edit_text, this);
        label = (TextView) this.findViewById(R.id.label_textView);
        label.setText("some label");
        editText = (EditText) this.findViewById(R.id.value_editText);
    }

    protected void onRestoreInstanceState(Parcelable state) {
        String id= this.getId()+" ";
        if (state instanceof Bundle) // implicit null check
        {
            Bundle bundle = (Bundle) state;
            state = bundle.getParcelable(id+"super");
            super.onRestoreInstanceState(state);
            editText.setText(bundle.getString(id+"editText"));
        }
    }

    protected Parcelable onSaveInstanceState() {
        String id= this.getId()+" ";
        Bundle bundle = new Bundle();
        bundle.putParcelable(id+"super",super.onSaveInstanceState());
        bundle.putString(id+"editText",editText.getText().toString());
        return bundle;
    }   
}

then I use it in a 3 fragments that represent 3 steps. 然后我在代表3个步骤的3个片段中使用它。 When I insert values in the first 1 step/fragment 当我在前一步/片段中插入值时

第一次访问第一步后

then switch to other fragments and return to the 1 step/fragment again I find the following 然后切换到其他片段并再次返回1步/片段我发现以下内容

第二次访问第一步

what is causing this problem ? 是什么导致了这个问题?

I have been debugging it for at least 5 days, keeping in mind that each of those custom views has different id when used inside the fragment layout. 我已经调试了至少5天,请记住,在片段布局中使用时,每个自定义视图都有不同的ID。

I also have tried to add the id of the custom view as part of the key during saving the state this.getId()+"editText" still the same problem. 我还试图在保存状态时添加自定义视图的id作为键的一部分this.getId()+"editText"仍然是同样的问题。

EDIT the genrateViewId for api < 17 编辑 genrateViewId for api <17

the code after alteration 改变后的代码

import java.util.concurrent.atomic.AtomicInteger;

public class LabeledEditText extends LinearLayout {
    private EditText editText;
    private TextView label;

    public LabeledEditText(Context context, AttributeSet attrs) {
        super(context, attrs);
        inflate(context,R.layout.labeled_edit_text, this);
        label = (TextView) this.findViewById(R.id.label_textView);
        editText = (EditText) this.findViewById(R.id.value_editText);
        editText.setId(generateViewId());
        applyAttr(context,attrs);
    }

    @Override
    protected Parcelable onSaveInstanceState() {
        Bundle bundle = new Bundle();
        //adding the id of the parent view as part of the key so that
        //editText state won't get overwritten by other editText 
        //holding the same id
        bundle.putParcelable("super",super.onSaveInstanceState());
        bundle.putString("editText",editText.getText().toString());
        return bundle;
    }

    @Override
    protected void onRestoreInstanceState(Parcelable state) {
        if (state instanceof Bundle) // implicit null check
        {
            Bundle bundle = (Bundle) state;
            state = bundle.getParcelable("super");
            super.onRestoreInstanceState(state);
            editText.setText(bundle.getString("editText"));
        }
    }

    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);

    public static int generateViewId() {
        for (;;) {
            final int result = sNextGeneratedId.get();
            // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
            int newValue = result + 1;
            if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
            if (sNextGeneratedId.compareAndSet(result, newValue)) {
                return result;
            }
        }
    }
}

Your problem is that you have multiple ViewGroups (LinearLayout), with children having same ids. 您的问题是您有多个ViewGroups(LinearLayout),其子项具有相同的ID。 Therefore when saving state, all of them are being saves in same state, and the last one, overwrites all. 因此,在保存状态时,所有这些状态都将保存在同一状态,而最后一个状态将覆盖所有状态。
To solve this, you have to give each view a unique I'd when you inflate. 要解决这个问题,你必须给每个视图一个独特的,当你膨胀。 In v17 and later you can use View.generateViewId(); 在v17及更高版本中,您可以使用View.generateViewId(); , in older versions you will have to create static ids manually in the ids file. ,在旧版本中,您必须在ids文件中手动创建静态ID。
Your code should look like this; 你的代码应该是这样的;

public LabeledEditText(Context context, AttributeSet attrs) { 
    super(context, attrs); inflate(context,R.layout.labeled_edit_text, this); 
    label = (TextView) this.findViewById(R.id.label_textView); label.setText("some label"); 
    editText = (EditText) this.findViewById(R.id.value_editText); 
    label.setId(View.generateViewId());
    editText.setId(View.generateViewId());
}

In any case it may be better to use static ids, as it would be easier to reference them later. 在任何情况下,使用静态ID可能更好,因为以后更容易引用它们。 You may not even need anymore to overwrite the onSave and onRestore, especially if you use static ids. 你可能甚至不需要覆盖onSave和onRestore,特别是如果你使用静态id。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM