กิจกรรมเริ่มต้นใหม่ในการหมุน Android


1379

ในแอปพลิเคชัน Android ของฉันเมื่อฉันหมุนอุปกรณ์ (เลื่อนคีย์บอร์ดออก) จากนั้นฉันActivityจะเริ่มใหม่ ( onCreateเรียกว่า) ตอนนี้อาจเป็นวิธีที่ควรจะเป็น แต่ฉันทำการตั้งค่าเริ่มต้นจำนวนมากในonCreateวิธีการดังนั้นฉันต้องการอย่างใดอย่างหนึ่ง:

  1. ใส่การตั้งค่าเริ่มต้นทั้งหมดไว้ในฟังก์ชั่นอื่นเพื่อไม่ให้อุปกรณ์หมุนหรือหายไป
  2. ทำให้มันonCreateไม่ถูกเรียกอีกครั้งและเลย์เอาต์ก็ปรับหรือ
  3. จำกัด แอพให้เป็นแนวตั้งเท่านั้นเพื่อที่onCreateจะไม่เรียกใช้

4
มีคำอธิบายที่ค่อนข้างสมบูรณ์เกี่ยวกับวิธีรักษางานอะซิงโครนัสระยะยาวในระหว่างการเปลี่ยนแปลงการกำหนดค่ากิจกรรมในโพสต์บล็อกนี้ด้วย!
Adrian Monk

3
นี่ไม่ใช่คำตอบโดยตรงตามที่คนอื่นได้ตอบไปแล้ว แต่ฉันขอเชิญคุณมาดูLogLifeCycleเพื่อทำความเข้าใจว่าเกิดอะไรขึ้นในแอป Android ของคุณเกี่ยวกับวงจรชีวิต
Snicolas

คำตอบ:


965

การใช้ Application Class

ขึ้นอยู่กับสิ่งที่คุณกำลังทำในการเริ่มต้นคุณสามารถพิจารณาการสร้างคลาสใหม่ที่ขยายApplicationและย้ายรหัสการเริ่มต้นของคุณไปยังonCreateวิธีการแทนที่ภายในชั้นเรียนนั้น

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

onCreateในชั้นเรียนของแอพลิเคชันที่เรียกว่าเฉพาะเมื่อโปรแกรมทั้งหมดถูกสร้างขึ้นเพื่อให้มีการรีสตาร์ทกิจกรรมปฐมนิเทศหรือการมองเห็นการเปลี่ยนแปลงแป้นพิมพ์จะไม่เรียกมัน

เป็นวิธีปฏิบัติที่ดีที่จะเปิดเผยอินสแตนซ์ของคลาสนี้เป็นซิงเกิลตันและเปิดเผยแอปพลิเคชันตัวแปรที่คุณกำลังเริ่มต้นด้วยการใช้ getters และ setters

หมายเหตุ: คุณจะต้องระบุชื่อคลาสแอปพลิเคชันใหม่ในไฟล์ Manifest เพื่อลงทะเบียนและใช้งาน:

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

การตอบสนองต่อการเปลี่ยนแปลงการกำหนดค่า [อัพเดท: เลิกใช้แล้วตั้งแต่ API 13; ดูทางเลือกที่แนะนำ ]

อีกทางเลือกหนึ่งคือคุณสามารถให้แอปพลิเคชันของคุณรับฟังเหตุการณ์ที่ทำให้เกิดการรีสตาร์ท - เช่นการวางแนวและการเปลี่ยนแปลงการมองเห็นแป้นพิมพ์ - และจัดการกับกิจกรรมภายในกิจกรรมของคุณ

เริ่มต้นด้วยการเพิ่ม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">

จากนั้นภายในกิจกรรมจะแทนที่onConfigurationChangedเมธอดและเรียกsetContentViewเพื่อบังคับให้โครงร่าง GUI ถูกทำซ้ำในการวางแนวใหม่

