【问题标题】:Android compound views render incorrectly when screen orientation changes屏幕方向更改时,Android 复合视图呈现不正确
【发布时间】:2014-01-18 02:02:29
【问题描述】:

对 Android 非常陌生。我有一个创建一些 N 自定义视图的活动。它创建的每个自定义视图都会添加到 Activity LinearLayout 并显示在屏幕上。这工作得很好,直到我旋转屏幕。突然,自定义视图无法正确显示其数据。在我的测试场景中,Activity 创建了我的自定义视图的 2 个实例,第一个显示“名称 1”,而第二个在每个自定义视图的 EditText 中显示“名称 2”。当我切换方向时(这一切都在我的 Nexus 7 上完成),两个自定义视图的 EditText 都说“名称 2”。我在 Activity 和自定义视图的构造函数中使用了 println 语句,以确保仍然传入正确的值,但它只是显示不同。有任何想法吗?这是一些简化的代码:

public class EditTemplateWorkout extends Activity{

private int templateWorkoutId = -1;
private TemplateWorkout templateWorkout;

@SuppressWarnings("unchecked")
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_edit_template);
    Intent intent = this.getIntent();
    templateWorkoutId = intent.getIntExtra(Messages.TEMPLATEWORKOUTID_MESSAGE, -1);                 
    templateWorkout = templateWorkoutId == -1 ? new TemplateWorkout() : LiftDroidDbHelper.TemplateWorkoutService.get(templateWorkoutId, true, this);            
    final EditText editTextName = (EditText) this.findViewById(R.id.editTextName);        
    editTextName.setText(templateWorkout.getName());
    editTextName.addTextChangedListener(new TextWatcher() {
        public void onTextChanged(CharSequence s, int start, int before,
                int count) {                
            if(!s.equals("") ){ 
                String txtVal = editTextName.getText().toString();                  
                templateWorkout.setName(txtVal);
            }
        }
        public void beforeTextChanged(CharSequence s, int start, int count,
                int after) {

        }
        public void afterTextChanged(Editable s) {

        }
    });

    LinearLayout root = (LinearLayout)this.findViewById(R.id.rootLayout);   
    root.removeAllViews();      
    for(TemplateExercise te : templateWorkout.getTemplateExercises()){
        root = (LinearLayout)this.findViewById(R.id.rootLayout);

        System.out.println("activity adding view for "+te.getName());
        EditTemplateExerciseView editTemplateExercise = new EditTemplateExerciseView(te, this, null);                   
        editTemplateExercise.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT));
        root.addView(editTemplateExercise);
    }

    int moot = 3;
}

}

public class EditTemplateExerciseView extends LinearLayout {

TemplateExercise templateExercise;  

public EditTemplateExerciseView(TemplateExercise te, Context context, AttributeSet attrs) {
    super(context, attrs);      
    templateExercise = te;
    setOrientation(LinearLayout.HORIZONTAL);
    LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    inflater.inflate(R.layout.view_edit_template_exercise, this, true);
    TextView txtName = (TextView) this.findViewById(R.id.templateExerciseNameTxt);        
    txtName.setText(templateExercise.getName() + templateExercise.getTemplateExerciseID());        
    System.out.println("templateXercise id ="+te.getTemplateExerciseID());
    System.out.println("set name:"+templateExercise.getName() + ", confirmed val="+txtName.getText());

}//constructor      

}

这是我的简化自定义视图的副本:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android" >

<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Name:"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <EditText
        android:id="@+id/templateExerciseNameTxt"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:ems="10" >

        <requestFocus />
    </EditText> 
</LinearLayout>    
</merge>

这里是截图的链接: http://imgur.com/a/2HqmS

以下是代码呈现良好预期视图时的 prinln 输出结果:

