关于App全屏的一二事

写在前面的几句话


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

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


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

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

  

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

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

configChanges

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

如下


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

方法如下

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

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

关于App全屏的一二事_第1张图片
图1 竖屏界面
关于App全屏的一二事_第2张图片
图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 竖屏相关操作
      }
 }

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

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

关于App全屏的一二事_第3张图片
图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?这个是什么鬼?为什么会这样?

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

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


首先我们需要知道下传感器,在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的监听,因为真的很耗电的

写在后面的几句话


我的愿望是世界和平!!!!!!!

你可能感兴趣的:(关于App全屏的一二事)