29

画面が回転したときに表示されたままになるように、PreferenceFragment 内のいくつかのカスタム DialogPreference サブクラスを取得するのに問題がありました。PreferenceActivity を使用している場合、この問題は発生していないため、Android のバグなのか、コードの問題なのかわかりませんが、同じような経験があるかどうか誰か確認してほしいです。

これをテストするには、最初に少なくとも 1 つの DialogPreference を含む設定画面を作成します (どのサブクラスでもかまいません)。次に、それを PreferenceActivity に表示します。アプリを実行するときに、DialogPreference を押して、ダイアログが表示されるようにします。次に、向きが変わるように画面を回転させます。ダイアログは表示されたままですか?

次に、同じことを試しますが、PreferenceActivity の代わりに PreferenceFragment を使用して設定を表示します。繰り返しますが、画面を回転させてもダイアログは表示されたままですか?

これまでのところ、PreferenceActivity を使用するとダイアログが表示されたままになりますが、PreferenceFragment を使用すると表示されないことがわかりました。DialogPreference のソース コードを見ると、ダイアログが表示されたままになるのが正しい動作のようです。これは、画面の向き変更で が呼び出されisDialogShowingたときに保存される状態情報であるためです。onSaveInstanceState()したがって、バグが PreferenceFragment (およびその中のすべて) がその状態情報を復元できない可能性があると思います。

これが Android のバグである場合、PreferenceFragment を使用している人は状態情報を保存および復元できないため、広範囲に及ぶ影響があります。

誰か確認してくれませんか?バグではない場合、何が起こっているのでしょうか?

4

2 に答える 2

56

最後に、この問題の解決策を見つけました。これはバグではなく、Android開発者向けドキュメントの問題/見落としであることが判明しました。

ほら、私はここでPreferenceFragmentチュートリアルに従っていました。この記事では、アクティビティ内でPreferenceFragmentをインスタンス化するために、次のことを行うように指示しています。

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Display the fragment as the main content.
        getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
    }
} 

これに伴う問題は、画面の向き(またはアクティビティを破棄して再作成するその他のアクション)を変更すると、PreferenceFragmentが2回作成されるため、状態が失われることです。

最初の作成は、アクティビティの呼び出しsuper.onCreate()(上に表示)を介して行わonActivityCreated()れます。これにより、PreferenceFragment()のonRestoreInstanceState()メソッドと、それに含まれる各Preferenceのメソッドが呼び出されます。これらはすべての状態を正常に復元します。

しかし、returnsを呼び出すと、メソッドがPreferenceFragmentをもう一度作成することsuper.onCreate()がわかります。再び無意味に作成されるため(そして今回は状態情報なしで!)、正常に復元されたばかりの状態はすべて完全に破棄/失われます。これは、アクティビティが破棄されたときに表示されていた可能性のあるDialogPreferenceが、アクティビティが再作成されると表示されなくなる理由を説明しています。onCreate()

それで、解決策は何ですか?さて、次のように、PreferenceFragmentがすでに作成されているかどうかを判断するための小さなチェックを追加するだけです。

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Fragment existingFragment = getFragmentManager().findFragmentById(android.R.id.content);
        if (existingFragment == null || !existingFragment.getClass().equals(SettingsFragment.class))
        {
            // Display the fragment as the main content.
            getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
        }
    }
}

onCreate()または、別の方法は、次のように、状態を復元することを意図しているかどうかを単に確認することです。

public class SettingsActivity extends Activity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        if (savedInstanceState == null)
        {
            // Display the fragment as the main content.
            getFragmentManager().beginTransaction()
                .replace(android.R.id.content, new SettingsFragment())
                .commit();
        }
    }
}

onCreate()したがって、ここで学んだ教訓は、アクティビティを初めて設定することも、以前の状態から復元することもできるという2つの役割があることだと思います。

ここでの答えは、私がこの解決策を実現することにつながりました。

于 2013-01-13T13:36:54.507 に答える
0

私は確かにこの問題を自分で抱えていました。DialogFragmentがnull であるために状態が復元されないというバグがあります。または、少なくともそれは私に起こりました。

複数のソースを使用して、最終的にソリューションが機能するようになりました。ダイアログにこれを拡張させますBaseDialogFragment

import android.app.Dialog;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.app.DialogFragment;

import com.actionbarsherlock.app.SherlockDialogFragment;

public class BaseDialogFragment extends DialogFragment {

    @Override
    public void onCreate(Bundle savedInstanceState)
    {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;

        setRetainInstance(true);
        Log.d("TAG", "saved instance state oncreate: "
                + WorkaroundSavedState.savedInstanceState);
        super.onCreate(savedInstanceState);
    }

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState)
    {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;
        Log.d("TAG", "saved instance state oncretaedialog: "
                + WorkaroundSavedState.savedInstanceState);

        return super.onCreateDialog(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        if (savedInstanceState == null || savedInstanceState.isEmpty())
            savedInstanceState = WorkaroundSavedState.savedInstanceState;

        Log.d("TAG", "saved instance state oncretaeview: "
                + WorkaroundSavedState.savedInstanceState);

        return super.onCreateView(inflater, container, savedInstanceState);
    }

    @Override
    public void onDestroyView() // necessary for restoring the dialog
    {
        if (getDialog() != null && getRetainInstance())
            getDialog().setOnDismissListener(null);

        super.onDestroyView();
    }

    @Override
    public void onSaveInstanceState(Bundle outState)
    {
        // ...

        super.onSaveInstanceState(outState);
        WorkaroundSavedState.savedInstanceState = outState;
        Log.d("TAG", "saved instance state onsaveins: "
                + WorkaroundSavedState.savedInstanceState);

    }

    @Override
    public void onDestroy()
    {
        WorkaroundSavedState.savedInstanceState = null;
        super.onDestroy();
    }

    /**
     * Static class that stores the state of the task across orientation
     * changes. There is a bug in the compatibility library, at least as of the
     * 4th revision, that causes the save state to be null in the dialog's
     * onRestoreInstanceState.
     */
    public static final class WorkaroundSavedState {
        public static Bundle savedInstanceState;
    }
}

savedInstanceStateメソッドにパラメーターがあるサブクラスでは、 で super を呼び出す必要がある場合があることに注意してくださいWorkaroundSavedState.savedInstanceState。そして、状態を復元するとき (つまり、onCreate()を無視してsavedInstanceState、代わりに を使用します。WorkaroundSavedState.savedInstanceState静的ホルダーは最もクリーンなソリューションではありませんが、機能します。.onDestroy()

いずれにせよ、DialogFragment画面を回転させても私のは消えません(そしてそれはありませんconfigChanges)。このコードで問題が解決するかどうかお知らせください。問題が解決しない場合は、状況を確認いたします。また、これをテストしていないことに注意してください。PreferenceFragment代わりにFragment、互換性クラスまたはからの他のActionBarSherlock.

于 2013-01-10T05:37:54.440 に答える