01-17 17:44:49.588: I/System.out(22474): activity adding view for Bench Press
01-17 17:44:49.618: I/System.out(22474): templateXercise id =4
01-17 17:44:49.618: I/System.out(22474): set name:Bench Press, confirmed val=Bench  Press4
01-17 17:44:49.618: I/System.out(22474): activity adding view for Incline Bench
01-17 17:44:49.648: I/System.out(22474): templateXercise id =6
01-17 17:44:49.648: I/System.out(22474): set name:Incline Bench, confirmed val=Incline Bench6

这是屏幕方向发生变化并调用 Activity 的 onCreate 方法时代码的 println 输出结果:

01-17 17:45:31.568: I/System.out(22474): activity adding view for Bench Press
01-17 17:45:31.588: I/System.out(22474): templateXercise id =4
01-17 17:45:31.588: I/System.out(22474): set name:Bench Press, confirmed val=Bench   Press4
01-17 17:45:31.588: I/System.out(22474): activity adding view for Incline Bench
01-17 17:45:31.618: I/System.out(22474): templateXercise id =6
01-17 17:45:31.618: I/System.out(22474): set name:Incline Bench, confirmed val=Incline Bench6
01-17 17:45:31.648: W/IInputConnectionWrapper(22474): showStatusIcon on inactive InputConnection
01-17 17:45:31.808: W/IInputConnectionWrapper(22474): getTextBeforeCursor on inactive InputConnection
01-17 17:45:31.878: W/IInputConnectionWrapper(22474): getTextBeforeCursor on inactive InputConnection
01-17 17:45:31.978: W/IInputConnectionWrapper(22474): getTextBeforeCursor on inactive InputConnection
01-17 17:45:32.178: W/IInputConnectionWrapper(22474): getTextBeforeCursor on inactive InputConnection
01-17 17:45:32.318: W/IInputConnectionWrapper(22474): getTextBeforeCursor on inactive InputConnection
01-17 17:45:32.338: W/IInputConnectionWrapper(22474): getTextBeforeCursor on inactive InputConnection

您可以看到我将预期值传递到第一个自定义视图中,但它显示了第二个自定义视图的值。您在此处看到的代码与视图最初所做的相比已大大简化。任何 android 大师都知道我在这里做错了什么?

【问题讨论】:

    标签: android android-custom-view


    【解决方案1】:

    您需要保存您的 CustomViews 状态,当您更改设备的方向时,android 操作系统会从头开始初始化您的视图,因此请尝试在您的视图方法中实现

    onSaveInstanceState 
    

    将在方向改变之前由 onpause 方法调用,在这里您将保存来自 edittext 的数据

    onRestoreInstanceState
    

    这将在定向完成后调用并将正确的数据添加到您的视图中

    这里有完整的代码:

    How to prevent custom views from losing state across screen orientation changes

    【讨论】:

    • 但是在我的活动中,当方向改变时,我正在清除自定义视图,然后重新添加它们。如果自定义视图已在 xml 中声明,而不是以编程方式声明,那么您的方法是否是必要的?令我惊讶的是,我创建了自定义视图的两个实例,一个用于数据库中的每条记录,当方向改变时,当我清楚地将唯一值传递给它们时,它们每个都具有完全相同的值。
    • 看起来相同的方法适用于我的 Activity wrt 从 onRestoreInstanceState 方法重建我的视图。为什么 onCreate 会弄乱视图但 onRestoreInstanceState 在屏幕方向更改后不会?感谢 Mohammed Momn 的提示。
    • onCreate 方法为活动进行新的初始化,因此它将清除所有变量和视图数据,但如果您声明具有唯一 ID 的视图,Android 操作系统可能会为您的视图保存状态,但如果您使用一个 ID在自定义视图中并使用您的自定义视图两次,android os 将为具有相同子视图 ID 的两个自定义视图放置初始值
    • onRestoreInstanceState 在activity创建时被调用,这里的bunndle对象会保存被销毁activity的最后一个状态,你可以在主题android生命周期中看到更多关于它的信息
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多