旋转时活动重启Android

在我的Android应用程序中,当我旋转设备(滑出键盘)时,我的Activity被重新启动(onCreate被调用)。现在,这可能是它应该是的样子,但是我在onCreate方法中做了很多初始设置,所以我需要:

  1. 将所有初始设置放在另一个函数中,这样它就不会在设备旋转或
  2. 使onCreate不再被调用,布局只是调整或
  3. 将应用程序限制为仅纵向,以便不调用onCreate
404989 次浏览

你描述的是默认行为。你必须通过添加:

android:configChanges

到您的清单,然后是您要处理的更改。因此,对于方向,您可以使用:

android:configChanges="orientation"

对于正在打开或关闭的键盘,您将使用:

android:configChanges="keyboardHidden"

如果你想处理这两个,你可以用管道命令将它们分开,比如:

android:configChanges="keyboardHidden|orientation"

这将在您调用的任何活动中触发onConfigurationChanged方法。如果您覆盖该方法,您可以传入新值。

希望这有帮助。

使用应用程序类

根据您在初始化中所做的事情,您可以考虑创建一个扩展Application的新类,并将您的初始化代码移动到该类中被覆盖的onCreate方法中。

public class MyApplicationClass extends Application {@Overridepublic void onCreate() {super.onCreate();// TODO Put your application initialization code here.}}

应用程序类中的onCreate仅在创建整个应用程序时被调用,因此活动在方向上重新启动或键盘可见性更改不会触发它。

最好将此类的实例公开为单例,并使用getter和setter公开您正在初始化的应用程序变量。

注意:您需要在清单中指定新Application类的名称才能注册和使用:

<applicationandroid:name="com.you.yourapp.MyApplicationClass"

响应配置更改[更新:自API 13以来已弃用;请参阅推荐的替代方案]

作为进一步的替代方案,您可以让您的应用程序侦听会导致重新启动的事件-例如方向和键盘可见性更改-并在您的活动中处理它们。

首先将android:configChanges节点添加到活动的清单节点

 <activity android:name=".MyActivity"android:configChanges="orientation|keyboardHidden"android:label="@string/app_name">

对于Android 3.2(API级别13)及更高版本

<activity android:name=".MyActivity"android:configChanges="keyboardHidden|orientation|screenSize"android:label="@string/app_name">

然后在活动中覆盖onConfigurationChanged方法并调用setContentView强制GUI布局在新方向上重新完成。

@Overridepublic void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);setContentView(R.layout.myLayout);}

即使您更改了android的orientationonCreate方法仍会被调用。因此将所有繁重的功能移动到此方法对您没有帮助

我刚刚发现了这个传说:

为了通过方向更改保持活动活跃,并通过onConfigurationChanged留档上面的代码示例处理它,建议在Mani花文件中这样做:

<activity android:name=".MyActivity"android:configChanges="orientation|keyboardHidden"android:label="@string/app_name">

它有一个额外的好处,它总是有效的。

额外的知识是省略keyboardHidden似乎是合乎逻辑的,但它会导致模拟器失败(至少对于Android 2.1):仅指定orientation会使模拟器有时同时调用OnCreateonConfigurationChanged,而其他时间仅调用OnCreate

我还没有看到设备上的失败,但我听说过仿真器在其他设备上失败。所以值得记录下来。

我所做的…

在清单中的活动部分,添加了:

android:configChanges="keyboardHidden|orientation"

在活动的代码中,实现了:

//used in onCreate() and onConfigurationChanged() to set up the UI elementspublic void InitializeUI(){//get views from ID'sthis.textViewHeaderMainMessage = (TextView) this.findViewById(R.id.TextViewHeaderMainMessage);
//etc... hook up click listeners, whatever you need from the Views}
//Called when the activity is first created.@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);
InitializeUI();}
//this is called when the screen rotates.// (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)@Overridepublic void onConfigurationChanged(Configuration newConfig){super.onConfigurationChanged(newConfig);setContentView(R.layout.main);
InitializeUI();}

