提问



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



  1. 将所有初始设置放在另一个功能中,以便在设备旋转或
  2. 时不会全部丢失
  3. 这样做onCreate不再被调用,布局只是调整或

  4. 将应用限制为仅限肖像,以便不调用onCreate


最佳参考


使用应用程序类


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


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


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


将此类的实例公开为单例并公开使用getter和setter初始化的应用程序变量是一种很好的做法。


注意:您需要在清单中指定新Application类的名称,以便注册和使用它:


<application
    android:name="com.you.yourapp.MyApplicationClass"


对配置更改做出反应 [[更新:自API 13以来不推荐使用此功能;看推荐的替代方案]] [104]


作为另一种选择,您可以让应用程序侦听可能导致重新启动的事件(如方向和键盘可见性更改),并在您的Activity中处理它们。


首先将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);
}

其它参考1


Android 3.2及更高版本的更新:



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


其它参考2


可能会尝试检查传入事件的Bundle savedInstanceState以查看它是否为空,而不是试图阻止onCreate()被完全解雇。


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


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


public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game_list);

        if(savedInstanceState == null){
            setupCloudMessaging();
        }
}


不确定这是否是最终的答案,但它对我有用。

其它参考3


我做了什么...


在清单中,到Activity部分,添加:


android:configChanges="keyboardHidden|orientation"


在Activity的代码中,实现了:


//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();
}

其它参考4


您描述的是默认行为。您必须通过添加以下内容来自行检测和处理这些事件:


android:configChanges


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


android:configChanges="orientation"


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


android:configChanges="keyboardHidden"


如果你想要处理两者,你可以使用管道命令将它们分开,如:


android:configChanges="keyboardHidden|orientation"


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


希望这可以帮助。

其它参考5


我刚刚发现了这个传说:


为了通过方向更改保持Activity存活,并通过onConfigurationChanged处理它,上面的文档和代码示例在清单文件中提出了这一点:[106]


android:configChanges="keyboardHidden|orientation"


它具有始终有效的额外好处。


奖金传说是省略keyboardHidden似乎是合乎逻辑的,但它会导致模拟器失败(至少对于Android 2.1):仅指定orientation将使模拟器同时调用OnCreateonConfigurationChanged有时,只有OnCreate其他时间。


我没有看到设备上的故障,但我听说模拟器失败了。所以值得记录。

其它参考6


您也可以考虑使用Android平台在方向更改中保持数据的方式:onRetainNonConfigurationInstance()getLastNonConfigurationInstance()


这允许您在配置更改中保留数据,例如您可能从服务器提取中获取的信息或在onCreate中计算的其他信息,或者同时允许Android重新布局您的Activity]]使用xml文件进行正在使用的方向。


见这里或这里。[108] [109]


应该注意的是,这些方法现在已被弃用(尽管比上述大多数解决方案所建议的更方便处理方向更改),并建议每个人切换到Fragments,而是在每个方法上使用setRetainInstance(true) Fragment你想保留。

其它参考7


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


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


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


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


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

其它参考8


我只是简单地补充道


     android:configChanges="keyboard|keyboardHidden|orientation"


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


所以每次键盘滑出或没有任何反应。[111]

其它参考9



 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个设备却没有。
(不确定为什么会有任何区别。)

其它参考10


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


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


这个Activity的片段: -


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

其它参考11


在Android清单中进行的更改包括:


android:configChanges="keyboardHidden|orientation" 


内部Activity的增加是:


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();
    }
}

其它参考12


做这件事有很多种方法:


保存Activity状态



您可以在onSaveInstanceState中保存Activity状态。


@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
    }
}


限制轮换



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


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


        android:screenOrientation="portrait"


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


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

其它参考13


即使更改了android的orientationonCreate方法仍会被调用。因此,将所有重要功能移至此方法并不会对您有所帮助

其它参考14


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


android:configChanges="screenLayout|screenSize|orientation"

其它参考15


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

其它参考16


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


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

其它参考17


只需执行以下步骤即可:


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


这对我有用:


注意:方向取决于您的要求

其它参考18


注意:如果将来某人遇到与我相同的问题,我会发布此答案。对我来说,以下行不是:


android:configChanges="orientation"


当我旋转屏幕时,方法`onConfigurationChanged(Configuration newConfig)没有被调用。


解决方案:即使问题与方向有关,我也必须添加screenSize。所以在AndroidManifest.xml文件中,添加:


android:configChanges="keyboardHidden|orientation|screenSize"


然后实现方法onConfigurationChanged(Configuration newConfig)

其它参考19


您需要使用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");
    } 


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

其它参考20


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


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


2。如果要阻止重新启动Activity,请在manifest.xml文件中放置以下行。


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

其它参考21


manifest的Activity部分,添加:


android:configChanges="keyboardHidden|orientation"

其它参考22


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


清单配置:


<?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>


MainActivity:


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上找到。[113]

其它参考23


使用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);
    }
}

其它参考24


将以下代码放在Android ManifestActivity中。


android:configChanges="orientation"


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

其它参考25


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


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


为此,不调用onResume()方法。

其它参考26


添加此行android:configChanges =orientation | screenSize
在清单中

其它参考27


人们说你应该使用


android:configChanges="keyboardHidden|orientation"


但是在Android中处理旋转的最佳和最专业的方法是使用Loader类。它不是一个着名的类(我不知道为什么),但它比AsyncTask更好。有关更多信息,您可以阅读Udacity的Android课程中的Android教程。


当然,作为另一种方式,您可以使用onSaveInstanceState存储值或视图,并使用onRestoreInstanceState读取它们。这真的取决于你。

其它参考28


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


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);
        }