简体   繁体   English

对话框打开时如何处理屏幕方向变化?

[英]How do I handle screen orientation changes when a dialog is open?

I have an android app which is already handling changes for orientation, ie there is a android:configChanges="orientation" in the manifest and an onConfigurationChange() handler in the activity that switches to the appropriate layout and preps it.我有一个 android 应用程序,它已经在处理方向更改,即清单中有一个android:configChanges="orientation" ,活动中有一个onConfigurationChange()处理程序,它切换到适当的布局并准备它。 I have a landscape / portrait version of the layout.我有布局的横向/纵向版本。

The problem I face is that the activity has a dialog which could be open when the user rotates the device orientation.我面临的问题是该活动有一个对话框,当用户旋转设备方向时该对话框可能会打开。 I also have a landscape / portrait version of the dialog.我也有对话框的横向/纵向版本。

Should I go about changing the layout of the dialog on the fly or perhaps locking the activity's rotation until the user dismisses the dialog.我应该着手更改对话框的布局还是锁定活动的旋转直到用户关闭对话框。

The latter option of locking the app appeals to me since it saves having to do anything special in the dialog.锁定应用程序的后一种选择对我很有吸引力,因为它无需在对话框中执行任何特殊操作。 I am supposing that I might disable the orientation when a dialog opens, such as我假设我可能会在打开对话框时禁用方向,例如

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);

and then when it dismisses然后当它解散时

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR);

Would that be a sensible thing to do?那会是明智的做法吗? If the screen orientation did change while it was locked, would it immediately sense the orientation change when it was unlocked?如果屏幕方向在锁定时确实发生了变化,那么在解锁时它会立即感知到方向变化吗?

Are there alternatives?有其他选择吗?

I would recommend not turning off the screen rotation, instead of this handle the configuration changes for the Dialog. 我建议不要关闭屏幕旋转,而不是这个处理对话框的配置更改。 You could use one of these two approach for this: 您可以使用以下两种方法之一:

The first one is using a flag variable in onSaveInstanceState(outState) method, and restore the dialog onCreate(bundle) method: 第一个是在onSaveInstanceState(outState)方法中使用flag变量,并恢复对话onCreate(bundle)方法:

in this example my flag variable is called 'isShowing Dialog', when the onCreate method is called by the android System for first time, the bundle argument will be null and nothing happens. 在这个例子中,我的标志变量被称为'isShowing Dialog',当第一次由android系统调用onCreate方法时,bundle参数将为null并且没有任何反应。 However when the activity it's recreated by a configuration change (screen rotation), the bundle will have the boolean value isShowing Dialog, previously saved by the inSaveInstanceState(...) method, so if the variable gets true the dialog is created again, the trick here is set the flag in true when the dialog get showing, and false when it's not, is a little but simple trick. 但是,当通过配置更改(屏幕旋转)重新创建活动时,该包将具有以前由inSaveInstanceState(...)方法保存的布尔值isShowing Dialog,因此如果变量为true,则会再次创建对话框,这里的技巧是在对话框显示时将标志设置为true,而在不显示时将其设置为false,这是一个简单但很简单的技巧。

Class MyClass extends Activity {
    Boolean isShowingDialog = false;
    AlertDialog myDialog;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        if(savedInstanceState!=null){
            isShowingDialog = savedInstanceState.getBoolean("IS_SHOWING_DIALOG", false);
            if(isShowingDialog){
                createDialog();
            }
        }

    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        outState.putBoolean("IS_SHOWING_DIALOG", isShowingDialog);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onPause() {
        if(myDialog!=null && myDialog.isShowing()) {
            myDialog.dismiss();
        }
    }

    private void createDialog() {
        AlertDialog.Builder dialog_builder = new AlertDialog.Builder(this);
        dialog_builder.setTitle("Some Title"):
        ... more dialog settings ...

        myDialog = dialog_builder.create();
        myDialog.show();
        isShowingDialog = true;
    }

    private void hideDialog(){
        myDialog.dismiss();
        isShowingDialog = false;
    }
}

The second approach is to use the ability of the fragments components to retain its states, the main idea is create the dialog inside a fragment, there is the problem about detach and reattach the fragment during the configuration changes (because you need dismiss and show the dialog correctly), but the solution is very similar to the first approach. 第二种方法是使用片段组件保持其状态的能力,主要思想是在片段内创建对话框,在配置更改期间存在分离和重新附加片段的问题(因为您需要关闭并显示对话框正确),但解决方案与第一种方法非常相似。 The advantage of this approach is that if you have an AlertDialog with a couple of configurations, when the fragment is recreated there is not needed to create and setting up the dialog again, only make it show() and the AlertDialog state is maintained by the fragment. 这种方法的优点是,如果你有一个带有几个配置的AlertDialog,当重新创建片段时,不需要再次创建和设置对话框,只需将它设为show()并且由此保持AlertDialog状态。分段。

I hope this helps. 我希望这有帮助。

