إعادة تشغيل النشاط عند تدوير Android




rotation android-activity (20)

 onConfigurationChanged is called when the screen rotates. 
 (onCreate is no longer called when screen rotates due to manifest, see:  
 android:configChanges)

أي جزء من البيان يخبره "لا تستدعي onCreate()

كما يقول محرر مستندات Google أيضًا لتجنب استخدام android:configChanges (باستثناء كملاذ أخير) ... ولكن بعد ذلك تستخدم الطرق البديلة التي android:configChanges جميعًا android:configChanges .

لقد كانت تجربتي أن المحاكي دائما يدعو على onCreate() عند الدوران.
لكن الأجهزة 1-2 التي أقوم بتشغيل نفس التعليمات البرمجية على ... لا. (غير متأكد من سبب وجود أي فرق.)

في تطبيق Android الخاص بي ، عندما أقوم بتدوير الجهاز ( onCreate لوحة المفاتيح) ، تتم إعادة تشغيل onCreate (يتم استدعاء onCreate ). الآن ، ربما هذا هو المفترض أن يكون ، لكنني أقوم بالكثير من الإعداد الأولي في طريقة onCreate ، لذلك أحتاج إلى:

  1. ضع كل الإعداد الأولي في وظيفة أخرى ، بحيث لا تكون كلها مفقودة على دوران الجهاز أو
  2. جعل ذلك onCreate لم يتم استدعاء مرة أخرى والتخطيط فقط يضبط أو
  3. حدد التطبيق على صورة عمودي فقط حتى لا يتم استدعاء onCreate .

أصلح اتجاه الشاشة (أفقي أو عمودي) في AndroidManifest.xml

android:screenOrientation="portrait" أو android:screenOrientation="landscape"

لهذا لم يتم استدعاء طريقة onResume() الخاصة بك.


أضف هذا الخط إلى بيانك: -

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


استخدم مستمع 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 هي:

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="keyboardHidden|orientation"

ولكن أفضل الطرق وأكثرها احترافية للتعامل مع التدوير في Android هي استخدام فئة برنامج Loader.إنها ليست فئة مشهورة (لا أعرف لماذا) ، لكنها أفضل من AsyncTask. استخدمته لإكمال مهمة Popular-Movies-Part-1 من دورة Android nano degree.

أفلام شعبية جزء 1

بالطبع يمكنك تخزين القيم أو المشاهدات مع onSaveInstanceState وقراءتها مع onRestoreInstanceState. والامر متروك لك حقا.


النهج مفيد ولكنه غير كامل عند استخدام الأجزاء.

عادةً ما يتم إعادة إنشاء الأجزاء على تغيير التكوين. إذا كنت لا ترغب في حدوث ذلك ، فاستخدمه

setRetainInstance(true); في منشئ Fragment

سيؤدي هذا إلى الاحتفاظ بشظايا أثناء تغيير التكوين.

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


بعد فترة من التجربة والخطأ ، وجدت حلاً يناسب احتياجاتي في معظم الحالات. هنا الرمز:

تكوين مظاهر:

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


تحتاج إلى استخدام أسلوب 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");
    } 

لإعادة تعيين وتعيين القيمة لعرض الكائنات ، فإنه سيتم معالجة تدوير الشاشة


ضع الشفرة أدناه داخل علامة <activity> في Manifest.xml :

android:configChanges="screenLayout|screenSize|orientation"

ضع هذا الرمز أدناه في نشاطك في Android Manifest .

android:configChanges="orientation"

لن يؤدي هذا إلى إعادة تشغيل نشاطك عندما تقوم بتغيير الاتجاه.


في قسم النشاط في manifest ، أضف:

android:configChanges="keyboardHidden|orientation"

في كل مرة يتم فيها تدوير الشاشة ، يتم إنهاء النشاط المفتوح ويتم استدعاء onCreate () مرة أخرى.

1. يمكنك القيام بشيء واحد حفظ حالة النشاط عند تدوير الشاشة بحيث يمكنك استرداد كل الأشياء القديمة عند استدعاء onCreate () للنشاط مرة أخرى. إحالة this الرابط

2. إذا كنت تريد منع إعادة تشغيل النشاط ، فما عليك سوى وضع الأسطر التالية في ملف manifest.xml.

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

لا يزال يتم استدعاء الأسلوب onCreate حتى عند تغيير orientation android. لذا فإن نقل جميع الوظائف الثقيلة إلى هذه الطريقة لن يساعدك


لقد اكتشفت هذا العلم:

للحفاظ على النشاط على قيد الحياة من خلال تغيير الاتجاه ، والتعامل معه من خلال onConfigurationChanged ، onConfigurationChanged الوثائق ونموذج التعليمات البرمجية أعلاه إلى ذلك في ملف البيان:

android:configChanges="keyboardHidden|orientation"

الذي لديه فائدة إضافية أنه يعمل دائما.

إن العلم المكافأة هو أن حذف keyboardHidden Hidden قد يبدو منطقيًا ، لكنه يتسبب في حدوث فشل في محاكي (لـ Android 2.1 على الأقل): تحديد orientation فقط سيجعل المحاكي يستدعي OnCreate و onConfigurationChanged بعض الأحيان ، وفقط OnCreate أوقات أخرى.

لم أشاهد الفشل على جهاز ، لكنني سمعت عن فشل المحاكي للآخرين. لذلك يستحق توثيقه.


ماذا فعلت...

في البيان ، إلى قسم النشاط ، وأضاف:

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

هناك عدة طرق للقيام بذلك:

حفظ دولة النشاط

يمكنك حفظ حالة النشاط في 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:configChanges="orientation"

عندما قمت بتدوير الشاشة ، لم يتم استدعاء الطريقة `onConfigurationChanged (تكوين newConfig).

الحل: اضطررت أيضًا إلى إضافة "screenSize" حتى إذا كانت المشكلة متعلقة بالتوجيه. لذا في ملف 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-activity