有的适合会遇到锁定应用程序为横/竖屏的需求,那么我们如何控制Activity的屏幕方向呢?
废话不多说,我们直接上方法
控制屏幕方向
通过修改配置清单文件实现
- 首先打开AndroidManifest.xml
- 找到我们需要进行配置的Activity
- 找到其<activity>节点
- 为其添加android:screenOrientation属性,参数方面portrait为竖屏,landscape为横屏。
下面提供一个landscape横屏的测试:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.a4_1activity">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="landscape"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
效果如👆..由于是模拟器,默认是竖着的,但是可以看到,模拟器的画面已经被强制横屏了。可以在右侧工具栏
将模拟器横过来,效果如👇
通过代码实现
主要是👇的语句
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
ActivityInfo有SCREEN_ORIENTATION_PORTRAIT和SCREEN_ORIENTATION_LANDSCAPE两种选择
和前面一样,portrait为竖屏,landscape为横屏。
我们写一段简单的竖屏测试,但是我们没有去掉之前配置清单的配置。
package com.example.a4_1activity;
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//同样的portrait为竖屏,landscape为横屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
setContentView(R.layout.activity_main);
}
}
可以看到一个奇妙的现象,即一开始是横屏,随后变为竖屏。
网上有资料说配置清单的优先级高一些?会以清单文件为准,可是实际测试,配置清单似乎只是顺序上先于代码执行,而非等级上高于。可能是版本的原因,有兴趣的同学可以测试下低版本的?看看网上的说法是否正确。
有时候,控制方向还不够,可能还需要控制全屏显示。
控制全屏
可以通过下面的代码实现
package com.example.a4_1activity;
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//同样的portrait为竖屏,landscape为横屏
//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//控制全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
setContentView(R.layout.activity_main);
}
}
效果如👈,仔细观察一下,全屏的效果主要是状态栏没了。
去除标题栏
然后还有发现一个标题栏,如果也想去掉。可以添加如下语句,同时修改继承的父类为Activity
requestWindowFeature(Window.FEATURE_NO_TITLE);
package com.example.a4_1activity;
import android.content.pm.ActivityInfo;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.Window;
import android.view.WindowManager;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//同样的portrait为竖屏,landscape为横屏
//setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
//控制全屏
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);
//去除标题栏
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
}
}
效果如下:
设置主题实现实现无标题全屏
只要加上一句android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"即可实现等效的效果
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.a4_1activity">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:screenOrientation="landscape"
android:theme="@android:style/Theme.DeviceDefault.NoActionBar.Fullscreen"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
效果如👈,无视颜色...是主题自带的默认效果。
窗口模式
有全屏对应的就有窗口模式,只需要如下语句
android:theme="@android:style/Theme.DeviceDefault.Dialog"
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.a4_1activity">
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name=".MainActivity"
android:theme="@android:style/Theme.DeviceDefault.Dialog"
>
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
注意配置窗口模式不能再在清单文件中配置屏幕方向,否则冲突报错,你也没见过横着的dialog是把← ←
屏幕旋转若干事项
屏幕旋转销毁Activity
首先明确一个关键点:实际上App在进行横竖屏切换的时候会重新创建Activity。
可以通过在onCreate方法加入测试输出语句,然后手动进行横竖屏切换,观察是否多次进行输出来验证。
启动
然后我们横屏
可以看到确实再一次创建了Activity
明确了关键点之后,意味着什么呢?当前Activity内的信息将会被丢失。
可以试着定义一个变量,输出一次,再加一个button,点击使这个变量发生变化,再输出,然后旋转屏幕来观察输出的结果,测试比较简单,就不进行放截图了。
Activity的里的所有信息被丢失可不是一件小事,我们如何解决这个问题呢?
在这之前我们先修改一下我们MainActivity继承的父类,改成Activiy
然后就要提到我们👇方法(其实之前打过照面哦)
onSaveInstanceState
← ← 实际上这块应该放再Activity的生命周期里讲,接下来我们详细来讲一讲。
onSaveInstanceState
protected void onSaveInstanceState(Bundle outState)
- 首先明确调用时机,当Activity被意外回收的时候,onSaveInstanceState就会被调用
- 然后是参数,onSaveInstanceState提供一个Bundle参数。
Bundle在之前的文章已经有过介绍了: https://blog.csdn.net/nishigesb123/article/details/88877609
总之就是各种能传递数据就对了,get和put来取和存。
- 我们可以把数据存在这个Bundle里,存了之后有啥用呢?
然后我们默默的看一眼我们的onCreate
是不是有什么似曾相识的东西(其实在之前的文章里也提到过,onCreate参数里的Bundle实际上就是onSaveInstanceState存的Bundle!
话到这里差不多已经基本都清楚了。直接上代码(布局的话比较简单,给一个按钮,然后加个onClick事件就好)
package com.example.a4_1orientation;
import android.app.Activity;
import android.os.PersistableBundle;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
int index=0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
System.out.println("onCreate被调用");
if (savedInstanceState!=null){
index = savedInstanceState.getInt("index",0);
}
}
public void onClick(View view){
index++;
System.out.println("index的值为:"+index);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
System.out.println("onSaveInstanceState被调用");
outState.putInt("index",index);
}
}
首次运行:
点击三次button
旋转屏幕
再次点击button
可以看到,虽然Activity被重建,但是index的值以及保留了下来。
弊端与解决
虽然我们成功的保存了数据,但实际上是一种“治标不治本”的策略,每次都重新创建Activity,就算能通过临时保存,然后还原。但如果需要保存数据量很大怎么办?势必影响用户体验。
这时候我们可以通过AndroidManifest.xml中android:configChanges来解决这个问题。
它可以指定的当某个属性发生变化时,不去重启Activity,转换成通知程序去调用onConfigurationChanged()函数。
常见属性有:
- "keyboard'键盘发生了改变 例如用户用了外部的键盘
- "keyboardHidden"键盘的可用性发生了改变
- "orientation"屏幕方向改变了
- "screenSize"屏幕大小改变了
我们修改配置清单文件:
android:configChanges="screenSize|orientation"
并在代码中补上onConfigurationChanged方法
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
System.out.println("onConfigurationChanged被调用");
}
再看执行效果:
我们还是启动,然后点三次button
旋转屏幕,可以看到onCreate并没有被调用,即Activity并没有被重新创建。
竖屏/横屏布局自定义
有的时候,竖屏切换成横屏,布局也需要进行对应的调整,
首先,在Src文件夹下创建一个目录layout-land,并创建对应的布局文件xml,直接复制一份原来的在基础上进行修改即可。
(系统会自动将这个文件夹作为横屏的布局,Android视图可能会显示不出来,可以切换到Project视图)
可以看到,直接复制原来的布局文件,系统自动的显示成横屏模式了,为了辨识,在button上加上一些提示文字。
运行