I suggest your Dialog should override onSaveInstanceState() and onRestoreInstanceState(Bundle) to save its state into a Bundle. 我建议您的Dialog应该覆盖onSaveInstanceState()onRestoreInstanceState(Bundle)以将其状态保存到Bundle中。

You then override those methods in your Activity, checking if the Dialog is shown and if so - calling the dialog's methods to save and restore it's state. 然后,在Activity中覆盖这些方法,检查是否显示Dialog,如果是,则调用对话框的方法来保存和恢复它的状态。

If you are displaying this dialog from a fragment, you will want to override OnActivityCreated(Bundle) instead of OnRestoreInstanceState . 如果要从片段显示此对话框,则需要覆盖OnActivityCreated(Bundle)而不是OnRestoreInstanceState

For a source example see the built-in clock app provided with Android, where the SetAlarm Activity handles the TimePickerDialog this way. 有关源示例,请参阅随Android提供的内置时钟应用程序,其中SetAlarm活动以这种方式处理TimePickerDialog。

If you are handling orientation changes yourself, then here is an approach. 如果您自己处理方向更改,那么这是一种方法。

I won't claim that this is an elegant solution, but it works: 我不会声称这是一个优雅的解决方案,但它的工作原理:

You can keep track of whether the dialog has an active instance inside the dialog class itself, by using a static variable activeInstance, and overriding onStart() to set activeInstance = this and onCancel() to set activeInstance = null. 您可以通过使用静态变量activeInstance跟踪对话框中是否具有活动实例,并覆盖onStart()以设置activeInstance = this和onCancel()以设置activeInstance = null。

Provide a static method updateConfigurationForAnyCurrentInstance() that tests that activeInstance variable and, if non-null, invokes a method activeInstance.reInitializeDialog(), which is a method that you will write to contain the setContentView() call plus the code that wires the handlers for the dialog controls (button onClick handlers, etc. - this is code that would normally appear in onCreate()). 提供一个静态方法updateConfigurationForAnyCurrentInstance(),用于测试activeInstance变量,如果非null,则调用方法activeInstance.reInitializeDialog(),这是一个方法,您将编写包含setContentView()调用以及连接处理程序的代码对于对话框控件(按钮onClick处理程序等 - 这是通常出现在onCreate()中的代码)。 Following that, you would restore any displayed data to those controls (from member variables in your dialog object). 之后,您可以将任何显示的数据还原到这些控件(来自对话框对象中的成员变量)。 So, for example, if you had a list of items to be viewed, and the user were viewing item three of that list before the orientation change, you would re-display that same item three at the end of updateConfigurationForAnyCurrentInstance(), right after re-loading the controls from the dialog resource and re-wiring the control handlers. 因此,例如,如果您有一个要查看的项目列表,并且用户在方向更改之前查看该列表的第三项,您将在updateConfigurationForAnyCurrentInstance()结束时重新显示相同的项目三从对话框资源重新加载控件并重新连接控制处理程序。

You would then call that same reInitializeDialog() method from onCreate(), right after super.onCreate(), and place your onCreate()-specific initialization code (eg, setting up the list of items from which the user could choose, as described above) after that call. 然后,您可以在super.onCreate()之后立即从onCreate()调用相同的reInitializeDialog()方法,并放置onCreate()特定的初始化代码(例如, 设置用户可以选择的项目列表,如如上所述)。

This will cause the appropriate resource (portrait or landscape) for the dialog's new orientation to be loaded (provided that you have two resources defined having the same name, one in the layout folder and the other in the layout-land folder, as usual). 这将导致加载对话框新方向的相应资源(纵向或横向)(前提是您有两个定义了相同名称的资源,一个在布局文件夹中,另一个在layout-land文件夹中,如常) 。

Here's some code that would be in a class called YourDialog: 这里有一些名为YourDialog的代码:

ArrayList<String> listOfPossibleChoices = null;
int currentUserChoice = 0;

static private YourDialog activeInstance = null;

@Override
protected void onStart() {
  super.onStart();
  activeInstance = this;
}

@Override
public void cancel() {
  super.cancel();
  activeInstance = null;
}


static public void updateConfigurationForAnyCurrentInstance() {
    if(activeInstance != null) {
        activeInstance.reInitializeDialog();
        displayCurrentUserChoice();
    }
}

private void reInitializeDialog() {
  setContentView(R.layout.your_dialog);
  btnClose = (Button) findViewById(R.id.btnClose);
  btnClose.setOnClickListener(this);
  btnNextChoice = (Button) findViewById(R.id.btnNextChoice);
  btnNextChoice.setOnClickListener(this);
  btnPriorChoice = (Button) findViewById(R.id.btnPriorChoice);
  btnPriorChoice.setOnClickListener(this);
  tvCurrentChoice = (TextView) findViewById(R.id.tvCurrentChoice);
}

private void displayCurrentUserChoice() {
  tvCurrentChoice.setText(listOfPossibleChoices.get(currentUserChoice));
}

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    reInitializeDialog();
    listOfPossibleChoices = new ArrayList<String>();
    listOfPossibleChoices.add("One");
    listOfPossibleChoices.add("Two");
    listOfPossibleChoices.add("Three");
    currentUserChoice = 0;
    displayCurrentUserChoice();
}

