写在前面的几句话
其实大家在开发过程中,很少会接触到从竖屏到全屏的过程,只有在关于视频播放软件相关才会设计到这个方面的知识,刚好这次的开发中就遇到这样的问题了,那么就把这次遇到的针对关于全屏相关的东西记录下来。
一.简单实现横竖屏切换效果
其实这个很简单,只要打开手机的屏幕旋转功能就可以了,那么当手机旋转过来的时候,界面也会自动旋转过来的。
当然一般我们的开发都会屏蔽掉旋转,设置为单一的竖屏方向,设置如下
这样的横竖屏其实可以满足部分的需求了,但是其实横竖屏相互旋转都是Activity重新的创建,这样在某些应用场景下就不满足需求了,比如当视频播放软件竖屏播放切换到横屏时,如果重新创建的话,那么视频自然就不能够持续播放了,这样自然就不能满足需求了,
如何实现旋转过程中不重新创建Activity呢?
configChanges
在AndroidManifest.xml中设置这个Activity的configChanges参数就可以了
如下
添加这句的作用是,当Activity发生keyboardHidden(虚拟键盘隐藏),orientation(屏幕方向变化),screenSize(屏幕大小改变)这些变化的时候不会重新创建Activity,但是会在onConfigurationChanged方法中检测到响应的变化,通过这个方法才实现我们响应需要实现的逻辑
方法如下
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
那么我们结合下一个小的事例来讲解实现
如图所示,竖屏下上半部分为视频播放区域,下半部分则是相关的控制或者其他显示区域,而横屏下则全屏为视频播放区域,那么我们如何通过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 竖屏相关操作
}
}
接下来就是处理横竖屏幕响应的方法了
如果不做处理呢?我们可以看到旋转屏幕后会是这样的效果
但是这样明显就和我们最开始给出的横屏界面不一致
所以为了实现这种效果我们分析一下,横屏的时候视频播放区域覆盖了整个屏幕,而控制显示区域则消失了,所以其实很好理解的是当横屏的时候将控制区域设置为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中检测重力感应变化大致需要下面几个步骤:
-
- 得到传感器服务 getSystemService(SENSOR_SERVICE);
得到一个SensorManager,用来管理分配调度处理Sensor的工作,注意它并不服务运行于后台,真正属于Sensor的系统服务是SensorService,终端下#service list可以看到sensorservice: [android.gui.SensorServer]。
-
- 得到传感器类型 getDefaultSensor(Sensor.TYPE_GRAVITY);
当然还有各种千奇百怪的传感器,可以查阅Android官网API或者源码Sensor.java。
-
- 注册监听器 SensorEventListener
应用程序打开一个监听接口,专门处理传感器的数据,这个监听机制比较重要,被系统广泛使用。
-
- 实现监听器的回调函数 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的监听,因为真的很耗电的
写在后面的几句话
我的愿望是世界和平!!!!!!!