@Override
public void onConfigurationChanged(Configuration newConfig) {
  super.onConfigurationChanged(newConfig);
  setContentView(R.layout.myLayout);
}

17
ฉันไม่คิดว่าวิธีที่สองใช้งานได้ ฉันลองแล้ว หนึ่งกิจกรรมที่มี EditText ฉันเขียนข้อความที่นั่นเปลี่ยนทิศทางและข้อความหายไป / รีเซ็ต
Ted

231
นี่หวังว่าเราจะเห็นวิธีการ onRotate () ในอนาคต แม้จะต้องกังวลเกี่ยวกับสิ่งต่าง ๆ เช่นนี้ก็คือ - ตรงไปตรงมา - น่าผิดหวัง
Kelly Sutton

84
โปรดทราบว่าAndroid Dev Guideข้อควรระวังในการใช้สิ่งนี้: หมายเหตุ: การใช้ ( android:configChanges) ควรหลีกเลี่ยงและใช้เป็นทางเลือกสุดท้าย โปรดอ่านการจัดการการเปลี่ยนแปลงรันไทม์สำหรับข้อมูลเพิ่มเติมเกี่ยวกับวิธีการจัดการการรีสตาร์ทอย่างถูกต้องเนื่องจากการเปลี่ยนแปลงการกำหนดค่า แทนเพื่อยืนยันข้อมูลข้ามเหตุการณ์การหมุนพวกเขาดูเหมือนจะชอบใช้onSaveInstanceState Bundle; หรือเป็น @ Jon-O กล่าวonRetainNonConfigurationInstance ,
Jeffro

19
นั่นเป็นทางออกที่ไม่ดีเพราะมันตอบสนองต่อการเปลี่ยนแปลงการตั้งค่าที่รู้จักเท่านั้น ด้วย Android เวอร์ชันใหม่กว่าการเปลี่ยนแปลงการกำหนดค่าอื่น ๆ อาจเกิดขึ้นซึ่งรหัสนี้จะไม่ตรวจจับ (เพราะจะต้องแสดงรายการการเปลี่ยนแปลงการกำหนดค่าทั้งหมดในรายการ) วิธีการแก้ปัญหาของการบันทึกสถานะด้วยonRetainNonConfigurationChangesความอดทนมากขึ้นข้อบกพร่องและตรงไปข้างหน้า
Bananeweizen

16
ฉันคิดว่าคุณควรเพิ่มการอัปเดตนี้ในคำตอบของคุณ3.2มันค่อนข้างสำคัญ (เพิ่งเจอปัญหานั้น) และอาจถูกมองข้าม
bigstones

185

อัปเดตสำหรับ Android 3.2 และสูงกว่า:

ข้อควรระวัง : เริ่มต้นด้วย Android 3.2 (ระดับ API 13) "ขนาดหน้าจอ" จะเปลี่ยนไปเมื่ออุปกรณ์สลับระหว่างแนวตั้งและแนวนอน ดังนั้นหากคุณต้องการป้องกันไม่ให้รีสตาร์ทรันไทม์เนื่องจากการเปลี่ยนแปลงการวางแนวเมื่อพัฒนาสำหรับ API ระดับ 13 ขึ้นไป (ตามที่ประกาศโดยแอ็ตทริบิวต์ minSdkVersion และ targetSdkVersion) คุณต้องรวม"screenSize"ค่าเพิ่มเติมจาก"orientation"ค่า android:configChanges="orientation|screenSize"นั่นคือคุณจะต้องประกาศ อย่างไรก็ตามหากแอปพลิเคชันของคุณกำหนดเป้าหมาย API ระดับ 12 หรือต่ำกว่ากิจกรรมของคุณจะจัดการกับการเปลี่ยนแปลงการกำหนดค่านี้เสมอ (การเปลี่ยนแปลงการกำหนดค่านี้จะไม่เริ่มกิจกรรมของคุณใหม่แม้ว่าจะทำงานบนอุปกรณ์ Android 3.2 หรือสูงกว่า)


