在我的Android应用程序中,当我旋转设备(滑出键盘)时,我Activity
将重新启动(onCreate
称为)。现在,这大概是应该的样子,但是我在onCreate
方法中做了很多初始设置,因此我需要:
- 将所有初始设置都放在另一个功能中,这样就不会因设备旋转或
- 使其
onCreate
不再被调用,布局只是调整或 - 将应用程序限制为仅纵向,这样
onCreate
就不会被调用。
在我的Android应用程序中,当我旋转设备(滑出键盘)时,我Activity
将重新启动(onCreate
称为)。现在,这大概是应该的样子,但是我在onCreate
方法中做了很多初始设置,因此我需要:
onCreate
不再被调用,布局只是调整或onCreate
就不会被调用。Answers:
使用应用程序类
根据您在初始化过程中所做的事情,您可以考虑创建一个新的类来扩展该类,Application
并将初始化代码移动到onCreate
该类中的重写方法中。
public class MyApplicationClass extends Application {
@Override
public void onCreate() {
super.onCreate();
// TODO Put your application initialization code here.
}
}
在onCreate
创建整个应用程序时在应用程序类只叫,等方向或键盘知名度转变活动重启不会触发它。
优良作法是将此类的实例公开为单例,并使用getter和setter公开要初始化的应用程序变量。
注意:您需要在清单中指定新Application类的名称,以便注册和使用它:
<application
android:name="com.you.yourapp.MyApplicationClass"
对配置更改做出反应 [更新:自API 13起已弃用;有关详细信息,请参阅参考资料。请参阅推荐的替代方法 ]
作为另一种选择,您可以让您的应用程序监听会导致重启的事件(例如方向和键盘可见性更改),并在Activity中处理它们。
首先将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">
然后在Activity中覆盖该onConfigurationChanged
方法并调用setContentView
以强制以新的方向重新完成GUI布局。
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.myLayout);
}
android:configChanges
应避免使用(),并且只能将其用作最后一种手段。请阅读处理运行时更改以获取有关如何正确处理由于配置更改而导致的重新启动的更多信息。 取而代之的是,要在轮换事件之间保留数据,他们似乎更喜欢使用onSaveInstanceState Bundle
;。或@ Jon-O 提到,onRetainNonConfigurationInstance
。
onRetainNonConfigurationChanges
更高的容错性和直截了当。
Android 3.2及更高版本的更新:
注意:从Android 3.2(API级别13)开始,当设备在纵向和横向之间切换时,“屏幕尺寸”也会更改。因此,如果要防止在开发API级别13或更高级别(由minSdkVersion和targetSdkVersion属性声明)时由于方向更改而导致运行时重新启动,则必须在该
"screenSize"
值之外添加该"orientation"
值。也就是说,您必须声明android:configChanges="orientation|screenSize"
。但是,如果您的应用程序以API级别12或更低级别为目标,则您的活动始终会自行处理此配置更改(即使在Android 3.2或更高版本的设备上运行,此配置更改也不会重新启动您的活动)。
Fragments
和setRetainInstance
代替。
screenSize
Android 3.2及更高版本,它解决了我的问题,谢谢!
与其尝试完全停止onCreate()
触发,不如尝试检查Bundle
savedInstanceState
该事件,不如传递到事件中的事件以查看其是否为null。
例如,如果我有一些逻辑应在Activity
真正创建时运行,而不是在每次方向更改时都应运行,则仅在以下情况onCreate()
下运行该逻辑:savedInstanceState
为null。
否则,我仍然希望布局重新绘制以适应方向。
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_list);
if(savedInstanceState == null){
setupCloudMessaging();
}
}
不知道这是否是最终答案,但这对我有用。
Intent serverintent = new Intent(MainActivity.this, MessageListener.class);
并startService(serverintent);
使用创建一个serverSocket = new ServerSocket(0xcff2);
和Socket client = serverSocket.accept();
,BufferedReader(new InputStreamReader(client.getInputStream()));
并且可以旋转我的android并保持客户端/服务器连接处于活动状态,但可以旋转GUI。根据手册,在关闭最后一个活动时,将对savedInstanceState进行初始化。
我做了什么...
在清单中的“活动”部分中添加:
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"
对于打开或关闭的键盘,您可以使用:
android:configChanges="keyboardHidden"
如果要同时处理这两种情况,则可以使用管道命令将它们分开,例如:
android:configChanges="keyboardHidden|orientation"
这将在您调用的任何Activity中触发onConfigurationChanged方法。如果您覆盖该方法,则可以传入新值。
希望这可以帮助。
我刚刚发现了这个传说:
为了通过定向更改使活动保持活动状态并通过进行处理onConfigurationChanged
,上述文档和代码示例在清单文件中提出了建议:
<activity android:name=".MyActivity"
android:configChanges="orientation|keyboardHidden"
android:label="@string/app_name">
它具有始终有效的额外好处。
额外的知识是,省略keyboardHidden
可能看起来合乎逻辑,但这会导致模拟器失败(至少适用于Android 2.1):仅指定orientation
将使模拟器同时OnCreate
(onConfigurationChanged
有时)同时调用OnCreate
其他时间。
我还没有看到设备上的故障,但是我听说模拟器对其他设备失败。因此,值得记录。
Fragments
和setRetainInstance
代替。
您可能还会考虑使用Android平台的方法来跨方向更改持久化数据: onRetainNonConfigurationInstance()
和getLastNonConfigurationInstance()
。
这样一来,您就可以在配置更改期间保留数据,例如,您可能是从服务器获取的信息中获取的信息,或在此onCreate
之后获取的信息,同时还允许Android Activity
使用xml文件重新布局您当前使用的方向。
应该注意的是,现在不建议使用这些方法(尽管比大多数解决方案所建议的,要比改变方向更灵活),建议每个人都切换到您要保留的每个Fragments
方法setRetainInstance(true)
上Fragment
。
该方法很有用,但在使用片段时不完整。
片段通常在配置更改时重新创建。如果您不希望这种情况发生,请使用
setRetainInstance(true);
在Fragment的构造函数中
这将导致片段在配置更改期间被保留。
http://developer.android.com/reference/android/app/Fragment.html#setRetainInstance(boolean)
setRetainInstance()
。
我只是简单地添加
android:configChanges="keyboard|keyboardHidden|orientation"
在清单文件中,没有添加任何onConfigurationChanged
在我的活动中方法。
<application ...android:configChanges="keyboard|keyboardHidden|orientation">
它,并且正在工作。我在build.gradle中的设置:minSdkVersion 15, compileSdkVersion 23, buildToolsVersion "23.0.2"
将下面的代码放在<activity>
标记中Manifest.xml
:
android:configChanges="screenLayout|screenSize|orientation"
onConfigurationChanged is called when the screen rotates. (onCreate is no longer called when screen rotates due to manifest, see: android:configChanges)
清单的哪一部分告诉它“不要调用 onCreate()
”?
此外,谷歌的文档说,以避免使用android:configChanges
(除非万不得已)......但随后的替代方法,他们建议所有DO使用android:configChanges
。
根据我的经验,仿真器总是onCreate()
在旋转时调用。
但是我在同一设备上运行相同代码的1-2个设备却没有。(不确定为什么会有任何区别。)
在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();
}
}
将此行添加到清单:
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);
}
做这件事有很多种方法:
您可以将活动状态保存在中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
}
}
另一种选择是自己处理方向变化。但这不是一个好习惯。
将此添加到清单文件。
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);
}
即使不是“ Android方式”,我也可以通过自己处理方向更改并简单地将小部件重新放置在视图中以考虑更改的方向来获得很好的效果。这比任何其他方法都快,因为不必保存和还原视图。由于重新定位的窗口小部件与刚刚移动和/或调整大小的窗口小部件完全相同,因此它还为用户提供了更加无缝的体验。以这种方式不仅可以保留模型状态,还可以保留视图状态。
RelativeLayout
对于有时需要重新定向的视图,有时可能是一个不错的选择。您只需要为每个子窗口小部件提供一组纵向布局参数和一组横向布局参数,每个参数具有不同的相对定位规则。然后,在您的onConfigurationChanged()
方法中,将适当的方法传递setLayoutParams()
给每个孩子的电话。如果任何子控件本身需要在内部重新定向,则只需在该子控件上调用一个方法来执行重新定向。那个孩子同样呼吁任何方法的需要重新定位内部,等子控件。
您需要使用onSavedInstanceState方法将所有值存储到其参数是否为bundle
@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");
}
检索并设置值以查看对象,它将处理屏幕旋转
注意:如果将来有人遇到与我相同的问题,我会发布此答案。对我来说,以下行是不够的:
android:configChanges="orientation"
当我旋转屏幕时,没有调用方法onConfigurationChanged(Configuration newConfig)。
解决方案:即使问题与方向有关,我也必须添加“ screenSize”。因此,在AndroidManifest.xml-文件中,添加以下内容:
android:configChanges="keyboardHidden|orientation|screenSize"
然后实施方法 onConfigurationChanged(Configuration newConfig)
在的活动部分中manifest
,添加:
android:configChanges="keyboardHidden|orientation"
在清单中添加以下行: android:configChanges="orientation|screenSize"
人们说你应该使用
android:configChanges="keyboardHidden|orientation"
但是在Android中处理旋转的最好,最专业的方法是使用Loader类。它不是一个著名的类(我不知道为什么),但是它比AsyncTask更好。有关更多信息,您可以阅读Udacity的Android课程中的Android教程。
当然,作为另一种方法,您可以使用onSaveInstanceState存储值或视图,并使用onRestoreInstanceState读取它们。完全取决于您。
经过一段时间的反复试验,我找到了适合大多数情况的解决方案。这是代码:
清单配置:
<?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上找到。
使用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);
}
}
将以下代码放在Activity
in中Android Manifest
。
android:configChanges="orientation"
当您更改方向时,这不会重新开始您的活动。
将屏幕方向(横向或纵向)固定为 AndroidManifest.xml
android:screenOrientation="portrait"
要么 android:screenOrientation="landscape"
为此,onResume()
不会调用您的方法。
Google引入的android architechure最好的组件之一将满足ViewModel的所有要求。
旨在以生命周期的方式存储和管理与UI相关的数据,并允许数据随着屏幕旋转而得以保存
class MyViewModel : ViewModel() {
请参考:https : //developer.android.com/topic/libraries/architecture/viewmodel
您可以在活动中使用ViewModel对象。
ViewModel对象会在配置更改期间自动保留,以便它们保存的数据可立即用于下一个活动或片段实例。阅读更多:
https://developer.android.com/topic/libraries/architecture/viewmodel