美文网首页
关于App全屏的一二事

关于App全屏的一二事

作者: 前世小书童 | 来源:发表于2016-06-15 16:24 被阅读1088次

    写在前面的几句话

    <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>
    我的愿望是世界和平!!!!!!!

    相关文章

      网友评论

          本文标题:关于App全屏的一二事

          本文链接:https://www.haomeiwen.com/subject/ijpudttx.html