Подтвердить что ты не робот

Почему EditText в пользовательском составном представлении повторно использует текст, введенный в другой составной экземпляр вида?

Я пытаюсь написать настраиваемый составной вид, состоящий из TextView и EditText, _compound_view.xml _:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/compoundText"
android:layout_width="match_parent"
android:layout_height="wrap_content" >

<TextView
    android:id="@+id/textLabel"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Label" />

<EditText
    android:id="@+id/textEdit"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="enter text here" >
</EditText>

и это класс, расширяющий LinearLayout:

public class CompoundView extends LinearLayout {

public CompoundView(Context context, AttributeSet attrs) {
    super(context, attrs);

    readAttributes(context, attrs);
    init(context);
}

public CompoundView(Context context) {
    super(context);

    init(context);
}

private void init(Context c) {

    final LayoutInflater inflater = (LayoutInflater) c
            .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.compound_view, this);

}
   }

Теперь, если я использую 2 из этих View в своем _activity_main.xml _:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" >
<it.moondroid.compoundview.example.CompoundView
    android:id="@+id/compoundview1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentTop="true" />

<it.moondroid.compoundview.example.CompoundView
    android:id="@+id/compoundview2"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_below="@id/compoundview1" />
</RelativeLayout>

а в коде действия я только раздуваю RelativeLayout, не управляя onSaveInstanceState:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

}

тогда, когда я пишу что-то во втором EditText и вращаю свое устройство, в EditText первого пользовательского View появляется тот же text.

Почему такое поведение происходит?

EDIT:

Я решил проблему, удалив android: id и используя android: тег для EditText в составном_view.xml, а затем управляя сохранением состояния EditText в классе CompoundView:

@Override
protected Parcelable onSaveInstanceState() {

    Bundle bundle = new Bundle();
    bundle.putParcelable("instanceState", super.onSaveInstanceState());
    bundle.putString("currentEdit", mEditText.getText().toString());
    bundle.putBoolean("isFocused", mEditText.hasFocus());
    return bundle;

}

@Override
protected void onRestoreInstanceState(Parcelable state) {

    if (state instanceof Bundle) {
        Bundle bundle = (Bundle) state;
        mEditText.setText(bundle.getString("currentEdit"));
        if (bundle.getBoolean("isFocused")) {
            mEditText.requestFocus();
        }
        super.onRestoreInstanceState(bundle.getParcelable("instanceState"));
        return;
    }

    super.onRestoreInstanceState(state);
}
4b9b3361

Ответ 1

Я начну, сказав, что я не подтвердил это... но у меня возникли те же проблемы при использовании сложного представления, похожего на то, что вы делали.

Я думаю, что корень проблемы в том, как Android автоматически сохраняет состояние элементов управления EditText, и я думаю, что он сохраняет его с помощью "id". Поэтому, если у вас есть несколько элементов управления в вашем xml с одинаковым "id", тогда, когда он сохраняет состояние, а затем восстанавливает состояние, все элементы управления с одинаковым идентификатором получают одинаковое значение. Вы можете попробовать это, добавив 2 EditText contols к нормальному xml и дайте им то же самое значение android:id и посмотрите, получится ли оно в результате того же значения.

В вашем случае вы можете попытаться НЕ использовать идентификаторы в составном виде и скорее найти элементы по-другому, либо по тегу (View.findViewWithTag), либо по имени, и посмотреть, не влияет ли это.

В моем случае я решил проблему, выполнив последнее.

Ответ 2

Вам необходимо отключить свойство SaveEnabled EditText с помощью android:saveEnabled="false"

В своем пользовательском представлении вы надуваете макет из XML, для которого определен идентификатор. Android OS имеет функциональность по умолчанию для сохранения и восстановления состояния представления, если для представления определен идентификатор.

Это означает, что он сохранит текст EditText, когда действие будет приостановлено, и восстановит автоматически, когда восстановление будет восстановлено. В вашем случае вы используете это настраиваемое представление несколько раз, и это приводит к тому, что все ваши EditText имеют одинаковый идентификатор. Теперь, когда активность будет приостановлена, Android будет извлекать значение EditText и будет сохранять против их идентификатора, но, поскольку у вас одинаковый идентификатор для каждого EditText, значения будут переопределены, и поэтому он восстановит одно и то же значение во всем вашем EditText.

Ответ 3

Взгляните на мой комментарий в своем вопросе, а также убедитесь, что вы правильно ссылаетесь на свои взгляды.

Я использую ваш код следующим образом:

CompoundView cv1 = (CompoundView) findViewById(R.id.compoundview1);
TextView tv1 = (TextView) cv1.findViewById(R.id.textEdit);

CompoundView cv2 = (CompoundView) findViewById(R.id.compoundview2);
TextView tv2 = (TextView) cv2.findViewById(R.id.textEdit);

Ответ 4

В случае, если у вас есть более сложное составное представление с множеством дочерних представлений, вы можете рассмотреть возможность переопределения dispatchSaveInstanceState вашего самого внешнего класса ViewGroup и не вызывать супер реализацию.

Как это:

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    //If you don't call super.dispatchRestoreInstanceState(container) here,
    //no child view gets its state saved
}

Я использую это, потому что в моем случае у меня есть иерархия многих различных составных представлений, которые имеют общий суперкласс, в котором я сделал это переопределение. (Я постоянно забывал установить для атрибута SaveEnabled значение false для новых макетов.) Однако есть много предостережений, например, вам необходимо вручную сохранить сфокусированный вид и затем запросить его фокус, чтобы ваше приложение не работало странно при повороте экрана.

РЕДАКТИРОВАТЬ: Если вам действительно нужно сохранить состояние вашего составного представления, переопределение dispatchSaveInstanceState с пустым телом приведет к тому, что onSave/RestoreInstanceState не будет вызываться. Если вы хотите использовать их и по-прежнему не сохранять состояние какого-либо из дочерних представлений, вам необходимо вызвать super.dispatchFreezeSelfOnly(container) в вашем переопределении.

Подробнее об этом здесь: http://charlesharley.com/2012/programming/views-saving-instance-state-in-android

Ответ 5

У меня такая же раздражающая проблема, и я решил ее другим решением, чем предлагалось. Когда пользовательский вид завышен, я не нахожу их с идентификаторами или тегами, я получаю их с помощью метода getChildAt. Мне не нужно было сохранять состояние экземпляра.

Вот пример:

Пользовательский XML для раздувания (custom_view.xml):

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >
    <TextView
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"
        android:textAppearance="?android:attr/textAppearanceMedium"/>
    <EditText
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:gravity="center"/>
</merge>

Затем вы просто получаете представления таким образом во время инициализации представления:

private void initView(Context context) {
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.custom_view, this, true);

    setOrientation(LinearLayout.HORIZONTAL);
    setGravity(Gravity.CENTER);

    TextView mTitleTV = (TextView) getChildAt(0);
    EditText mContentET = (EditText) getChildAt(1);
}

ВАЖНО: вы должны удалить все идентификаторы из XML