关于App全屏的一二事

写在前面的几句话

<p>
其实大家在开发过程中,很少会接触到从竖屏到全屏的过程,只有在关于视频播放软件相关才会设计到这个方面的知识,刚好这次的开发中就遇到这样的问题了,那么就把这次遇到的针对关于全屏相关的东西记录下来。

一.简单实现横竖屏切换效果

<p>
其实这个很简单,只要打开手机的屏幕旋转功能就可以了,那么当手机旋转过来的时候,界面也会自动旋转过来的。

当然一般我们的开发都会屏蔽掉旋转,设置为单一的竖屏方向,设置如下

  <activity
            android:name=".TestActivity"
            android:screenOrientation="portrait" />

这样的横竖屏其实可以满足部分的需求了,但是其实横竖屏相互旋转都是Activity重新的创建,这样在某些应用场景下就不满足需求了,比如当视频播放软件竖屏播放切换到横屏时,如果重新创建的话,那么视频自然就不能够持续播放了,这样自然就不能满足需求了,

如何实现旋转过程中不重新创建Activity呢?

configChanges

在AndroidManifest.xml中设置这个Activity的configChanges参数就可以了

如下

<activity
            android:name=".TestActivity"
            android:configChanges="keyboardHidden|orientation|screenSize"
            />

添加这句的作用是,当Activity发生keyboardHidden(虚拟键盘隐藏),orientation(屏幕方向变化),screenSize(屏幕大小改变)这些变化的时候不会重新创建Activity,但是会在onConfigurationChanged方法中检测到响应的变化,通过这个方法才实现我们响应需要实现的逻辑

方法如下

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
 }

那么我们结合下一个小的事例来讲解实现

图1 竖屏界面
图2 横屏界面

如图所示,竖屏下上半部分为视频播放区域,下半部分则是相关的控制或者其他显示区域,而横屏下则全屏为视频播放区域,那么我们如何通过onConfigurationChanged来实现这样的功能呢?

首先通过onConfigurationChanged来判断横竖屏的转换

public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);
       if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
      // TODO: 16/6/14 横屏相关操作
       } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
      // TODO: 16/6/14 竖屏相关操作
      }
 }

接下来就是处理横竖屏幕响应的方法了

如果不做处理呢?我们可以看到旋转屏幕后会是这样的效果

图3 不做处理的横屏界面

但是这样明显就和我们最开始给出的横屏界面不一致

所以为了实现这种效果我们分析一下,横屏的时候视频播放区域覆盖了整个屏幕,而控制显示区域则消失了,所以其实很好理解的是当横屏的时候将控制区域设置为GONE,而将视频播放区域设置为match_parent即可以,这样其实视频播放区域则可以自动拉升到充满整个屏幕,当竖屏的时候则将控制区域设置为VISIBLE,并将视频播放区域动态设置为之前的大小。

所以现在来看下onConfigurationChanged中的方法

public void onConfigurationChanged(Configuration newConfig) {
   super.onConfigurationChanged(newConfig);
   if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
        //动态设置视频播放区域的为整个屏幕区域
      DisplayMetrics dm = new DisplayMetrics();
        this.getWindowManager().getDefaultDisplay().getMetrics(dm);
        LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) mViedioLayout.getLayoutParams();
        linearParams.height = dm.heightPixels;
        linearParams.width = dm.widthPixels;
        linearParams.setMargins(0, 0, 0, 0);
        mViedioLayout.setLayoutParams(linearParams);

        mControlLayout.setVisibility(View.GONE);


   } else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
        //动态设置视频播放区域为之前的大小
      LinearLayout.LayoutParams linearParams = (LinearLayout.LayoutParams) mViedioLayout.getLayoutParams();
        linearParams.height = (int) layoutheight;
        linearParams.width = (int) layoutwidth;
        mViedioLayout.setLayoutParams(linearParams);

        mControlLayout.setVisibility(View.VISIBLE);
  }
 }

通过这样就实现所要求的结果,是不是很简单?

当然等等,还有新的需求,一般情况下视频播放软件是存在切换横竖屏的按钮的,点击则去旋转屏幕为横屏还是竖屏

怎么去做这个呢?查一下就知道有强制设置屏幕的方法了,如下

//设置为竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//设置为横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);

当点击后使用上面的方法,屏幕则会旋转过来,而且在onConfigurationChanged中也会监听到屏幕的横竖方向转变,一切看起来如此完美

等等,怎么强制设置屏幕后,重力感应就没有了,wtf?这个是什么鬼?为什么会这样?

没办法只能想想通过别的方式来实现重力感应的效果了

