Почему 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);
}
Ответы
Ответ 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