如何在更改屏幕方向时使DatePickerDialog维护选定的用户日期?

问题描述 投票:1回答:3

在打开的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 */ }
}

建议我,如何解决这个问题?

java android orientation-changes datepickerdialog
3个回答
1
投票

正确实现的DialogFragment将能够在没有任何额外工作的情况下恢复状态。代码中的主要问题是非静态内部类。这有缺陷有两个原因:

  1. 非静态内部类自然会保留对其外部类的引用(请参阅#2中的链接)。这实际上是对您的活动,背景,观点的参考;基本上是用户面临的一切。当设备旋转或用户切换到另一个应用程序时,您的应用程序很可能会关闭。但是,保留的片段将保持活跃,这有时非常有用。但是因为在你的情况下它是一个内部类,垃圾收集不允许废弃上下文,活动等,因为仍然存在一个有效的引用。因此,你正在泄漏记忆。如果您在应用程序重新创建后访问此上下文(例如,更新视图),您将收到一个运行时异常,告诉您正在处理过时的上下文/视图。如果你真的想走这条路,你可能想看看WeakReferences。 https://stackoverflow.com/a/10968689/1493269
  2. 非静态内部类危险的另一个原因是它们从不提供默认构造函数。但Android框架要求每个Fragment都有一个空构造函数,以便在幕后实例化它。如果您的应用程序被拆除并重新启动(例如轮换更改),那么Android将通过强制默认构造函数重新实例化您的Fragment,然后通过序列化捆绑包恢复状态。如果您没有默认构造函数:运行时异常。你可以通过保留你的片段来解决这个问题,但我在1)中解释了为什么这也很糟糕。 http://thecodersbreakfast.net/index.php?post/2011/09/26/Inner-classes-and-the-myth-of-the-default-constructor

故事的道德:小心非静态的内部阶级。

你的代码应该如何工作:

activity nek i.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
    }
}

my dialog fragment.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" )
        );
    }
}

请原谅小错误,因为我在没有测试的情况下将其写下来。


2
投票

你的代码看起来很糟糕。

首先,在方法体中声明匿名片段类 - 但是,最好将此类与Activity类分开或使其成为静态内部类。

然后,你setRetainInstance(true),它告诉Android不要在Activity旋转时销毁片段。这是针对其他用例的,你的情况并非如此。

我建议你阅读以下信息:

Handling runtime changes

Activity Lifecycle

How to communicate Activity with Fragments

基本上,如果您按照Android规则玩游戏,大部分用户输入都将保存在设备轮换中,您可以开箱即用(参见上面的链接)。


0
投票

当方向改变时,应用程序实际上被销毁并再次创建。所以你必须在某处保存状态,例如在instantState Bundle中

- 新编辑

我尝试了另一种方法,使用Button中的OnClickListener回调,并保持状态更改方向。

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

        Button button1 = (Button) findViewById(R.id.button1);

        button1.setOnClickListener(new View.OnClickListener() {

            @Override
            public void onClick(View v) {
                // the method that is called 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(getFragmentManager(), null);
            }
        });

    }
© www.soinside.com 2019 - 2024. All rights reserved.