1
ขอบคุณสำหรับคำชี้แจงที่เนื่องจากความคิดเห็นข้างต้นเกี่ยวกับเรื่องนี้เกือบจะส่งฉันไปดูมัน ฉันกำลังกำหนดเป้าหมาย API 8 ในปัจจุบันและรหัสของฉันไม่มี screenSize บน configChanges และสามารถยืนยันได้ว่าทำงานได้ดี (โดยไม่ต้องปรับทิศทางใหม่) บนอุปกรณ์ที่มี ICS ที่ใช้อยู่
Carl

ขอบคุณที่ชี้ให้เห็นว่าฉันมีเพียง android: configChanges = "การวางแนว | screenSize" ชุดและการสลับการวางแนวถูกสร้างกิจกรรมของฉันใหม่และสำหรับชีวิตของฉันฉันไม่สามารถคิดออกว่าทำไม!
Christopher Perry

5
เพิ่มหุ่นยนต์: configChanges ควรจะใช้เป็นทางเลือกสุดท้ายเท่านั้น พิจารณาใช้FragmentsและsetRetainInstanceแทน
Simon Forsberg

จุดสำคัญscreenSizeสำหรับ Android 3.2 และสูงกว่าที่แก้ไขปัญหาของฉันขอบคุณ!
fantouch

127

แทนที่จะพยายามหยุดไม่ให้onCreate()ถูกไล่ออกไปด้วยกันลองลองตรวจสอบการBundle savedInstanceStateถูกส่งเข้าไปในเหตุการณ์เพื่อดูว่ามันว่างหรือไม่

ตัวอย่างเช่นถ้าฉันมีตรรกะบางอย่างที่ควรเรียกใช้เมื่อActivityสร้างขึ้นอย่างแท้จริงไม่ใช่การเปลี่ยนแปลงการวางแนวทุกครั้งฉันจะเรียกใช้ตรรกะนั้นonCreate()เฉพาะในกรณีที่savedInstanceStateเป็นโมฆะเท่านั้น

ไม่เช่นนั้นฉันก็ยังต้องการให้เค้าโครงใหม่วาดใหม่อย่างถูกต้องสำหรับการวางแนว

public void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_game_list);

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

ไม่แน่ใจว่านี่เป็นคำตอบที่ดีที่สุดหรือเปล่า แต่ก็ใช้ได้สำหรับฉัน


6
และคุณกำลังบันทึกรัฐอยู่ที่ไหน?
Ewoks

5
ดูเหมือนว่าจะใช้งานได้สำหรับฉันและดูเหมือนจะเป็นวิธีที่ง่ายที่สุด ฉันสังเกตว่าคุณมี 4 อัพสำหรับสิ่งนี้ (5 รวมถึงของฉัน) เทียบกับ 373 สำหรับแนวคิดเกี่ยวกับแอปพลิเคชันการทำคลาสย่อยซึ่งสำหรับฉันดูเหมือนว่าซับซ้อนกว่ามาก มีข้อเสียวิธีนี้หรือไม่
สตีฟ

4
วิธีนี้ใช้ได้ผลดีมากสำหรับฉัน ฉันสามารถIntent serverintent = new Intent(MainActivity.this, MessageListener.class);และstartService(serverintent);การสร้างserverSocket = new ServerSocket(0xcff2);และSocket client = serverSocket.accept();ด้วยBufferedReader(new InputStreamReader(client.getInputStream()));และสามารถหมุนของฉันหุ่นยนต์และให้การเชื่อมต่อลูกค้า / เซิร์ฟเวอร์ที่ใช้งานยังมีการหมุนกุย ตามคู่มือแล้ว SaveInstanceState จะได้รับการเตรียมใช้งานเมื่อกิจกรรมล่าสุดถูกปิด
เฟร็ด F

3
ฉันไม่เข้าใจสิ่งที่จับ? สิ่งนี้ใช้งานได้ดีและมีความซับซ้อนน้อยกว่าโซลูชั่นอื่น ๆ
RTF

