【问题标题】:How to make DatePickerDialog maintaining selected user date when changing screen orientation?更改屏幕方向时如何使 DatePickerDialog 保持选定的用户日期?
【发布时间】:2015-02-04 16:34:14
【问题描述】:

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

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

【问题讨论】:

  • 你应该关闭它并重新打开它
  • 以及如何在转动屏幕(关闭并重新打开对话框)之前获取用户设置的日期?
  • 之前有过类似的问题。这里提供的解决方案看起来很优雅stackoverflow.com/a/15444485/2841101
  • 如果您正在调用 setRetainInstance( true )(您显然会这样做),这实际上应该是开箱即用的。但是你应该非常小心非静态内部类,特别是如果你保留实例! stackoverflow.com/a/10968689/1493269
  • 此外,Drew 的建议实际上是正确的。 setRetaininstance 甚至没有必要。 DialogFragment 能够自行恢复选定的时间!

标签: java android orientation-changes datepickerdialog


【解决方案1】:

你的代码看起来很糟糕。

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

然后,你 setRetainInstance(true),它告诉 Android 不要在 Activity 旋转时销毁 Fragment。这适用于其他用例,您的情况并非如此。

我建议您阅读以下信息:

Handling runtime changes

Activity Lifecycle

How to communicate Activity with Fragments

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

【讨论】:

    【解决方案2】:

    正确实施的 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

    故事的寓意:小心非静态内部类。

    您的代码应该如何工作:

    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" )
            );
        }
    }
    

    请原谅小错误,因为我没有测试就写下了这个。

    【讨论】:

      【解决方案3】:

      当方向改变时,应用程序实际上被销毁并再次创建。所以你必须将状态保存在某处,例如在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);
                  }
              });
      
          }
      

      【讨论】:

      • 你必须重写 onSaveInstanceState 方法。看developer.android.com/training/basics/activity-lifecycle/…
      • 是的,但我不知道如何在转动屏幕之前获取用户设置的数据?我知道我必须将这些数据放在 onSaveInstanceState 中,我这样做是为了在 Activity 中节省时间,如您所见
      • 但是你是在活动上做的,而不是在对话框上
      • 是的,因为我不知道如何获取我描述的数据... :(
      猜你喜欢
      • 1970-01-01
      • 2018-05-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-12-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多