二 .利用传感器服务实现横竖屏切换效果

<p>
首先我们需要知道下传感器,在Android设备中一般内置了很多的传感器,其中就包含重力感应传感器,虽然横竖屏切换与重力感应貌似有关,其实他们是两回事。横竖屏在重力感应中只是最粗略的说法了,你把手机平放着不动,重力感应都是时时刻刻发生着的,因为安卓设备能感应到很细微的震动。

Android中检测重力感应变化大致需要下面几个步骤:

    1. 得到传感器服务 getSystemService(SENSOR_SERVICE);

得到一个SensorManager,用来管理分配调度处理Sensor的工作,注意它并不服务运行于后台,真正属于Sensor的系统服务是SensorService,终端下#service list可以看到sensorservice: [android.gui.SensorServer]。

    1. 得到传感器类型 getDefaultSensor(Sensor.TYPE_GRAVITY);

当然还有各种千奇百怪的传感器,可以查阅Android官网API或者源码Sensor.java。

    1. 注册监听器 SensorEventListener

应用程序打开一个监听接口,专门处理传感器的数据,这个监听机制比较重要,被系统广泛使用。

    1. 实现监听器的回调函数 onSensorChanged, onAccuracyChanged

很多移动设备都内置了感应器,android通过Sensor和SensorManager类抽象了这些感应器,通过这些类可以使用android设备的传感器

所以我们通过onSensorChanged中的位置来判断手机的方向,通过这个方向来设置手机的横竖屏幕即可

ok,上代码如下


//注册重力感应器  屏幕旋转
SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
OrientationSensorListener listener = new OrientationSensorListener(handler);
sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);

private Handler handler = new Handler(){
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 888:
                int orientation = msg.arg1;
                if (orientation>45&&orientation<135) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                  
                }else if (orientation>135&&orientation<225){

                }else if (orientation>225&&orientation<315){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                   
                }else if ((orientation>315&&orientation<360)||(orientation>0&&orientation<45)){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                  
                }
                break;
            default:
                break;
        }

    };
};


/**
 * 重力感应监听者
 */
public class OrientationSensorListener implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    public static final int ORIENTATION_UNKNOWN = -1;

    private Handler rotateHandler;

    public OrientationSensorListener(Handler handler) {
        rotateHandler = handler;
    }

    public void onAccuracyChanged(Sensor arg0, int arg1) {
        // TODO Auto-generated method stub

    }

    public void onSensorChanged(SensorEvent event) {

        if(sensor_flag!=stretch_flag)  //只有两个不相同才开始监听行为
        {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X*X + Y*Y;
            // Don't trust the angle if the magnitude is small compared to the y value
            if (magnitude * 4 >= Z*Z) {
                //屏幕旋转时
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - (int)Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            System.out.println("orientation-->" + orientation);
            if (rotateHandler!=null) {
                rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();
            }

        }
    }
}

所以这里其实的逻辑就是检测重力感应传感器的变化,通过这个变化来设置屏幕的方向就可以了

看起来很完美的样子,实际运行就会发现问题了,当我们点击手动控制横竖屏幕的按钮后,发现又不起作用了,但是其实是有一个闪屏的效果,为什么会这样呢?

其实想想也可以理解,虽然我们强制设了横屏屏幕的方向,但是这个时候重力感应传感器是一直在监听手机的变化的啊,所以这时候变化就再次取强制设置屏幕方向,方向又被设为了竖屏,所以看起来就没有任何变化,但实际上这中间有这个设置的过程,既然知道是什么原因造成的了,那么就应该去解决这样的问题了

既然我们注册的重力感应监听的会对我们操作有影响,那么在按下的时候把重力感应监听的注册取消掉就好了,等后面在注册上就可以即对操作不影响,也在后面有了重力感应了啊,完美!!!!

等等,什么时候加上呢?问题来了,omg,无解,救命

没有什么事情是一个重力感应监听做不了,如果有,那么就用两个重力感应的监听

所以我们注册两个重力感应的监听,一个重力感应负责旋转屏幕方向,另一个负责动态去注册上面的那个监听,这样就解决了什么时候重力感应监听注册的问题了,

二话不说,直接上代码

//注册重力感应器  屏幕旋转
SensorManager sm = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor sensor = sm.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
OrientationSensorListener listener = new OrientationSensorListener(handler);
sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);


//根据  旋转之后 点击 符合之后 激活sm
SensorManager sm1 = (SensorManager)getSystemService(Context.SENSOR_SERVICE);
Sensor sensor1 = sm1.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
listener1 = new OrientationSensorListener2();
sm1.registerListener(listener1, sensor1, SensorManager.SENSOR_DELAY_UI);