3
นี่เป็นวิธีที่ถูกต้องใน Android วิธีอื่นในการจับการหมุนด้วย configChanges และสิ่งที่ใหญ่เทอะทะและไม่จำเป็น
LukeWaggoner

99

ฉันทำอะไรลงไป...

ในรายการในส่วนกิจกรรมเพิ่ม:

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

3
เพื่อชี้แจง: กับการใช้งานของฉันตอนนี้คุณสามารถมีการเริ่มต้นตัวแปรใน onCreate () และ onConfigurationChanged () จะถูกเรียกเพียงแค่หมุนหน้าจอ ตัวแปรของคุณได้รับการหุ้มฉนวนจากการหมุนหน้าจอ ;-) nice และ ez
คนอยู่ที่ไหนสักแห่ง

2
ฉันทำทุกอย่างตามที่อธิบายไว้ที่นี่ แต่ฉันได้รับ NullPointerException เมื่อฉันพยายามกดปุ่มหลังจากเปลี่ยนการวางแนว มีอะไรผิดปกติ?
Finnboy11

5
โปรดจำไว้ว่าคำตอบของฉันเหมือน 3 ปีและ Android ยังคงพัฒนา ... Simon - คุณมีลิงค์ไปยังรหัสตัวอย่างหรือไม่? นั่นคือสิ่งที่ผู้คนต้องการ
คนที่ไหนสักแห่งที่

3
เมื่อหุ่นยนต์เตือน: configChanges @ SimonAndréForsbergเป็นจริงเพียงถอดความเอกสาร Android การจัดการการเปลี่ยนแปลงรันไทม์มีข้อมูลรายละเอียดเพิ่มเติมเกี่ยวกับทางเลือกอื่น (รวมถึงตัวอย่างโค้ด)
Leif Arne Storset

67

สิ่งที่คุณอธิบายคือพฤติกรรมเริ่มต้น คุณต้องตรวจจับและจัดการกับเหตุการณ์เหล่านี้ด้วยตัวเองโดยการเพิ่ม:

android:configChanges

ต่อรายการของคุณและจากนั้นการเปลี่ยนแปลงที่คุณต้องการจัดการ ดังนั้นสำหรับการปฐมนิเทศคุณจะใช้:

android:configChanges="orientation"

และสำหรับคีย์บอร์ดที่กำลังเปิดหรือปิดคุณจะต้องใช้:

android:configChanges="keyboardHidden"

หากคุณต้องการจัดการทั้งสองคุณสามารถแยกพวกเขาด้วยคำสั่งไปป์เช่น:

android:configChanges="keyboardHidden|orientation"

สิ่งนี้จะทริกเกอร์วิธี onConfigurationChanged ในกิจกรรมที่คุณเรียก หากคุณแทนที่วิธีที่คุณสามารถส่งผ่านในค่าใหม่

หวังว่านี่จะช่วยได้


2
@GregD ฉันรู้ซึ่งเป็นเหตุผลว่าทำไมตอนนี้เป็นเวลาที่ดีในการอัปเดตเพื่อให้สอดคล้องกับสถานการณ์ของวันนี้ จากจำนวน upvotes คำถามนี้ก็ยังคงถูกอ้างอิงจากคำถามอื่น ๆ ใน SO
Simon Forsberg

48

ฉันเพิ่งค้นพบตำนานนี้:

สำหรับการรักษากิจกรรมชีวิตผ่านการเปลี่ยนแปลงการปฐมนิเทศและการจัดการมันผ่านonConfigurationChanged, เอกสารและตัวอย่างโค้ดข้างต้นแนะนำนี้ในไฟล์ Manifest:

<activity android:name=".MyActivity"
      android:configChanges="orientation|keyboardHidden"
      android:label="@string/app_name">

ซึ่งมีประโยชน์พิเศษที่มันใช้งานได้เสมอ