您还可以考虑使用Android平台的方式跨方向更改持久化数据:onRetainNonConfigurationInstance()getLastNonConfigurationInstance()

这允许您在配置更改中持久化数据,例如您可能从服务器获取的信息或在onCreate或之后计算的其他信息,同时还允许Android使用xml文件为现在使用的方向重新布局Activity

这里这里

应该注意的是,这些方法现在已被弃用(尽管仍然比大多数上述解决方案建议的处理方向变化更灵活),建议每个人都切换到Fragments,而是在您想要保留的每个Fragment上使用setRetainInstance(true)

要在Android清单中进行的更改是:

android:configChanges="keyboardHidden|orientation"

要在活动中添加的内容是:

public void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);
// Checks the orientation of the screenif (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {Toast.makeText(this, "landscape", Toast.LENGTH_SHORT).show();} else if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {Toast.makeText(this, "portrait", Toast.LENGTH_SHORT).show();}}
onConfigurationChanged is called when the screen rotates.(onCreate is no longer called when the screen rotates due to manifest, see:android:configChanges)

清单的哪一部分告诉它“不要调用onCreate()”?

此外,谷歌的文档说避免使用android:configChanges(除了作为最后的手段)。但他们建议所有DO的替代方法都使用android:configChanges

根据我的经验,模拟器总是在旋转时调用onCreate()
但是我运行相同代码的1-2个设备……没有。(不知道为什么会有不同)

Android 3.2及更高版本的更新:

注意:从Android 3.2(API级别13)开始,当设备在纵向和横向之间切换时“屏幕尺寸”也在变化。因此,如果您想在开发API级别13或更高版本(由minSdkVersion和ectivetSdkVersion属性声明)时防止由于方向更改而重新启动运行时,除了"orientation"值之外,您还必须包含"screenSize"值。也就是说,您必须声明android:configChanges="orientation|screenSize"。但是,如果您的应用程序的目标是API级别12或更低,那么您的活动始终会自行处理此配置更改(此配置更改不会重新启动您的活动,即使在Android 3.2或更高版本的设备上运行)。

http://web.archive.org/web/20120805085007/http://developer.android.com/guide/topics/resources/runtime-changes.html

尽管它不是“Android方式”,但我通过自己处理方向变化并简单地在视图中重新定位小部件以考虑改变的方向来获得非常好的结果。这比任何其他方法都快,因为你的视图不必保存和恢复。它还为用户提供了更无缝的体验,因为重新定位的小部件完全相同,只是移动和/或调整大小。不仅模型状态,而且视图状态,都可以以这种方式保存。

对于必须不时重新定向自己的视图来说,RelativeLayout有时是一个不错的选择。你只需为每个子小部件提供一组纵向布局参数和一组景观布局参数,每个参数都有不同的相对定位规则。然后,在你的onConfigurationChanged()方法中,你将适当的一个传递给每个子控件的setLayoutParams()调用。如果任何子控件本身需要内部重定向,你只需调用该子控件上的一个方法来执行重定向。该子控件类似地调用个需要内部重定向的子控件中的任何一个的方法,依此类推。

与其试图阻止onCreate()被完全触发,不如尝试检查传递到事件中的BundlesavedInstanceState,看看它是否为null。

例如,如果我有一些逻辑应该在真正创建Activity时运行,而不是在每次方向更改时运行,那么只有当savedInstanceState为空时,我才会在onCreate()中运行该逻辑。

否则,我仍然希望布局重新绘制正确的方向。

public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_list);
if(savedInstanceState == null){setupCloudMessaging();}}

不知道这是否是最终的答案,但它对我有效。

我只是简单地加上

     android:configChanges="keyboard|keyboardHidden|orientation"

在清单文件和没有添加中,我的活动中的任何onConfigurationChanged方法。

所以每次键盘滑出或滑入时什么也没发生

该方法很有用,但在使用Fragments时是不完整的。

碎片通常会在配置更改时重新创建。如果您不希望这种情况发生,请使用

setRetainInstance(true);在片段的构造函数中

这将导致在配置更改期间保留片段。

http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)

我发现这样做的方法是使用onRestoreInstanceStateonSaveInstanceState事件在Bundle中保存一些东西(即使你不需要保存任何变量,只要在那里放一些东西,这样Bundle就不是空的)。然后,在onCreate方法上,检查Bundle是否为空,如果是,然后进行初始化,如果不是,然后进行初始化。

修复AndroidManifest.xml中的屏幕方向(横向或纵向)

android:screenOrientation="portrait"android:screenOrientation="landscape"

为此,您的onResume()方法未被调用。

把下面的代码放在你的ActivityAndroid Manifest中。

android:configChanges="orientation"

当您更改方向时,这不会重新启动您的活动。

备注:如果将来有人遇到和我一样的问题,我会发布这个答案。对我来说,下面这行是不够的:

android:configChanges="orientation"

当我旋转屏幕时,没有调用方法onConfigurationChanged(配置新配置)。

解决方案:即使问题与方向有关,我也必须添加“屏幕大小”。所以在AndroidManifest.xml文件中,添加以下内容:

android:configChanges="keyboardHidden|orientation|screenSize"

然后实现方法onConfigurationChanged(Configuration newConfig)

您可以使用此代码锁定屏幕的当前方向…

int currentOrientation =context.getResources().getConfiguration().orientation;if (currentOrientation == Configuration.ORIENTATION_PORTRAIT) {((Activity) context).setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);} else {((Activity) context). setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);}

将此行添加到您的清单中:-

android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"

这个活动的片段:-

@Overridepublic void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,WindowManager.LayoutParams.FLAG_FULLSCREEN);}

将下面的代码放在Manifest.xml中的<activity>标签中:

android:configChanges="screenLayout|screenSize|orientation"

使用orientation侦听器在不同的方向上执行不同的任务。

@Overridepublic void onConfigurationChanged(Configuration myConfig){super.onConfigurationChanged(myConfig);int orient = getResources().getConfiguration().orientation;switch(orient){case Configuration.ORIENTATION_LANDSCAPE:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);break;case Configuration.ORIENTATION_PORTRAIT:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);break;default:setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);}}

经过一段时间的反复试验,我找到了一个在大多数情况下都能满足我需求的解决方案。这是代码:

清单配置:

<?xml version="1.0" encoding="utf-8"?><manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.pepperonas.myapplication">
<applicationandroid:name=".App"android:allowBackup="true"android:icon="@mipmap/ic_launcher"android:label="@string/app_name"android:supportsRtl="true"android:theme="@style/AppTheme"><activityandroid:name=".MainActivity"android:configChanges="orientation|keyboardHidden|screenSize"><intent-filter><action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/></intent-filter></activity></application>
</manifest>

主活动

import android.content.res.Configuration;import android.os.Bundle;import android.support.v4.app.Fragment;import android.support.v4.app.FragmentManager;import android.support.v4.app.FragmentTransaction;import android.support.v7.app.AppCompatActivity;import android.util.Log;import android.view.View;import android.widget.Button;
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private static final String TAG = "MainActivity";
private Fragment mFragment;
private int mSelected = -1;

@Overridepublic void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);Log.d(TAG, "onCreate  " + "");
// null check not realy needed - but just in case...if (savedInstanceState == null) {
initUi();
// get an instance of FragmentTransaction from your ActivityFragmentManager fragmentManager = getSupportFragmentManager();FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
/*IMPORTANT: Do the INITIAL(!) transaction only once!* If we call this everytime the layout changes orientation,* we will end with a messy, half-working UI.* */mFragment = FragmentOne.newInstance(mSelected = 0);fragmentTransaction.add(R.id.frame, mFragment);fragmentTransaction.commit();}}

