有 Java 编程相关的问题?

你可以在下面搜索框中键入要查询的问题!

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

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

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


共 (3) 个答案

  1. # 1 楼答案

    正确实现的DialogFragment将能够恢复状态,而无需您进行任何额外的工作。代码中的主要问题是非静态内部类。这是有缺陷的,原因有二:

    1. 非静态的内部类自然会保留对其外部类的引用(参见#2中的链接)。这实际上是指你的活动、你的背景、你的观点;基本上,用户所面临的一切。当设备旋转或用户切换到其他应用程序时,您的应用程序很可能会关闭。然而,保留的片段将保持活性,这有时非常有用。但是,由于在您的情况下它是一个内部类,所以不允许垃圾收集对上下文、活动等进行垃圾回收,因为仍然存在一个活动引用。因此,您正在泄漏内存。如果您在应用程序重新创建后访问此上下文(例如更新视图),您将得到一个运行时异常,告诉您您正在处理一个过时的上下文/视图。如果你真的想沿着那条路走,你可能想去看看WeakReference

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

    2. 非静态内部类危险的另一个原因是它们从不提供默认构造函数。但是Android框架要求每个片段都有一个空构造函数,以便在幕后实例化它。如果你的应用程序被删除并重新启动(例如,在旋转更改时),那么Android将通过强制默认构造函数重新实例化你的片段,然后通过序列化包恢复状态。如果没有默认构造函数:运行时异常。你通过保留片段来解决这个问题,但我在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" )
            );
        }
    }
    

    请原谅我的小错误,因为我没有经过测试就把它写在了我的头上

  2. # 2 楼答案

    你的代码看起来很糟糕

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

    然后,您setRetainInstance(true),它告诉Android不要在活动旋转时销毁片段。这是用于其他用例的,而您的用例不是

    我建议您阅读以下信息:

    Handling runtime changes

    Activity Lifecycle

    How to communicate Activity with Fragments

    基本上,如果你按照安卓规则玩游戏,大部分用户输入将在设备旋转时为你保存(见上面的链接)

  3. # 3 楼答案

    当方向改变时,应用程序实际上被销毁并再次创建。因此,您必须将状态保存到某个位置,例如在instantState包中

    新编辑

    我尝试了另一种方法,使用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);
                }
            });
    
        }