应用程序在方向更改时保存文本框状态?

时间:2014-09-10 21:20:44

标签: android android-activity

我正在尝试了解Android应用程序的总流量,并且我遇到了我认为是一种奇怪的情况(注意:我对Android编程很新)。

我用一个多行编辑文本字段制作了一个测试应用程序。我在场上写了1234。没有其他任何东西,没有对默认的Eclipse ADT应用程序进行其他更改,也没有在后端专门覆盖任何内容,我改变了方向。 1234仍然存在。然后我点击主页按钮,然后从最近的应用程序打开它。 1234仍然存在。

我对应用程序生命周期的理解是,当按下主页按钮时应用程序已停止并启动,应用程序在应用程序的方向更改时被销毁并创建。如果这是正确的,是否存在某种形式的自动状态保持?我假设我必须从状态包中提取单个变量并自行恢复它们。这不正确吗?

对于经验丰富的Android开发者可能给出的任何解释都会很棒。我一直试图找到合适的答案,但无济于事。

MainActivity.java

package com.example.teststate;

import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;


public class MainActivity extends Activity {

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


    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        return super.onOptionsItemSelected(item);
    }
}

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"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="com.example.teststate.MainActivity" >

    <EditText
        android:id="@+id/editText1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:layout_marginTop="30dp"
        android:ems="10"
        android:inputType="textMultiLine" >

        <requestFocus />
    </EditText>

</RelativeLayout>

Manifest.xml的片段

<application
    android:allowBackup="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_name"
    android:theme="@style/AppTheme" >
    <activity
        android:name=".MainActivity"
        android:label="@string/app_name" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>

谢谢!

2 个答案:

答案 0 :(得分:3)

发生这种情况的原因是,视图通过覆盖View#onSaveInstanceState()View#onRestoreInstanceStance(Parcelable)显式保存并恢复其方向更改状态。以下是 TextView (超强类 EditText )中的实现

@Override
public Parcelable onSaveInstanceState() {
    Parcelable superState = super.onSaveInstanceState();

    // Save state if we are forced to
    boolean save = mFreezesText;
    int start = 0;
    int end = 0;

    if (mText != null) {
        start = getSelectionStart();
        end = getSelectionEnd();
        if (start >= 0 || end >= 0) {
            // Or save state if there is a selection
            save = true;
        }
    }

    if (save) {
        SavedState ss = new SavedState(superState);
        // XXX Should also save the current scroll position!
        ss.selStart = start;
        ss.selEnd = end;

        if (mText instanceof Spanned) {
            Spannable sp = new SpannableStringBuilder(mText);

            if (mEditor != null) {
                removeMisspelledSpans(sp);
                sp.removeSpan(mEditor.mSuggestionRangeSpan);
            }

            ss.text = sp;
        } else {
            ss.text = mText.toString();
        }

        if (isFocused() && start >= 0 && end >= 0) {
            ss.frozenWithFocus = true;
        }

        ss.error = getError();

        return ss;
    }

    return superState;
}

[...]

@Override
public void onRestoreInstanceState(Parcelable state) {
    if (!(state instanceof SavedState)) {
        super.onRestoreInstanceState(state);
        return;
    }

    SavedState ss = (SavedState)state;
    super.onRestoreInstanceState(ss.getSuperState());

    // XXX restore buffer type too, as well as lots of other stuff
    if (ss.text != null) {
        setText(ss.text);
    }

    if (ss.selStart >= 0 && ss.selEnd >= 0) {
        if (mText instanceof Spannable) {
            int len = mText.length();

            if (ss.selStart > len || ss.selEnd > len) {
                String restored = "";

                if (ss.text != null) {
                    restored = "(restored) ";
                }

                Log.e(LOG_TAG, "Saved cursor position " + ss.selStart +
                      "/" + ss.selEnd + " out of range for " + restored +
                      "text " + mText);
            } else {
                Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);

                if (ss.frozenWithFocus) {
                    createEditorIfNeeded();
                    mEditor.mFrozenWithFocus = true;
                }
            }
        }
    }

    if (ss.error != null) {
        final CharSequence error = ss.error;
        // Display the error later, after the first layout pass
        post(new Runnable() {
            public void run() {
                setError(error);
            }
        });
    }
}

如您所见,它将状态保存在Parcelable对象中,然后将其传递给新实例中具有相同android:id的视图,并调用onRestoreInstanceState()在新观点上。如果您创建的自定义视图不包含其他视图,则可能需要覆盖这些方法。

答案 1 :(得分:2)

我无法向您提供代码级解释。但这是android承诺要做的事情之一。您可以查看此Recreating activity

注意:为了让Android系统恢复活动中的视图状态,每个视图必须具有唯一的ID,由android:id属性提供。