ตำนานโบนัสคือการละเว้นkeyboardHiddenอาจดูเหมือนตรรกะ แต่มันทำให้เกิดความล้มเหลวในการจำลอง (สำหรับ Android 2.1 อย่างน้อย): การระบุเท่านั้นorientationจะทำให้การจำลองเรียกทั้งOnCreateและonConfigurationChangedบางครั้งและเท่านั้นOnCreateเวลาอื่น ๆ

ฉันไม่เห็นความล้มเหลวของอุปกรณ์ แต่ฉันเคยได้ยินเกี่ยวกับตัวจำลองความล้มเหลวสำหรับผู้อื่น ดังนั้นมันจึงคุ้มค่าแก่การบันทึก


14
ข้อควรระวัง: เริ่มต้นด้วย Android 3.2 (ระดับ API 13) "ขนาดหน้าจอ" จะเปลี่ยนไปเมื่ออุปกรณ์สลับระหว่างแนวตั้งและแนวนอน ดังนั้นหากคุณต้องการป้องกันการรีสตาร์ท runtime เนื่องจากการเปลี่ยนแปลงการวางแนวเมื่อพัฒนาสำหรับ API ระดับ 13 ขึ้นไป: android: configChanges = "ปฐมนิเทศ | keyboardHidden | screenSize"
Geltrude

ใช่ตัวจำลองจะดูดครั้งใหญ่ คุณไม่สามารถเชื่อถือได้เพื่อรายงานการเปลี่ยนแปลงการกำหนดค่าอย่างถูกต้อง
IgorGanapolsky

เพิ่มหุ่นยนต์: configChanges ควรจะใช้เป็นทางเลือกสุดท้ายเท่านั้น พิจารณาใช้FragmentsและsetRetainInstanceแทน
Simon Forsberg

38

นอกจากนี้คุณยังอาจพิจารณาใช้วิธีแพลตฟอร์ม Android ของ persisting ข้อมูลในการเปลี่ยนแปลงการวางแนว: และonRetainNonConfigurationInstance()getLastNonConfigurationInstance()

สิ่งนี้ช่วยให้คุณยืนยันข้อมูลในการเปลี่ยนแปลงการกำหนดค่าเช่นข้อมูลที่คุณได้รับจากการดึงข้อมูลเซิร์ฟเวอร์หรืออย่างอื่นที่คำนวณในonCreateหรือตั้งแต่นั้นในขณะที่อนุญาตให้ Android จัดโครงร่างของคุณใหม่Activityโดยใช้ไฟล์ xml สำหรับการวางแนว .

ดูที่นี่หรือที่นี่

ควรสังเกตว่าวิธีการเหล่านี้เลิกใช้แล้ว (แม้ว่าจะมีความยืดหยุ่นมากกว่าการวางแนวการเปลี่ยนแปลงตัวเองตามแนวทางแก้ไขปัญหาข้างต้นส่วนใหญ่แนะนำ) พร้อมคำแนะนำที่ทุกคนเปลี่ยนไปFragmentsใช้แทนsetRetainInstance(true)ในแต่ละวิธีที่Fragmentคุณต้องการเก็บไว้


3
ฉันคิดว่า Fragments และ setRetainInstance เป็นวิธีที่ดีที่สุด (และวิธีที่ Google แนะนำ) ในการทำเช่นนี้ +1 ให้คุณและอีก -1 คนสำหรับคนอื่น ๆ การเพิ่ม android: configChanges ควรใช้เป็นทางเลือกสุดท้าย
Simon Forsberg

32

วิธีการนี้มีประโยชน์ แต่ไม่สมบูรณ์เมื่อใช้แฟรกเมนต์

แฟรกเมนต์มักจะได้รับการสร้างใหม่เมื่อมีการเปลี่ยนแปลงการกำหนดค่า หากคุณไม่ต้องการให้สิ่งนี้เกิดขึ้นใช้

setRetainInstance(true); ในตัวสร้างของ Fragment (s)