private Handler handler = new Handler(){
    public void handleMessage(Message msg) {
        switch (msg.what) {
            case 888:
                int orientation = msg.arg1;
                if (orientation>45&&orientation<135) {
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_REVERSE_LANDSCAPE);
                    sensor_flag = false;
                    stretch_flag=false;
                }else if (orientation>135&&orientation<225){

                }else if (orientation>225&&orientation<315){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
                    sensor_flag = false;
                    stretch_flag=false;

                }else if ((orientation>315&&orientation<360)||(orientation>0&&orientation<45)){
                    setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
                    sensor_flag = true;
                    stretch_flag=true;

                }
                break;
            default:
                break;
        }

    };
};


/**
 * 重力感应监听者
 */
public class OrientationSensorListener implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    public static final int ORIENTATION_UNKNOWN = -1;

    private Handler rotateHandler;

    public OrientationSensorListener(Handler handler) {
        rotateHandler = handler;
    }

    public void onAccuracyChanged(Sensor arg0, int arg1) {
        // TODO Auto-generated method stub

    }

    public void onSensorChanged(SensorEvent event) {

        if(sensor_flag!=stretch_flag)  //只有两个不相同才开始监听行为
        {
            float[] values = event.values;
            int orientation = ORIENTATION_UNKNOWN;
            float X = -values[_DATA_X];
            float Y = -values[_DATA_Y];
            float Z = -values[_DATA_Z];
            float magnitude = X*X + Y*Y;
            // Don't trust the angle if the magnitude is small compared to the y value
            if (magnitude * 4 >= Z*Z) {
                //屏幕旋转时
                float OneEightyOverPi = 57.29577957855f;
                float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
                orientation = 90 - (int)Math.round(angle);
                // normalize to 0 - 359 range
                while (orientation >= 360) {
                    orientation -= 360;
                }
                while (orientation < 0) {
                    orientation += 360;
                }
            }
            System.out.println("orientation-->" + orientation);
            if (rotateHandler!=null) {
                rotateHandler.obtainMessage(888, orientation, 0).sendToTarget();
            }

        }
    }
}


public class OrientationSensorListener2 implements SensorEventListener {
    private static final int _DATA_X = 0;
    private static final int _DATA_Y = 1;
    private static final int _DATA_Z = 2;

    public static final int ORIENTATION_UNKNOWN = -1;

    public void onAccuracyChanged(Sensor arg0, int arg1) {
        // TODO Auto-generated method stub

    }

    public void onSensorChanged(SensorEvent event) {

        float[] values = event.values;

        int orientation = ORIENTATION_UNKNOWN;
        float X = -values[_DATA_X];
        float Y = -values[_DATA_Y];
        float Z = -values[_DATA_Z];

        /**
         * 这一段据说是 android源码里面拿出来的计算 屏幕旋转的 不懂 先留着 万一以后懂了呢
         */
        float magnitude = X*X + Y*Y;
        // Don't trust the angle if the magnitude is small compared to the y value
        if (magnitude * 4 >= Z*Z) {
            //屏幕旋转时
            float OneEightyOverPi = 57.29577957855f;
            float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
            orientation = 90 - (int)Math.round(angle);
            // normalize to 0 - 359 range
            while (orientation >= 360) {
                orientation -= 360;
            }
            while (orientation < 0) {
                orientation += 360;
            }
        }


        if (orientation>45&&orientation<135) { //横屏
            sensor_flag = false;
        }else if (orientation>225&&orientation<315){  //横屏
            sensor_flag = false;
        }else if ((orientation>315&&orientation<360)||(orientation>0&&orientation<45)){  //竖屏
            sensor_flag = true;
        }



        if(stretch_flag == sensor_flag){  //点击变成横屏  屏幕 也转横屏 激活
            sm.registerListener(listener, sensor, SensorManager.SENSOR_DELAY_UI);
        }
    }
}   

ok,到这里就基本实现了所需求的功能了,其实全屏相关的内容应该大部分都已经涉及到了,相信后面如果有别的全屏需求,大家也应该可以以不变应万变了,

最后记得在pause的时候取消两个sensor的监听,因为真的很耗电的

写在后面的几句话

<p>
我的愿望是世界和平!!!!!!!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,444评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,421评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,363评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,460评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,502评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,511评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,280评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,736评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,014评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,190评论 1 342
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,848评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,531评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,159评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,411评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,067评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,078评论 2 352

推荐阅读更多精彩内容