1

開いている DatePickerDialog で、画面の向きが変更されると、選択したユーザー データがリセットされました。

(DatePickerDialog は閉じず、選択したデータを保持しません)

コード:

public class ActivityNeki extends FragmentActivity {
    DialogFragment newDF = null;
    private int datY, datM, datD;

    @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);
        if(savedInstanceState == null){ setTheData(); writeTheData(); }
    }

    @Override protected void onSaveInstanceState(Bundle outState) {
        outState.putInt("izY", datY); outState.putInt("izM", datM); outState.putInt("izD", datD);
        super.onSaveInstanceState(outState);
    }
    @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState);
        datY = savedInstanceState.getInt("izY"); datM = savedInstanceState.getInt("izM"); datD = savedInstanceState.getInt("izD");
        writeTheData();
    }

    public void onClickOpenDPD(View view) {    // the method that is caled from XML onClick
        class MyDialogFragment extends DialogFragment {
            @Override public void onDestroyView() {
                if (getDialog() != null && getRetainInstance()) getDialog().setDismissMessage(null);
                super.onDestroyView();
            }
            @Override public void onCreate(Bundle state) { super.onCreate(state);
                setRetainInstance(true);
            }
            @Override public Dialog onCreateDialog(Bundle state) {
                DatePickerDialog dpd = new DatePickerDialog( getActivity(), new DatePickerDialog.OnDateSetListener() {
                    @Override public void onDateSet(DatePicker view, int leto, int mesec, int dan) {
                        datY = leto; datM = mesec; datD = dan;
                        writeTheData();
                } }, datY, datM, datD);
                return dpd;
            }
        }
        newDF = new MyDialogFragment();
        newDF.show( getSupportFragmentManager(), null );
    }
    public void setTheData(){
        Calendar c = Calendar.getInstance();
        datY = c.get(Calendar.YEAR); datM = c.get(Calendar.MONTH); datD = c.get(Calendar.DAY_OF_MONTH);
    }
    public void writeTheData(){  /* writes the data in a txtView */ }
}

この問題を解決するにはどうすればよいですか?

4

3 に答える 3

2

あなたのコードはかなり悪いようです。

まず、メソッド本体内で無名フラグメント クラスを宣言します。ただし、このクラスをクラスから分離Activityするか、静的内部クラスにする方がよいでしょう。

次に、setRetainInstance(true)アクティビティのローテーション時にフラグメントを破棄しないように Android に指示します。これは他のユースケースを対象としていますが、あなたのケースはそうではありません。

次の情報を読むことをお勧めします。

実行時の変更の処理

アクティビティのライフサイクル

アクティビティをフラグメントと通信する方法

基本的に、Android のルール (上記のリンクを参照) に従ってプレイする場合、すぐに使用できるように、デバイスのローテーション時にユーザー入力の多くが保存されます。

于 2015-02-05T10:29:59.663 に答える
1

適切に実装された DialogFragment は、ユーザー側で追加作業を行わなくても状態を復元できます。コードの主な問題は、非静的内部クラスです。これには 2 つの理由で欠陥があります。

  1. 非静的内部クラスは、当然その外部クラスへの参照を保持します (#2 のリンクを参照)。これは実際には、あなたの活動、コンテキスト、ビューへの参照です。基本的に、ユーザーが直面しているすべてのもの。デバイスがローテーションされるか、ユーザーが別のアプリに切り替えると、アプリがシャットダウンされる可能性が高くなります。ただし、保持されたフラグメントは存続するため、非常に役立つ場合があります。しかし、それはあなたのケースでは内部クラスであるため、アクティブな参照がまだ存在するため、ガベージ コレクションはコンテキストやアクティビティなどを破棄できません。したがって、メモリリークが発生しています。また、アプリケーションの再作成後にこのコンテキストにアクセスすると (たとえば、ビューを更新するために)、古いコンテキスト/ビューを扱っていることを示す実行時例外が発生します。あなたはチェックアウトしたいかもしれませんWeakReferenceあなたが本当にその道を進みたいのなら。

    https://stackoverflow.com/a/10968689/1493269

  2. 非静的内部クラスが危険であるもう 1 つの理由は、デフォルトのコンストラクターが提供されないことです。しかし、Android フレームワークでは、バックグラウンドでインスタンス化するために、すべての Fragment に空のコンストラクターが必要です。アプリが取り壊されて再起動された場合 (ローテーションの変更など)、Android は強制的なデフォルト コンストラクターを介して Fragment を再インスタンス化し、後でシリアル化されたバンドルを介して状態を復元します。デフォルトのコンストラクターがない場合: 実行時例外。フラグメントを保持することでこれを回避できますが、1) でこれがなぜ悪いのかを説明しました。

    http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor

話の教訓: 非静的内部クラスには注意してください。

コードがどのように機能する:

ActivityNeki.java

public class ActivityNeki extends FragmentActivity implements DatePickerDialog.OnDateSetListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity);

        Fragment fragment = getSupportFragmentManager().findFragmentByTag( "my_dialog_tag" );

        if( fragment != null )
        {
            ( (MyDialogFragment) fragment ).listener = this;
        }
    }

    // Called from xml
    public void onClickOpenDPD(View view)
    {    
       MyDialogFragment.newInstance( x, x, x, this ).show( getSupportFragmentManager(), "my_dialog_tag" );
    }

    public void onDateSet(DatePicker view, int year, int monthOfYear, int dayOfMonth)
    {
        // Do your crazy callback stuff
    }
}

MyDialogFragment.java

class MyDialogFragment extends DialogFragment
{
    public static MyDialogFragment newInstance( int datY, int datM, int datD, DatePickerDialog.OnDateSetListener listener )
    {
        Bundle bundle = new Bundle( 3 );
        bundle.putInt( "y", datY );
        bundle.putInt( "m", datM );
        bundle.putInt( "d", datD );

        MyDialogFragment fragment = new MyDialogFragment();
        fragment.setArguments( bundle );
        fragment.listener = listener;
        return fragment;
    }

    public DatePickerDialog.OnDateSetListener listener = null;

    // Not entirely sure if this is still necessary    
    @Override
    public void onDestroyView()
    {
        if(getDialog() != null && getRetainInstance())
        {
            getDialog().setDismissMessage(null);
            super.onDestroyView();
        }
    }

    @Override
    public Dialog onCreateDialog(Bundle state)
    {
        return new DatePickerDialog(
            getActivity(),
            listener,
            getArguments().getInt( "y" ),
            getArguments().getInt( "m" ),
            getArguments().getInt( "d" )
        );
    }
}

テストせずに頭のてっぺんから書いたので、軽微なエラーをお許しください。

于 2015-02-05T16:34:29.280 に答える