สิ่งนี้จะทำให้ชิ้นส่วนถูกเก็บไว้ระหว่างการเปลี่ยนแปลงการกำหนดค่า

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


7
ตกลง ด้วย Android API ล่าสุดดูเหมือนว่า Fragments เป็นวิธีที่ถูกต้องในการจัดการสิ่งนี้ ฉันยังไม่ได้ลองด้วยตัวเอง แต่จากสิ่งที่ฉันรวบรวมได้อ่านหน้านี้โดยทั่วไปแล้วคุณย้าย 99% ของสิ่งที่คุณใช้ในกิจกรรมไปเป็นคลาสย่อยของ Fragment แล้วเพิ่ม Fragment ให้กับกิจกรรม กิจกรรมจะยังคงถูกทำลายและสร้างใหม่ในการหมุนหน้าจอ แต่คุณสามารถบอกหุ่นยนต์ไม่ให้ทำลายชิ้นส่วนโดยใช้setRetainInstance()วิธีการ @Abdo ที่กล่าวถึง
brianmearns

25

ฉันเพิ่งเพิ่ม

     android:configChanges="keyboard|keyboardHidden|orientation"

ในไฟล์รายการและไม่ได้เพิ่มonConfigurationChangedวิธีการใด ๆในกิจกรรมของฉัน

ดังนั้นทุกครั้งที่สไลด์คีย์บอร์ดออกหรือไม่มีอะไรเกิดขึ้น


เพิ่มไป<application ...android:configChanges="keyboard|keyboardHidden|orientation">และมันทำงานอยู่ การตั้งค่าของฉันใน build.gradle:minSdkVersion 15, compileSdkVersion 23, buildToolsVersion "23.0.2"
จูเนียร์Mayhé

19

onCreateวิธีการที่เรียกว่ายังคงแม้เมื่อคุณเปลี่ยนorientationของหุ่นยนต์ ดังนั้นการย้ายฟังก์ชั่นการทำงานหนักทั้งหมดไปยังวิธีนี้จะไม่ช่วยคุณ



17
 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(ยกเว้นเป็นที่พึ่งสุดท้าย) .... แต่แล้ววิธีการอื่นพวกเขาแนะนำทุกDOandroid:configChangesใช้

เป็นประสบการณ์ของฉันที่ผู้เลียนแบบเรียกร้องonCreate()ให้หมุนเสมอ
แต่อุปกรณ์ 1-2 ตัวที่ฉันรันรหัสเดียวกันบน ... ทำไม่ได้ (ไม่แน่ใจว่าทำไมจะมีความแตกต่างใด ๆ )


16

มันง่ายมากเพียงทำตามขั้นตอนต่อไปนี้:

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

สิ่งนี้ใช้ได้กับฉัน:

หมายเหตุ:การวางแนวขึ้นอยู่กับความต้องการของคุณ


15

การเปลี่ยนแปลงที่จะเกิดขึ้นในรายการ 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();
    }
}

15

เพิ่มบรรทัดนี้ในรายการของคุณ: -

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

14

มีหลายวิธีในการทำสิ่งนี้:

บันทึกสถานะกิจกรรม

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

จัดการการเปลี่ยนแปลงปฐมนิเทศด้วยตัวเอง

อีกทางเลือกหนึ่งคือการจัดการการเปลี่ยนแปลงการวางแนวด้วยตัวเอง แต่นี่ไม่ถือว่าเป็นการปฏิบัติที่ดี

เพิ่มลงในไฟล์ Manifest ของคุณ

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

11

วิธีที่ฉันได้พบการทำเช่นนี้คือการใช้onRestoreInstanceStateและonSaveInstanceStateกิจกรรมเพื่อบันทึกบางอย่างในBundle(แม้ว่าคุณไม่ต้องการตัวแปรใด ๆ ที่บันทึกไว้เพียงแค่ใส่บางอย่างในนั้นเพื่อที่Bundleจะไม่ว่างเปล่า) จากนั้นในonCreateวิธีการตรวจสอบเพื่อดูว่าBundleว่างเปล่าและถ้าเป็นแล้วทำเริ่มต้นถ้าไม่ทำแล้ว


