Checkboxes all showing the same text after switching orientations
Edit: See the accepted answer. Lesson: Sometimes views will save and restore their state automatically. This happens AFTER onCreate. This can cause the overwriting of stuff you did in onCreate. If you don't have unique ids, all views of a certain kind (in my case textboxes) can be overwritten with the same saved state. (ps: thanks for your help everyone!)
So, I have a simple linear layout and I want to add some views that have checkboxes with images. Everything works fine until I switch the orientation of my android phone. When I do it goes back through the onCreate but this time the checkboxes all end up with the same text. Weirdly, the images appear fine.
My question is: why is it doing this and how can I make it appear like the first time everytime?
In case that makes no sense here's an example: (Edit: It turns out it always shows the last element's text)
What I see at first
[] a *a'开发者_运维百科s image*
[] b *b's image*
[] c *c's image*
[] d *d's image*
Then, after rotating my phone, it redraws
[] d *a's image*
[] d *b's image*
[] d *c's image*
[] d *d's image*
My original code is pretty complex, but i constructed the following that demonstrates the problem.
Main.java:
public class Main extends Activity {
ArrayList<AnswerView> answers = new ArrayList<AnswerView>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView title = (TextView)findViewById(R.id.questionText);
title.setText("This is a test");
HashMap<String, Drawable> answerInfo = new HashMap<String, Drawable>();
Resources res = getResources();
answerInfo.put("a", res.getDrawable(R.drawable.flower_orange));
answerInfo.put("b", res.getDrawable(R.drawable.flower_white));
answerInfo.put("c", res.getDrawable(R.drawable.leaf));
answerInfo.put("d", res.getDrawable(R.drawable.flower_yellow));
setBoxes(answerInfo);
}
private void setBoxes(HashMap<String, Drawable> answerInfo) {
LinearLayout answerList = (LinearLayout)findViewById(R.id.answerlist);
AnswerView cb = null;
//Remove all existing answer views
answerList.removeAllViews();
answers.clear();
//For each possible answer create a answer views
for (String s : answerInfo.keySet()) {
cb = new AnswerView(this, s, answerInfo.get(s));
answers.add(cb);
String text = cb.getText();
answerList.addView(cb);
}
}
}
AnswerView.java
public class AnswerView extends RelativeLayout {
private CheckBox m_checkbox;
private ImageView m_image;
//private Context m_context;
public AnswerView(Context context, String answer, Drawable d) {
super(context);
LayoutInflater inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.image_checkbox, this, true);
m_checkbox = (CheckBox) view.findViewById(R.id.image_checkbox_cb);
m_image = (ImageView) view.findViewById(R.id.image_checkbox_img);
//m_context = context;
m_checkbox.setText(answer);
m_image.setImageDrawable(d);
m_image.setVisibility(VISIBLE);
}
public void setChecked(boolean checked) {
m_checkbox.setChecked(checked);
}
public boolean isChecked() {
return m_checkbox.isChecked();
}
public String getText() {
return m_checkbox.getText().toString();
}
}
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dip">
<TextView android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/questionText"
android:textSize="18sp"/>
<LinearLayout android:orientation="vertical"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/answerlist"/>
<LinearLayout android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center">
<Button
android:layout_width="200dip"
android:layout_height="wrap_content"
android:text="Enter"
android:id="@+id/buttonAnswerEnter"/>
/>
</LinearLayout>
</LinearLayout>
</ScrollView>
image_checkbox.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<CheckBox
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/image_checkbox_cb"></CheckBox>
<ImageView
android:id="@+id/image_checkbox_img"
android:layout_width="100dip"
android:layout_height="100dip"
android:visibility="gone"></ImageView>
</LinearLayout>
Does setting a unique ID for each AnswerView solve your issue? You could achieve that as follows:
private void setBoxes(HashMap<String, Drawable> answerInfo) {
LinearLayout answerList = (LinearLayout)findViewById(R.id.answerlist);
AnswerView cb = null;
//Remove all existing answer views
answerList.removeAllViews();
answers.clear();
//For each possible answer create a answer views
// BEGIN modified code
int counter = 0;
// END modified code
for (String s : answerInfo.keySet()) {
cb = new AnswerView(this, s, answerInfo.get(s));
// BEGIN modified code
cb.setId(counter);
counter++;
// END modified code
answers.add(cb);
String text = cb.getText();
answerList.addView(cb);
}
}
Implement
onSaveInstanceState ()
onRestoreInstanceState ()
in your custom layout AnswerView.
Try as I might, I'm unable to come up with reasoning for why this is occurring (and I would happily upvote anyone who can explain it), but here is a solution. For some reason, Android isn't happy with you adjusting the XML inflated layout so close to its actual inflation in setContentView()
. The solution is to do the dynamic portion of the layout process either in onPostCreate()
or onResume()
, depending on your Activity needs.
I would recommend onResume()
if you can afford it, since onPostCreate()
is not typically suggested for override...however onResume()
may be called to often and may mess up your layout. Here is your example modified to use onPostCreate()
instead.
public class Main extends Activity {
ArrayList<AnswerView> answers = new ArrayList<AnswerView>();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
TextView title = (TextView)findViewById(R.id.questionText);
title.setText("This is a test");
}
@Override
protected void onPostCreate(Bundle savedInstanceState) {
super.onPostCreate(savedInstanceState);
HashMap<String, Drawable> answerInfo = new HashMap<String, Drawable>();
Resources res = getResources();
answerInfo.put("a", res.getDrawable(R.drawable.flower_orange));
answerInfo.put("b", res.getDrawable(R.drawable.flower_white));
answerInfo.put("c", res.getDrawable(R.drawable.leaf));
answerInfo.put("d", res.getDrawable(R.drawable.flower_yellow));
setBoxes(answerInfo);
}
private void setBoxes(HashMap<String, Drawable> answerInfo) {
LinearLayout answerList = (LinearLayout)findViewById(R.id.answerlist);
AnswerView cb = null;
//Remove all existing answer views
answerList.removeAllViews();
answers.clear();
//For each possible answer create a answer views
for (String s : answerInfo.keySet()) {
cb = new AnswerView(this, s, answerInfo.get(s));
answers.add(cb);
String text = cb.getText();
answerList.addView(cb);
}
}
}
Two other things I noticed which may not affect you, but I figured I'd mention:
- If you want your items to always return in the same order,
HashMap.keySet()
is a bad choice. This method returns these values in different orders on different implementations. I would recommend aLinkedHashMap
or aList
if you need the order to always be the same. - At least in this example, it doesn't make sense to clear your collections before setting them again...since this is only done when the Activity is reconstructed anyway. In the larger implementation, however, perhaps this has a larger purpose.
Hope that Helps! Feel free to ignore anything that wasn't useful :)
精彩评论