@Override
public void onClick(View v) {
    int viewID = v.getId();

    if(viewID == R.id.btnNextChoice) {
      if(currentUserChoice < (listOfPossibleChoices.size() - 1))
        currentUserChoice++;
        displayCurrentUserChoice();
      }
    }
    else if(viewID == R.id.btnPriorChoice) {
      if(currentUserChoice > 0) {
        currentUserChoice--;
        displayCurrentUserChoice();
      }
    }
    Etc.

Then, in your main activity's onConfigurationChanged() method, you would just invoke YourDialog.updateConfigurationForAnyCurrentInstance() whenever onConfigurationChanged() is called by the OS. 然后,在主活动的onConfigurationChanged()方法中,只要OS调用onConfigurationChanged(),您就会调用YourDialog.updateConfigurationForAnyCurrentInstance()。

Doesn't seem the title was ever resolved (Google Necro Direct).标题似乎从未解决过(Google Necro Direct)。

Here is the solution, matching the request.这是解决方案,符合要求。

When your activity is created, log the screen orientation value.创建活动时,记录屏幕方向值。 when onConfiguration change is called on your activity, compare the orientation values.当在您的活动上调用 onConfiguration change 时,比较方向值。 if the values don't match, fire off all of your orientation change listeners, THEN record the new orientation value.如果值不匹配,则触发所有方向更改侦听器,然后记录新的方向值。

Here is some constructive code to put in your activity (or any object that can handle configuration change events)这里有一些建设性的代码可以放入您的活动(或任何可以处理配置更改事件的对象)

int orientation; // TODO: record orientation here in your on create using Activity.this.getRequestedOrientation() to initialize!

public int getOrientation(){return orientation;}
public interface OrientationChangeListener {
    void onOrientationChange();
}
Stack<OrientationChangeListener> orientationChangeListeners = new Stack<>();
public void addOrientationChangeListener(OrientationChangeListener ocl){ ... }
public void removeOrientationChangeListener(OrientationChangeListener ocl){ ... }

That's the basic environment.这就是基本环境。 Here's your executive:这是你的主管:

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
    if (orientation != newConfig.orientation)
        for (OrientationChangeListener ocl:orientationChangeListeners) ocl.onOrientationChange();
    orientation = newConfig.orientation;
}

In YOUR code model, you may need to send the new configuration, with the event, or the two orientation values with the event.在您的代码模型中,您可能需要随事件发送新配置,或随事件发送两个方向值。 However, Activity.this.getOrientation().= Activity.this.getRequestedOrientation() during event handling (because we are in a logical state of change between two logical values).但是,在事件处理期间, Activity.this.getOrientation().= Activity.this.getRequestedOrientation() (因为我们处于两个逻辑值之间变化的逻辑状态)。

In review of my post, i have determined that there could be some synchronization issues, with multiple events, This is not a fault of this code, but a fault of "Android Platform" for not having defacto orientation sense handlers on every window.在查看我的帖子时,我确定可能存在一些同步问题,有多个事件,这不是此代码的错误,而是“Android 平台”的错误,因为每个窗口都没有事实上的方向感处理程序。 thusly trashing the polymorphic benefits of using java in the first place..因此首先破坏了使用 java 的多态优势。

See the answer from Viktor Valencia above.请参阅上面 Viktor Valencia 的回答。 That will work perfectly with the slight adjustment that you move the createDialog() to onResume.这将与您将 createDialog() 移动到 onResume 的轻微调整完美配合。

@Override
protected void onResume() {
    super.onResume();
    if(isShowingDialog){
        createDialog();
    }
}

Fetch the boolean isShowingDialog value at onCreate, as suggested, but wait for onResume to display the dialog.按照建议,在 onCreate 获取布尔值 isShowingDialog 值,但等待 onResume 显示对话框。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 方向改变时如何获取屏幕尺寸? [android] - How do i get the dimensions of the screen when orientation changes? [android] 处理屏幕方向变化 - Handle screen orientation changes 打开新消息框或其他对话框时,如何调暗背景屏幕 - how can i do to dim background screen when open new message box or other dialog box 更改屏幕方向时如何控制android webview活动的更改? - How can I control android webview activity changes when I change screen orientation? 如何处理萤幕方向变更 - How to handle screen orientation change 对话框本身会更改方向。 如何将对话框的方向固定为横向 - Dialog changes the orientation by itself. How to fix the orientation of a dialog to landscape 屏幕方向更改时android线程崩溃 - android thread crashes when screen orientation changes 屏幕方向更改时lockCanvas失败 - lockCanvas failing when screen orientation changes 方向更改时,如何在GridView中保存位图图像的状态? - How do I save state for bitmap images within GridView when orientation changes? 屏幕改变方向时会调用什么? 更改方向后,我的应用程序正在重新启动 - What gets called when the screen changes orientation? My app is restarting when I change orientations
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM