Activity restart on rotation Android
在我的Android应用程序中,当我旋转设备(滑出键盘)时,我的
使用应用程序类
根据您在初始化过程中所做的操作,您可以考虑创建一个扩展
1 2 3 4 5 6 7 | public class MyApplicationClass extends Application { @Override public void onCreate() { super.onCreate(); // TODO Put your application initialization code here. } } |
应用程序类中的
将这个类的实例作为单例公开,并使用getter和setter公开正在初始化的应用程序变量,这是一个很好的实践。
注意:您需要在清单中指定要注册和使用的新应用程序类的名称:
1 2 | <application android:name="com.you.yourapp.MyApplicationClass" |
对配置更改作出反应[更新:自API 13以来,此项已弃用;请参阅建议的替代项]
作为另一种选择,您可以让应用程序监听可能导致重新启动的事件(如方向和键盘可见性更改),并在活动中处理这些事件。
首先将
1 2 3 | <activity android:name=".MyActivity" android:configChanges="orientation|keyboardHidden" android:label="@string/app_name"> |
或Android 3.2(API 13级)及更高版本:
1 2 3 | <activity android:name=".MyActivity" android:configChanges="keyboardHidden|orientation|screenSize" android:label="@string/app_name"> |
然后在活动中覆盖
1 2 3 4 5 | @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setContentView(R.layout.myLayout); } |
Android 3.2及更高版本的更新:
Caution: Beginning with Android 3.2 (API level 13), the"screen size" also changes when the device switches between portrait and landscape orientation. Thus, if you want to prevent runtime restarts due to orientation change when developing for API level 13 or higher (as declared by the minSdkVersion and targetSdkVersion attributes), you must include the
"screenSize" value in addition to the"orientation" value. That is, you must declareandroid:configChanges="orientation|screenSize" . However, if your application targets API level 12 or lower, then your activity always handles this configuration change itself (this configuration change does not restart your activity, even when running on an Android 3.2 or higher device).
与其试图阻止
例如,如果我有一些逻辑应该在真正创建
否则,我仍然希望布局为方向重新正确绘制。
1 2 3 4 5 6 7 8 9 10 | public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_game_list); if(savedInstanceState == null){ setupCloudMessaging(); } } |
不确定这是否是最终的答案,但它对我有效。
我做了什么…
在清单的"活动"部分中,添加了:
1 | android:configChanges="keyboardHidden|orientation" |
在活动的代码中,实现了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | //used in onCreate() and onConfigurationChanged() to set up the UI elements public void InitializeUI() { //get views from ID's this.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. @Override public 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) @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setContentView(R.layout.main); InitializeUI(); } |
您所描述的是默认行为。您必须自己检测和处理这些事件,方法是添加:
1 | android:configChanges |
到您的清单,然后是您想要处理的更改。因此,对于方向,您将使用:
1 | android:configChanges="orientation" |
对于正在打开或关闭的键盘,您将使用:
1 | android:configChanges="keyboardHidden" |
如果要同时处理这两个问题,可以使用pipe命令将它们分开,如下所示:
1 | android:configChanges="keyboardHidden|orientation" |
这将在您调用的任何活动中触发onconfigurationChanged方法。如果重写方法,则可以传入新值。
希望这有帮助。
我刚刚发现了这个传说:
为了通过方向更改保持活动的活动,并通过
1 | android:configChanges="keyboardHidden|orientation" |
这有一个额外的好处,它总是有效的。
另外一个传说是,省略
我没有在设备上看到失败,但是我听说模拟器在其他设备上失败了。所以值得记录。
您还可以考虑使用Android平台的方法来在方向变化中保持数据:
这允许您跨配置更改持久保存数据,例如从服务器获取的信息或在
看这里或这里。
应该注意的是,这些方法现在已被弃用(尽管仍然比处理方向改变更灵活,正如上述大多数解决方案所建议的那样),建议每个人切换到
该方法很有用,但在使用片段时不完整。
片段通常在配置更改时重新创建。如果您不希望发生这种情况,请使用
片段构造器中的
这将导致在配置更改期间保留片段。
http://developer.android.com/reference/android/app/fragment.html setretaininstance(布尔值)
我只是简单地加了一句
1 | android:configChanges="keyboard|keyboardHidden|orientation" |
在清单文件中,没有在我的活动中添加任何
所以每次键盘滑出或滑入时都不会发生任何事情。
将下面的代码放入
1 | android:configChanges="screenLayout|screenSize|orientation" |
即使更改了android的
很简单,只需执行以下步骤:
1 2 3 4 5 | <activity android:name=".Test" android:configChanges="orientation|screenSize" android:screenOrientation="landscape"> </activity> |
这对我很有用:
注意:方向取决于您的需求
1
2
3 onConfigurationChanged is called when the screen rotates.
(onCreate is no longer called when screen rotates due to manifest, see:
android:configChanges)
舱单的哪个部分告诉它"不要叫
也,谷歌的文档说要避免使用
根据我的经验,仿真器在旋转时总是调用
将此行添加到清单中:
1 | android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode" |
活动的这段代码:
1 2 3 4 5 6 | @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); } |
要在Android清单中进行的更改包括:
1 | android:configChanges="keyboardHidden|orientation" |
要在活动中添加的内容包括:
1 2 3 4 5 6 7 8 9 10 | public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); // Checks the orientation of the screen if (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(); } } |
有几种方法可以做到这一点:
保存活动状态您可以在
1 2 3 4 5 6 | @Override public void onSaveInstanceState(Bundle outState) { /*Save your data to be restored here Example : outState.putLong("time_state", time); , time is a long variable*/ super.onSaveInstanceState(outState); } |
然后使用
1 2 3 4 5 6 7 8 9 10 11 | @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if(savedInstanceState!= null){ /*When rotation occurs Example : time = savedInstanceState.getLong("time_state", 0); */ } else { //When onCreate is called for the first time } } |
自己处理方向更改
另一种选择是自己处理方向更改。但这并不是一个好的做法。
将此添加到清单文件。
1 | android:configChanges="keyboardHidden|orientation" |
对于Android 3.2及更高版本:
1 2 3 4 5 6 7 8 9 10 11 12 | android:configChanges="keyboardHidden|orientation|screenSize" @Override public void onConfigurationChanged(Configuration config) { super.onConfigurationChanged(config); if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) { //Handle rotation from landscape to portarit mode here } else if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE){ //Handle rotation from portrait to landscape mode here } } |
限制旋转
您还可以将活动限制在纵向或横向模式,以避免旋转。
将此添加到清单文件中的活动标记:
1 | android:screenOrientation="portrait" |
或者在活动中以编程方式实现:
1 2 3 4 5 | @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); } |
我发现这样做的方法是使用
尽管这不是"安卓方式",但我通过自己处理方向更改并简单地在视图中重新定位小部件以将更改的方向考虑在内,获得了非常好的结果。这比任何其他方法都快,因为您的视图不需要保存和恢复。它还为用户提供了更无缝的体验,因为重新定位的小部件完全相同,只是移动和/或调整了大小。这样不仅可以保留模型状态,还可以保留视图状态。
对于一个需要不时调整自身方向的观点,
注意:如果将来有人和我面临同样的问题,我会发布这个答案。对我来说,下面这句话并不新鲜:
1 | android:configChanges="orientation" |
当我旋转屏幕时,没有调用方法"onconfigurationchanged(configuration newconfig)"。
解决方案:即使问题与方向有关,我也必须添加"屏幕大小"。因此,在androidmanifest.xml文件中,添加以下内容:
1 | android:configChanges="keyboardHidden|orientation|screenSize" |
然后实现方法
您需要使用onsavedinstanceState方法来存储其参数is has(即bundle)的所有值。
1 2 3 4 5 | @Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { super.onSaveInstanceState(outState, outPersistentState); outPersistentState.putBoolean("key",value); } |
使用
1 2 3 4 5 | @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); savedInstanceState.getBoolean("key"); } |
检索并设置值以查看对象它将处理屏幕旋转
每次旋转屏幕时,打开的活动都会完成,并再次调用onCreate()。
1。您可以做一件事,即在屏幕旋转时保存活动状态,以便在再次调用活动的onCreate()时恢复所有旧内容。参考此链接
2。如果要阻止重新启动活动,只需在manifest.xml文件中放置以下行。
1 2 | <activity android:name=".Youractivity" android:configChanges="orientation|screenSize"/> |
在
1 | android:configChanges="keyboardHidden|orientation" |
在舱单中加这一行:
人们说你应该使用
1 | android:configChanges="keyboardHidden|orientation" |
但在Android中处理旋转的最佳和最专业的方法是使用loader类。它不是一个著名的类(我不知道为什么),但它比AsyncTask要好得多。有关更多信息,您可以阅读Udacity的Android课程中的Android教程。
当然,作为另一种方法,您可以使用OnSaveInstanceState存储值或视图,并使用OnRestoreInstanceState读取它们。这真的取决于你。
把下面的代码放到你的EDOCX1中(10),在EDOCX1中(26)。
1 | android:configChanges="orientation" |
当你改变方向时,这不会重新开始你的活动。
使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | @Override public 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); } } |
经过一段时间的反复尝试,我找到了一个最适合我需要的解决方案。代码如下:
清单配置:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.pepperonas.myapplication"> <application android:name=".App" android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme"> <activity android:name=".MainActivity" android:configChanges="orientation|keyboardHidden|screenSize"> <intent-filter> <category android:name="android.intent.category.LAUNCHER"/> </intent-filter> </activity> </application> </manifest> |
MainActivity:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 | 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; @Override public 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 Activity FragmentManager 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(); } } @Override public 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)... */ @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); Log.d(TAG,"onSaveInstanceState " +"YOU WON'T SEE ME!!!"); } /** * Not invoked (just for testing)... */ @Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); Log.d(TAG,"onSaveInstanceState " +"YOU WON'T SEE ME, AS WELL!!!"); } @Override protected void onResume() { super.onResume(); Log.d(TAG,"onResume " +""); } @Override protected void onPause() { super.onPause(); Log.d(TAG,"onPause " +""); } @Override protected void onDestroy() { super.onDestroy(); Log.d(TAG,"onDestroy " +""); } @Override public 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 transaction FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); // Replace whatever is in the fragment_container view with this fragment, // and add the transaction to the back stack transaction.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 transaction transaction.commit(); } } |
样本片段:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 | 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; } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { Log.d(TAG,"onCreateView " +""); return inflater.inflate(R.layout.fragment_one, container, false); } } |
可以在Github上找到。
固定
为此,不调用您的
谷歌推出的Android架构的最佳组件之一,将满足您的所有要求,即ViewModel。
它旨在以生命周期的方式存储和管理与用户界面相关的数据,并且允许数据在屏幕旋转时存活。
1 | class MyViewModel : ViewModel() { |
请参考:https://developer.android.com/topic/libraries/architecture/viewmodel
您可以在活动中使用ViewModel对象。
在配置更改期间,ViewModel对象会自动保留,以便它们保存的数据立即可用于下一个活动或片段实例。阅读更多:
https://developer.android.com/topic/libraries/architecture/viewmodel(https://developer.android.com/topic/libraries/architecture/viewmodel)
您可以使用此代码锁定到屏幕的当前方向…
1 2 3 4 5 6 | 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); } |