首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何在Android多窗口模式下确定正确的设备定位?

如何在Android多窗口模式下确定正确的设备定位?
EN

Stack Overflow用户
提问于 2016-12-23 16:35:56
回答 2查看 2.4K关注 0票数 23

来自多窗口文档

禁用了多窗口模式的功能 当设备处于多窗口模式时,某些功能会被禁用或忽略,因为它们对可能与其他活动或应用共享设备屏幕的活动没有意义。这些特点包括:

  • 有些系统用户界面定制选项被禁用;例如,如果应用程序没有在全屏模式下运行,则无法隐藏状态栏。
  • 系统忽略对android:屏幕导向属性的更改。

我明白,对于大多数应用程序来说,区分肖像模式和景观模式是没有意义的,但是我正在开发SDK,它包含用户可以进行任何活动的相机视图--包括支持多窗口模式的活动。问题是摄像机视图包含显示摄像机预览的SurfaceView/TextureView,为了在所有活动方向上正确显示预览,需要正确的活动方向知识,以便正确旋转摄像机预览。

问题是,我的代码通过检查当前配置方向(纵向或横向)和当前屏幕旋转来计算正确的活动方向。问题是在多窗口模式下,当前的配置方向不能反映实际的活动方向。这将导致摄像机预览旋转90度,因为Android报告的配置与方向不同。

我目前的解决方法是检查所请求的活动方向并使用它作为基础,但这有两个问题:

  1. 所请求的活动导向不必反映实际的活动方向(即请求可能仍未得到满足)
  2. 所请求的活动方向可以是“后面”、“传感器”、“用户”等,这不会显示任何关于当前活动方向的信息。
  3. 根据文档的说法,在多窗口模式下屏幕方向实际上被忽略了,所以1和2就不能工作了。

即使在多窗口配置中,也有任何方法可以可靠地计算正确的活动方向吗?

下面是我目前使用的代码(请参阅问题部分的注释):

代码语言:javascript
复制
protected int calculateHostScreenOrientation() {
    int hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
    WindowManager wm = (WindowManager) getContext().getSystemService(Context.WINDOW_SERVICE);
    int rotation = getDisplayOrientation(wm);

    boolean activityInPortrait;
    if ( !isInMultiWindowMode() ) {
        activityInPortrait = (mConfigurationOrientation == Configuration.ORIENTATION_PORTRAIT);
    } else {
        // in multi-window mode configuration orientation can be landscape even if activity is actually in portrait and vice versa
        // Try determining from requested orientation (not entirely correct, because the requested orientation does not have to
        // be the same as actual orientation (when they differ, this means that OS will soon rotate activity into requested orientation)
        // Also not correct because, according to https://developer.android.com/guide/topics/ui/multi-window.html#running this orientation
        // is actually ignored.
        int requestedOrientation = getHostActivity().getRequestedOrientation();
        if ( requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_PORTRAIT ||
                requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT ||
                requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT ||
                requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT ) {
            activityInPortrait = true;
        } else if ( requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE ||
                requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE ||
                requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE ||
                requestedOrientation == ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE ) {
            activityInPortrait = false;
        } else {
            // what to do when requested orientation is 'behind', 'sensor', 'user', etc. ?!?
            activityInPortrait = true; // just guess
        }
    }

    if ( activityInPortrait ) {
        Log.d(this, "Activity is in portrait");
        if (rotation == Surface.ROTATION_0) {
            Log.d(this, "Screen orientation is 0");
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        } else if (rotation == Surface.ROTATION_180) {
            Log.d(this, "Screen orientation is 180");
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
        } else if (rotation == Surface.ROTATION_270) {
            Log.d(this, "Screen orientation is 270");
            // natural display rotation is landscape (tablet)
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_PORTRAIT;
        } else {
            Log.d(this, "Screen orientation is 90");
            // natural display rotation is landscape (tablet)
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_PORTRAIT;
        }
    } else {
        Log.d(this, "Activity is in landscape");
        if (rotation == Surface.ROTATION_90) {
            Log.d(this, "Screen orientation is 90");
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        } else if (rotation == Surface.ROTATION_270) {
            Log.d(this, "Screen orientation is 270");
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        } else if (rotation == Surface.ROTATION_0) {
            Log.d(this, "Screen orientation is 0");
            // natural display rotation is landscape (tablet)
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
        } else {
            Log.d(this, "Screen orientation is 180");
            // natural display rotation is landscape (tablet)
            hostScreenOrientation = ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE;
        }
    }
    return hostScreenOrientation;
}