@Overridepublic void onConfigurationChanged(Configuration newConfig) {super.onConfigurationChanged(newConfig);Log.d(TAG, "onConfigurationChanged  " +(newConfig.orientation== Configuration.ORIENTATION_LANDSCAPE? "landscape" : "portrait"));
initUi();
Log.i(TAG, "onConfigurationChanged - last selected: " + mSelected);makeFragmentTransaction(mSelected);}

/*** Called from {@link #onCreate} and {@link #onConfigurationChanged}*/private void initUi() {setContentView(R.layout.activity_main);Log.d(TAG, "onCreate  instanceState == null / reinitializing..." + "");Button btnFragmentOne = (Button) findViewById(R.id.btn_fragment_one);Button btnFragmentTwo = (Button) findViewById(R.id.btn_fragment_two);btnFragmentOne.setOnClickListener(this);btnFragmentTwo.setOnClickListener(this);}

/*** Not invoked (just for testing)...*/@Overrideprotected void onSaveInstanceState(Bundle outState) {super.onSaveInstanceState(outState);Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME!!!");}

/*** Not invoked (just for testing)...*/@Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);Log.d(TAG, "onSaveInstanceState  " + "YOU WON'T SEE ME, AS WELL!!!");}

@Overrideprotected void onResume() {super.onResume();Log.d(TAG, "onResume  " + "");}

@Overrideprotected void onPause() {super.onPause();Log.d(TAG, "onPause  " + "");}

@Overrideprotected void onDestroy() {super.onDestroy();Log.d(TAG, "onDestroy  " + "");}

@Overridepublic void onClick(View v) {
switch (v.getId()) {case R.id.btn_fragment_one:Log.d(TAG, "onClick btn_fragment_one " + "");makeFragmentTransaction(0);break;
case R.id.btn_fragment_two:Log.d(TAG, "onClick btn_fragment_two " + "");makeFragmentTransaction(1);break;
default:Log.d(TAG, "onClick  null - wtf?!" + "");}}

/*** We replace the current Fragment with the selected one.* Note: It's called from {@link #onConfigurationChanged} as well.*/private void makeFragmentTransaction(int selection) {
switch (selection) {case 0:mFragment = FragmentOne.newInstance(mSelected = 0);break;case 1:mFragment = FragmentTwo.newInstance(mSelected = 1);break;}
// Create new transactionFragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,// and add the transaction to the back stacktransaction.replace(R.id.frame, mFragment);
/*This would add the Fragment to the backstack...* But right now we comment it out.*///        transaction.addToBackStack(null);
// Commit the transactiontransaction.commit();}
}

和样本片段:

import android.os.Bundle;import android.support.v4.app.Fragment;import android.util.Log;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;
/*** @author Martin Pfeffer (pepperonas)*/public class FragmentOne extends Fragment {
private static final String TAG = "FragmentOne";

public static Fragment newInstance(int i) {Fragment fragment = new FragmentOne();Bundle args = new Bundle();args.putInt("the_id", i);fragment.setArguments(args);return fragment;}

@Overridepublic View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {Log.d(TAG, "onCreateView  " + "");return inflater.inflate(R.layout.fragment_one, container, false);}
}

可以在github上找到。

这很简单,只需执行以下步骤:

<activityandroid:name=".Test"android:configChanges="orientation|screenSize"android:screenOrientation="landscape" ></activity>

这对我有用:

备注:方向取决于您的请求

有几种方法可以做到这一点:

保存活动状态

您可以将活动状态保存在onSaveInstanceState中。

@Overridepublic void onSaveInstanceState(Bundle outState) {/*Save your data to be restored hereExample: outState.putLong("time_state", time); , time is a long variable*/super.onSaveInstanceState(outState);}