11

แม้ว่าจะไม่ใช่ "วิธี Android" ฉันได้รับผลลัพธ์ที่ดีมากโดยการจัดการการวางแนวการเปลี่ยนแปลงตัวเองและเพียงแค่เปลี่ยนตำแหน่งของวิดเจ็ตในมุมมองเพื่อปรับการวางแนวที่คำนึงถึง นี่เร็วกว่าวิธีอื่น ๆ เนื่องจากมุมมองของคุณไม่จำเป็นต้องบันทึกและกู้คืน นอกจากนี้ยังมอบประสบการณ์การใช้งานที่ราบรื่นยิ่งขึ้นให้กับผู้ใช้เนื่องจากวิดเจ็ต respositioned เป็นวิดเจ็ตเดียวกันทั้งหมดเพียงแค่ย้ายและ / หรือปรับขนาด ไม่เพียง แต่สถานะโมเดลเท่านั้น แต่ยังสามารถดูสถานะได้ด้วยวิธีนี้

RelativeLayoutบางครั้งอาจเป็นตัวเลือกที่ดีสำหรับมุมมองที่ต้องปรับตัวเองเป็นครั้งคราว คุณเพียงแค่เตรียมชุดของเค้าโครงเค้าโครงแนวตั้งและชุดของเค้าโครงเค้าโครงภูมิทัศน์ที่มีกฎการวางตำแหน่งสัมพัทธ์ที่แตกต่างกันสำหรับแต่ละวิดเจ็ตลูก จากนั้นในonConfigurationChanged()วิธีการของคุณคุณผ่านหนึ่งที่เหมาะสมเพื่อsetLayoutParams()โทรหาเด็กแต่ละคน หากการควบคุมลูกใด ๆ จำเป็นต้องได้รับการ reoriented ภายในคุณเพียงเรียกวิธีการที่เด็กคนนั้นจะทำการ reorientation เด็กคนนั้นก็เรียกวิธีการต่าง ๆ ในการควบคุมลูกของมันที่ต้องการ reorientation ภายในและอื่น ๆ


ฉันชอบที่จะเห็นโค้ดตัวอย่างของนี้ดูเหมือนจะยอดเยี่ยม!
Henrique de Sousa

8

ทุกครั้งที่หน้าจอหมุนกิจกรรมที่เปิดจะเสร็จสิ้นและ onCreate () จะถูกเรียกอีกครั้ง

1. คุณสามารถทำสิ่งหนึ่งที่บันทึกสถานะของกิจกรรมเมื่อหน้าจอหมุนเพื่อให้คุณสามารถกู้คืนสิ่งเก่าทั้งหมดเมื่อกิจกรรม onCreate () ถูกเรียกอีกครั้ง อ้างอิง ลิงค์นี้

2. หากคุณต้องการป้องกันการเริ่มต้นกิจกรรมใหม่ให้วางบรรทัดต่อไปนี้ในไฟล์ manifest.xml ของคุณ

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

7

คุณจำเป็นต้องใช้เมธอด 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");
    } 

เพื่อดึงและตั้งค่าให้ดูวัตถุมันจะจัดการการหมุนหน้าจอ


ต้องใช้ API ระดับ 22
Mohammad Afrashteh

6

หมายเหตุ:ฉันโพสต์คำตอบนี้หากใครบางคนในอนาคตประสบปัญหาเดียวกันกับฉัน สำหรับฉันบรรทัดต่อไปนี้ไม่เพียงพอ:

android:configChanges="orientation"

