Activity在Android上重新启动
在我的Android应用程序中,当我旋转设备(滑出键盘),然后我的Activity
重新启动( onCreate
被称为)。 现在,这可能是应该的,但我在onCreate
方法中做了很多初始设置,所以我需要:
- 把所有的初始设置放在另一个函数中,所以它不会在设备旋转或丢失时丢失
- 使其如此创build不被再次调用和布局只是调整或
- 限制应用程序只是肖像,以便
onCreate
不被调用。
使用应用程序类
根据您在初始化时所做的操作,可以考虑创build一个新的类来扩展Application
并将初始化代码移动到该类中的onCreate
方法中。
public class MyApplicationClass extends Application { @Override public void onCreate() { super.onCreate(); // TODO Put your application initialization code here. } }
应用程序类中的onCreate
仅在创build完整个应用程序时调用,因此Activity在方向上重新启动,或者键盘可见性更改不会触发它。
将这个类的实例作为一个单例公开并使用getter和setter公开你正在初始化的应用程序variables是一个很好的习惯。
注意:您需要在清单中指定新的Application类的名称,以便注册和使用它:
<application android:name="com.you.yourapp.MyApplicationClass"
反应到configuration的变化 [更新:这是弃用自API 13; 看到推荐的替代scheme ]
作为另一种select,您可以让应用程序监听可能导致重新启动的事件(如方向和键盘可见性更改),并在您的“活动”中处理这些事件。
首先将android:configChanges
节点添加到Activity的清单节点
android:configChanges="keyboardHidden|orientation"
或Android 3.2(API级别13)和更新版本 :
android:configChanges="keyboardHidden|orientation|screenSize"
然后在Activity中覆盖onConfigurationChanged
方法并调用setContentView
来强制GUI布局在新的方向上重新完成。
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); setContentView(R.layout.myLayout); }
Android 3.2及更高版本的更新:
注意 :从Android 3.2(API级别13)开始,当设备在纵向和横向之间切换时, “屏幕大小”也会改变 。 因此,如果要在API级别13或更高级别(由minSdkVersion和targetSdkVersion属性声明)开发时防止由于方向更改而导致运行时重新启动,则除
"orientation"
值外,还必须包含"screenSize"
"orientation"
值。 也就是说,你必须声明android:configChanges="orientation|screenSize"
。 但是,如果您的应用程序的目标级别为12或更低,那么您的活动始终会自行处理此configuration更改(即使在Android 3.2或更高版本的设备上运行,此configuration更改也不会重新启动您的活动)。
而不是试图阻止onCreate()
完全被解雇,也许尝试检查传递给事件的Bundle
savedInstanceState
是否为null。
例如,如果我在创buildActivity
时应该运行某些逻辑,而不是在每个方向更改上运行,则只有在savedInstanceState
为null时,才会在onCreate()
运行该逻辑。
否则,我仍然希望布局正确重绘方向。
public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_game_list); if(savedInstanceState == null){ setupCloudMessaging(); } }
不知道这是否是最终的答案,但它适用于我。
我做了什么…
在清单,活动部分,补充说:
android:configChanges="keyboardHidden|orientation"
在活动的代码中,执行:
//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(); }
你所描述的是默认行为。 您必须通过添加以下内容来自行检测并处理这些事件:
android:configChanges
你的清单,然后是你想要处理的变化。 所以为了方向,你可以使用:
android:configChanges="orientation"
而对于打开或closures的键盘,您可以使用:
android:configChanges="keyboardHidden"
如果你想处理两者,你可以用pipe命令分隔它们,如:
android:configChanges="keyboardHidden|orientation"
这将触发你调用的任何Activity的onConfigurationChanged方法。 如果您重写该方法,则可以传入新的值。
希望这可以帮助。
我刚刚发现了这个绝招:
为了通过方向更改保持活动的活动状态,并通过onConfigurationChanged
处理它,上面的文档和代码示例在Manifest文件中显示了这一点:
android:configChanges="keyboardHidden|orientation"
它具有总是有效的额外好处。
奖金绝杀是省略keyboardHidden
onConfigurationChanged
可能看起来合乎逻辑,但它导致在模拟器(至lessAndroid 2.1)失败:只指定orientation
将使模拟器有时调用OnCreate
和onConfigurationChanged
,只有其他时间OnCreate
。
我没有看到设备上的故障,但是我听说过仿真器不能用于其他设备。 所以这是值得logging的。
您也可以考虑使用Android平台的跨方向更改持久化数据的方式: onRetainNonConfigurationInstance()
和getLastNonConfigurationInstance()
。
这允许您在configuration更改中保留数据,例如您可能从服务器获取的信息或onCreate
计算的其他信息,或者之后,还允许Android使用xml文件重新定位您的Activity
正在使用。
看到这里或这里 。
应该注意的是,这些方法现在已被弃用(尽pipe比上面大多数解决scheme所build议的处理方向更改更灵活),build议每个人都切换到Fragments
,而是在每个要保留的Fragment
上使用setRetainInstance(true)
。
该方法很有用,但在使用片段时不完整。
碎片通常会在configuration更改时重新创build。 如果你不希望发生这种情况,请使用
setRetainInstance(true);
在片段的构造函数中
这将导致configuration更改期间保留碎片。
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean);
我只是简单地添加
android:configChanges="keyboard|keyboardHidden|orientation"
在清单文件中, 并没有在我的活动中添加任何onConfigurationChanged
方法。
所以每次键盘滑出或没有任何反应 。
onConfigurationChanged is called when the screen rotates. (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
清单的哪一部分告诉它“不要调用onCreate()
”?
另外,谷歌的文档说避免使用android:configChanges
(除了作为最后的手段)….但是,然后他们build议所有使用android:configChanges
的替代方法。
我的经验是模拟器总是在旋转时调用onCreate()
。
但是,我运行相同的代码的1-2个设备不。 (不知道为什么会有什么区别。)
将此行添加到您的清单中: –
android:configChanges="orientation|keyboard|keyboardHidden|screenSize|screenLayout|uiMode"
和这个片段的活动: –
@Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); }
在Android清单中进行的更改是:
android:configChanges="keyboardHidden|orientation"
在活动中增加的内容是:
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(); } }
我发现这样做的方式是使用onRestoreInstanceState
和onSaveInstanceState
事件来保存Bundle
某些东西(即使你不需要保存任何variables,只需要把东西放在那里, Bundle
就不是空的)。 然后,在onCreate
方法上,检查Bundle
是否为空,如果是,则进行初始化,如果不是,则执行此操作。
即使在更改android的orientation
时, onCreate
方法仍然被调用。 所以将所有的重要function移到这个方法上是不会帮助你的
有几种方法可以做到这一点:
保存活动状态
您可以将活动状态保存在onSaveInstanceState
。
@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); }
然后使用该bundle
来恢复状态。
@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 } }
自行处理方向更改
另一种select是自己处理方向变化。 但这不被认为是一个好的做法。
将其添加到您的清单文件。
android:configChanges="keyboardHidden|orientation"
对于Android 3.2及更高版本:
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 } }
限制旋转
您还可以将您的活动限制为纵向或横向模式以避免旋转。
将其添加到清单文件中的活动标记中:
android:screenOrientation="portrait"
或者在您的活动中以编程方式实现:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); }
尽pipe它不是“Android方式”,但是通过自己处理方向变化以及在视图中简单地重新定位小部件来考虑改变的方向,我获得了非常好的结果。 这比任何其他方法都快,因为您的视图不必保存和恢复。 它还为用户提供了更加无缝的体验,因为重新定位的小部件是完全相同的小部件,只是移动和/或resize。 不仅模型状态,而且视图状态,都可以用这种方式保存下来。
有时候, RelativeLayout
可能是不时重新调整方向的一个很好的select。 您只需提供一组纵向布局参数和一组景观布局参数,每个子部件都有不同的相对定位规则。 然后,在onConfigurationChanged()
方法中,将适当的一个传递给每个孩子的setLayoutParams()
调用。 如果任何孩子控制本身需要在内部重新定位,你只需要调用一个方法来执行redirect。 那个孩子同样的调用任何需要内部redirect的子控件的方法,等等。
这很简单,只需执行以下步骤:
<activity android:name=".Test" android:configChanges="orientation|screenSize" android:screenOrientation="landscape" > </activity>
这适用于我:
注意:方向取决于您的要求
将下面的代码放在Manifest.xml
<activity>
标签中
机器人:configChanges = “屏幕布置|屏幕尺寸|方向”
注意:如果将来有人遇到和我一样的问题,我会发布这个答案。 对我来说,下面这行不够:
android:configChanges="orientation"
当我旋转屏幕时,onConfigurationChanged(Configuration newConfig)方法没有被调用。
解决scheme:即使问题与方向有关,我也必须添加“screenSize”。 所以在AndroidManifest.xml文件中添加这个:
android:configChanges="keyboardHidden|orientation|screenSize"
然后实现onConfigurationChanged(Configuration newConfig)
每当屏幕旋转时,打开的活动完成并再次调用onCreate()。
1。 当屏幕旋转时,你可以做一件事情来保存活动的状态,以便当活动的onCreate()被再次调用时,你可以恢复所有旧的东西。 参考这个链接
2。 如果要防止重新启动活动,只需在manifest.xml文件中放置以下行。
<activity android:name=".Youractivity" android:configChanges="orientation|screenSize"/>
在AndroidManifest.xml
修正屏幕方向(横向或纵向)
android:screenOrientation="portrait"
或android:screenOrientation="landscape"
为此,您的onResume()
方法不会被调用。
你需要使用onSavedInstanceState方法来存储所有的值到它的参数是有捆绑的
@Override public void onSaveInstanceState(Bundle outState, PersistableBundle outPersistentState) { super.onSaveInstanceState(outState, outPersistentState); outPersistentState.putBoolean("key",value); }
并使用
@Override protected void onRestoreInstanceState(Bundle savedInstanceState) { super.onRestoreInstanceState(savedInstanceState); savedInstanceState.getBoolean("key"); }
回顾并设置值来查看它将处理屏幕旋转的对象
在manifest
的活动部分中,添加:
android:configChanges="keyboardHidden|orientation"
经过一段时间的反复试验,我发现了一个解决scheme,可以满足大多数情况下的需求。 代码如下:
清单configuration:
<?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> <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; @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(); } }
和示例片段:
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上find。
使用orientation
侦听器在不同的方向上执行不同的任务。
@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); } }
将下面的代码放在Android Manifest
中的Activity
中。
android:configChanges="orientation"
当您改变方向时,这不会重新启动您的活动。
您可以使用此代码locking屏幕的当前方向…
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); }