private int getDisplayOrientation(WindowManager wm) {
    if (DeviceManager.getSdkVersion() < 8) {
        return wm.getDefaultDisplay().getOrientation();
    }

    return wm.getDefaultDisplay().getRotation();
}

private boolean isInMultiWindowMode() {
    return Build.VERSION.SDK_INT >= 24 && getHostActivity().isInMultiWindowMode();
}

protected Activity getHostActivity() {
    Context context = getContext();
    while (context instanceof ContextWrapper) {
        if (context instanceof Activity) {
            return (Activity) context;
        }
        context = ((ContextWrapper) context).getBaseContext();
    }
    return null;
}

编辑:我也向Android问题跟踪器报告了这一点。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2016-12-30 19:43:09

我不知道这是一种解决方案还是一种解决办法。

正如你所说,你的问题来自于Android和它的多窗口模式。当应用程序在多个窗口中时,您的Activity没有绑定到完整的显示尺寸。这重新定义了面向Activity的概念。引用伊恩湖

原来,“肖像”实际上是指高度大于宽度,而“景观”是指宽度大于高度。因此,考虑到这个定义,您的应用程序可以在调整大小的同时从一个转换到另一个,这当然是有意义的。

因此,在改变Activity方向和设备物理旋转之间不再有任何联系。(我认为现在只有才能合理地使用面向活动的更改来更新您的资源。)

由于您对设备尺寸感兴趣,DisplayMetrics。引用医生的话,

如果要求从非活动上下文度量将报告整个显示器的大小,基于当前的旋转和减去的系统装饰区域。

因此,解决办法是:

代码语言:javascript
复制
final Context app = context.getApplicationContext();
WindowManager manager = (WindowManager) app.getSystemService(Context.WINDOW_SERVICE);
Display display = manager.getDefaultDisplay();
DisplayMetrics metrics = new DisplayMetrics();
display.getMetrics(metrics);
int width = metrics.widthPixels;
int height = metrics.heightPixels;
boolean portrait = height >= width;

当设备倾斜时,宽度和高度值将被交换(或多或少)。

如果这样做有效,我每次都会亲自运行它,删除isInMultiWindowMode()分支,因为

  • 不贵
  • 我们的假设也处于非多窗口模式。
  • 它可能与未来任何其他类型的模式都能很好地工作。
  • 您可以避免isInMultiWindowMode() 由CommonsWare描述的竞赛条件。
票数 6
EN

Stack Overflow用户

发布于 2016-12-29 21:39:10

我想你可以用加速度计来检测哪里是“停机”--从而确定手机的方向。工程师盖伊解释说,这就是手机本身的工作方式。

我在这里搜索了一种方法,找到了这个答案。基本上,你需要检查三个加速度计中哪一个能探测到引力的最重要部分,你知道在地球地面附近大约是9.8米/秒。下面是它的代码片段:

代码语言:javascript
复制
private boolean isLandscape;

mSensorManager = (SensorManager)getSystemService(SENSOR_SERVICE);
mSensorManager.registerListener(mSensorListener,     mSensorManager.getDefaultSensor(
                                      Sensor.TYPE_ACCELEROMETER),1000000);
private final SensorEventListener mSensorListener = new SensorEventListener() { 
    @Override 
    public void onSensorChanged(SensorEvent mSensorEvent) {   
        float X_Axis = mSensorEvent.values[0]; 
        float Y_Axis = mSensorEvent.values[1]; 

        if((X_Axis <= 6 && X_Axis >= -6) && Y_Axis > 5){
        isLandscape = false; 
        }
        else if(X_Axis >= 6 || X_Axis <= -6){
        isLandscape = true;
        }

    }

    public void onAccuracyChanged(Sensor sensor, int accuracy) {
    }
};  

小心,因为这段代码可能并不适用于每一种情况,因为您需要考虑一些场景,比如在停/加速火车上或者在游戏中快速移动电话--下面是让您开始的维基百科数量级页。看起来,使用Khalil在代码中的值(在他的答案中)是安全的,但我会格外小心,并研究在不同场景中可能生成哪些值。

这不是一个完美无缺的想法,但我认为,只要API是按照它的方式构建的--没有允许您获得他们的计算方向--我认为这是一个有益的解决办法。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/41304917

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档