เมื่อฉันหมุนหน้าจอเมธอด `onConfigurationChanged (Configuration newConfig) ไม่ได้ถูกเรียก

การแก้ไข:ฉันยังต้องเพิ่ม "screenSize" แม้ว่าปัญหาเกี่ยวกับการวางแนว ดังนั้นใน AndroidManifest.xml - ไฟล์เพิ่มสิ่งนี้:

android:configChanges="keyboardHidden|orientation|screenSize"

จากนั้นใช้วิธีการ onConfigurationChanged(Configuration newConfig)




4

มีคนพูดว่าคุณควรใช้

android:configChanges="keyboardHidden|orientation"

แต่วิธีที่ดีที่สุดและเป็นมืออาชีพที่สุดในการจัดการการหมุนใน Android คือการใช้คลาส Loader มันไม่ใช่คลาสที่มีชื่อเสียง (ฉันไม่รู้ว่าทำไม) แต่มันก็ดีกว่า AsyncTask สำหรับข้อมูลเพิ่มเติมคุณสามารถอ่านบทแนะนำ Android ที่พบในหลักสูตร Android ของ Udacity

แน่นอนว่าเป็นอีกวิธีหนึ่งที่คุณสามารถเก็บค่าหรือมุมมองด้วย onSaveInstanceState และอ่านค่าด้วย onRestoreInstanceState มันขึ้นอยู่กับคุณจริงๆ


ใช่เพิ่มรหัสพิเศษของจำนวนมากเพื่อดู "มืออาชีพ" หรือวิธีการเพียงแค่ติดกับวิธีที่รวดเร็วง่ายจริงและพยายามทำกับคุณลักษณะการกำหนดค่าการเปลี่ยนแปลง
AndroidDev

3

หลังจากผ่านไปสักระยะหนึ่งจากการลองผิดลองถูกฉันพบวิธีแก้ปัญหาที่เหมาะกับความต้องการของฉันในสถานการณ์ส่วนใหญ่ นี่คือรหัส:

การกำหนดค่ารายการ:

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


3

ใช้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);
    }
}

3

ใส่โค้ดด้านล่างนี้ได้ในในActivityAndroid Manifest

android:configChanges="orientation"

สิ่งนี้จะไม่รีสตาร์ทกิจกรรมของคุณเมื่อคุณต้องการเปลี่ยนการวางแนว


2
@Mavamaarten อาจเป็นเพราะคนอื่น ๆ ชี้ไปมันเป็นบทสนทนาที่ไม่ดีและคำตอบอีกสิบคำนั้นได้กล่าวถึงไปแล้ว
MikkoP

3

แก้ไขการวางแนวหน้าจอ (แนวนอนหรือแนวตั้ง) ใน AndroidManifest.xml

android:screenOrientation="portrait" หรือ android:screenOrientation="landscape"

สำหรับonResume()วิธีการของคุณไม่ได้เรียกว่า


5
วิธีแก้ไขสิ่งที่นรกเป็นคำตอบ? ทำไมอุปกรณ์ของเราถึงหมุนถ้าเราล็อคผู้ใช้มัน?
เพิ่มอีก

3

หนึ่งในองค์ประกอบที่ดีที่สุดของ android architechure แนะนำโดย google จะตอบสนองทุกความต้องการของคุณนั่นคือ ViewModel

ที่ได้รับการออกแบบมาเพื่อจัดเก็บและจัดการข้อมูลที่เกี่ยวข้องกับ UI ในวงจรชีวิตและจะช่วยให้ข้อมูลอยู่รอดเมื่อหน้าจอหมุน

class MyViewModel : ViewModel() {

โปรดอ้างอิงถึง: https://developer.android.com/topic/l ไลบรารี/architecture/viewmodel


1

คุณสามารถใช้วัตถุ ViewModel ในกิจกรรมของคุณ

วัตถุ ViewModel จะถูกเก็บไว้โดยอัตโนมัติในระหว่างการเปลี่ยนแปลงการกำหนดค่าเพื่อให้ข้อมูลที่เก็บไว้สามารถใช้งานได้ทันทีในกิจกรรมถัดไปหรืออินสแตนซ์ชิ้นส่วน อ่านเพิ่มเติม:

https://developer.android.com/topic/libraries/architecture/viewmodel

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.