然后使用bundle恢复状态。

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);
if(savedInstanceState!= null){/*When rotation occursExample : time = savedInstanceState.getLong("time_state", 0); */} else {//When onCreate is called for the first time}}

自己处理方向变化

另一种选择是自己处理方向变化。但这并不是一个好的做法。

将其添加到您的清单文件中。

android:configChanges="keyboardHidden|orientation"

对于Android 3.2及更高版本:

android:configChanges="keyboardHidden|orientation|screenSize"
@Overridepublic void onConfigurationChanged(Configuration config) {super.onConfigurationChanged(config);    
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {//Handle rotation from landscape to portrait mode here} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){//Handle rotation from portrait to landscape mode here}}

限制旋转

您还可以将活动限制为纵向或横向模式,以避免旋转。

将其添加到清单文件中的活动标记中:

        android:screenOrientation="portrait"

或者在您的活动中以编程方式实现:

@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);}

您需要使用onSavedInstance State方法将所有值存储到其参数is has中,该参数是一个捆绑包

@Overridepublic void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) {super.onSaveInstanceState(outState, outPersistentState);outPersistentState.putBoolean("key",value);}

和使用

@Overrideprotected void onRestoreInstanceState(Bundle savedInstanceState) {super.onRestoreInstanceState(savedInstanceState);savedInstanceState.getBoolean("key");}

检索并设置查看对象的值它将处理屏幕旋转

每次旋转屏幕时,打开的活动都会完成并再次调用onCreate()。

1.你可以做一件事,当屏幕旋转时保存活动的状态,这样,再次调用活动的onCreate()时,你可以恢复所有旧的东西。引用这个链接

2.如果要防止重新启动活动,只需在manifest.xml文件中放置以下行。

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

manifest的活动部分,添加:

android:configChanges="keyboardHidden|orientation"

在清单中添加这一行:android:configChanges="orientation|screenSize"

人们说你应该用

android:configChanges="keyboardHidden|orientation"

但是,在Android中处理轮换的最佳和最专业的方法是使用Loader类。它不是一个著名的类(我不知道为什么),但它比Async任务好得多。有关更多信息,您可以阅读UdityAndroid课程中的Android教程。

当然,作为另一种方式,您可以使用onSaveInstance State存储值或视图,并使用onReporeInstance State读取它们。

您可以在活动中使用ViewModel对象。

ViewModel对象在配置更改期间自动保留,以便它们保存的数据可立即用于下一个活动或片段实例。阅读更多:https://developer.android.com/topic/libraries/architecture/viewmodel

谷歌推出的Android架构的最佳组件之一将满足ViewModel的所有要求。

它旨在以生命周期的方式存储和管理与UI相关的数据,并允许数据在屏幕旋转时存活下来

class MyViewModel : ViewModel() {

请参考:https://developer.android.com/topic/libraries/architecture/viewmodel

将此代码添加到您的manifest.xml.

这是你的活动。

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

在下面的活动集config changes

 android:configChanges="orientation|screenSize|keyboardHidden"

有很多方法可以做到这一点:

  • 在onCreate中使用标志

    在您的清单文件中添加以下行


> android:screenOrientation="portrait"

和onCreate()

setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
  • 使用onConfigurationChanged()方法

在您的清单文件中添加以下行/for Android 3.2及更高版本

     

android:configChanges="keyboardHidden|orientation" / android:configChanges="keyboardHidden|orientation|screenSize"

@Overridepublic void onConfigurationChanged(Configuration config) {super.onConfigurationChanged(config);
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {//Handle rotation from landscape to portrait mode here} else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){//Handle rotation from portrait to landscape mode here}}
  • //使用onSaveInstance()方法

    重写//保存实例状态(Bundle outState){//保存实例状态/保存您的数据以在此处恢复示例:outState.putLong("time_state", time);, time是一个长变量/super.on保存实例状态(outState);}

并恢复

重写//绑定保存的实例状态//创建时保护无效super.on创建(保存实例状态);

if(savedInstanceState!= null){/*When rotation occursExample : time = savedInstanceState.getLong("time_state", 0); */} else {//When onCreate is called for the first time}

}