แนวทางปฏิบัติที่ดีที่สุดสำหรับการสร้างอินสแตนซ์ Android ใหม่


707

ฉันได้เห็นวิธีปฏิบัติทั่วไปสองประการเพื่อยกตัวอย่างชิ้นส่วนใหม่ในแอปพลิเคชัน:

Fragment newFragment = new MyFragment();

และ

Fragment newFragment = MyFragment.newInstance();

ตัวเลือกที่สองใช้ประโยชน์จากวิธีการคงที่newInstance()และโดยทั่วไปมีวิธีการดังต่อไปนี้

public static Fragment newInstance() 
{
    MyFragment myFragment = new MyFragment();
    return myFragment;
}

ตอนแรกฉันคิดว่าประโยชน์หลักคือความจริงที่ว่าฉันสามารถโอเวอร์โหลดเมธอด newInstance () เพื่อให้ความยืดหยุ่นเมื่อสร้างอินสแตนซ์ใหม่ของ Fragment - แต่ฉันสามารถทำได้โดยการสร้าง Constructor ที่โอเวอร์โหลดสำหรับ Fragment

ฉันพลาดอะไรไปหรือเปล่า?

ประโยชน์ของวิธีการหนึ่งเหนืออีกวิธีหนึ่งคืออะไร หรือเป็นเพียงการปฏิบัติที่ดี?


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

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

คำตอบ:


1139

หาก Android ตัดสินใจที่จะสร้าง Fragment ของคุณขึ้นใหม่ในภายหลังมันจะเรียกตัวสร้างแบบไม่มีอาร์กิวเมนต์ของแฟรกเมนต์ของคุณ ดังนั้นการสร้างมากเกินไปไม่ใช่วิธีแก้ปัญหา

เมื่อพูดถึงวิธีการส่งข้อมูลไปยัง Fragment ของคุณเพื่อให้สามารถใช้งานได้หลังจากสร้างแฟรกเมนต์ใหม่โดย Android คือการส่งบันเดิลไปยังsetArgumentsวิธีการ

ตัวอย่างเช่นถ้าเราต้องการส่งจำนวนเต็มไปยังส่วนที่เราจะใช้สิ่งที่ชอบ:

public static MyFragment newInstance(int someInt) {
    MyFragment myFragment = new MyFragment();

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    myFragment.setArguments(args);

    return myFragment;
}

และในภายหลังใน Fragment onCreate()คุณสามารถเข้าถึงจำนวนเต็มนั้นได้โดยใช้:

getArguments().getInt("someInt", 0);

Bundle นี้จะสามารถใช้งานได้แม้ว่า Android จะสร้างขึ้นมาใหม่ก็ตาม

หมายเหตุ: setArgumentsสามารถเรียกได้เฉพาะก่อนที่แฟรกเมนต์จะแนบกับกิจกรรม

วิธีการนี้ได้รับการบันทึกไว้ในเอกสารอ้างอิงสำหรับนักพัฒนาซอฟต์แวร์ Android: https://developer.android.com/reference/android/app/Fragment.html


7
@Vlasto แต่น่าเสียดายที่วิธีการคงที่ไม่สามารถแทนที่
AJD

8
@yydl ฉันคิดว่าฉันขาดอะไรบางอย่างที่นี่คุณไม่สามารถใช้ตัวสร้างที่นี่ได้อีกต่อไปอันที่สร้าง Bundle และเรียก setArguments () ยังเพราะมันจะถูกเรียกโดยรหัสของคุณเท่านั้น (ไม่ใช่เมื่อ Android สร้างของคุณใหม่ ส่วน)?
Mike Tunnicliffe

9
@mgibson คุณต้องใช้บันเดิลหากคุณต้องการให้ข้อมูลพร้อมใช้งานเมื่อสร้างแฟรกเมนต์ใหม่ในภายหลัง
yydl

114
การถูกบังคับให้สร้างตัวสร้างแบบไม่มีอาร์กิวเมนต์สำหรับแฟรกเมนต์อาจเป็น gotcha ที่ใหญ่ที่สุดในการเขียนโปรแกรมทั้งหมดได้ทุกที่ มันบังคับให้เปลี่ยนกระบวนทัศน์ที่สมบูรณ์ในการสร้างวัตถุและการเริ่มต้น หากคุณยังใหม่กับ Android และสะดุดในหัวข้อนี้โปรดอ่านคำตอบข้างบนซ้ำแล้วซ้ำอีก
rmirabelle

9
ฉันจะโต้แย้งกับการยืนยันว่า อันดับแรกประเภทความปลอดภัยคือข้อกังวลด้านภาษาไม่ใช่ข้อกังวลเกี่ยวกับกรอบ ประการที่สอง IMO กรอบงานกำลังก้าวเข้าสู่ "สิ่งที่ API ของคุณต้องไม่ทำ" หากฉันต้องการส่งห้องสมุดของรัฐสภาไปยังตัวสร้างส่วนของฉันฉันควรได้รับอนุญาต คอนสตรัคเตอร์ "no-args" โดยทั่วไปจะฆ่าการใช้งานของการฉีดพึ่งพาในชิ้นส่วน - yuck ที่สำคัญ
rmirabelle

95

ประโยชน์เพียงอย่างเดียวในการใช้สิ่งnewInstance()ที่ฉันเห็นมีดังต่อไปนี้:

  1. คุณจะมีที่เดียวที่รวมอาร์กิวเมนต์ทั้งหมดที่แฟรกเมนต์ใช้และคุณไม่จำเป็นต้องเขียนโค้ดด้านล่างทุกครั้งที่คุณสร้างอินสแตนซ์ของชิ้นส่วน

    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    args.putString("someString", someString);
    // Put any other arguments
    myFragment.setArguments(args);
  2. เป็นวิธีที่ดีในการบอกคลาสอื่น ๆ ว่าอาร์กิวเมนต์ใดที่คาดว่าจะทำงานได้อย่างซื่อสัตย์ (แม้ว่าคุณควรจะสามารถจัดการกับกรณีและปัญหาได้หากไม่มีการรวมอาร์กิวเมนต์ในอินสแตนซ์แฟรกเมนต์)

ดังนั้นสิ่งที่ฉันใช้คือการใช้สแตติกnewInstance()เพื่อยกตัวอย่างชิ้นส่วนนั้นเป็นวิธีปฏิบัติที่ดี


4
1) สิ่งนี้แตกต่างจากการวางตรรกะใน Constructor อย่างไร ทั้งสองเป็นสถานที่เดียวที่คุณรวมตรรกะนี้ 2) พารามิเตอร์ของโรงงานแบบสแตติกมีความแตกต่างจากพารามิเตอร์ของคอนสตรัคเตอร์อย่างไร ทั้งคู่บอกว่ามีการโต้แย้งอะไร ประเด็นของฉันคือมันเป็นกระบวนทัศน์ที่แตกต่างแน่นอน แต่ไม่มีประโยชน์ชัดเจนในเรื่องนี้มากกว่าการใช้ตัวสร้าง
RJ Cuthbertson

2
คุณไม่สามารถใช้ตัวสร้างแบบกำหนดเองสำหรับส่วน Framework ใช้ตัวสร้างอาร์กิวเมนต์ที่ไม่มีสำหรับการกู้คืนแฟรกเมนต์
500865

5
ใช่ฉันเห็นด้วยกับคุณที่นั่น ฉันกำลังพูดถึงแนวคิดว่าไม่มีประโยชน์ในการใช้รูปแบบสแตติกจากโรงงานแทนที่จะใช้ตัวสร้างที่มากเกินไปและในทางกลับกัน คะแนนทั้งสองของคุณใช้ได้ทั้งสองแบบ ไม่มีประโยชน์ในการใช้อีกอันหนึ่ง Android บังคับให้คุณใช้รูปแบบสแตติกจากโรงงาน - แต่ไม่มีประโยชน์ที่จะใช้อย่างใดอย่างหนึ่ง
RJ Cuthbertson

pastebin.com/EYJzES0j
RJ Cuthbertson

@RJCuthbertson ประโยชน์ที่เป็นไปได้คือความสามารถในการสร้างและส่งคืนคลาสย่อยของคลาสของวิธีการคงที่ของโรงงานเช่นเพื่อส่งคืนคลาสย่อยที่เหมาะสมสำหรับสถานการณ์
เร่งด่วน

62

นอกจากนี้ยังมีวิธีอื่น:

Fragment.instantiate(context, MyFragment.class.getName(), myBundle)

หากฉันไม่เข้าใจผิดนี่เป็นไปได้เฉพาะเมื่อคุณใช้ห้องสมุดสนับสนุน Android
Timo

2
พยายามทำสิ่งนี้กับไลบรารีการสนับสนุน แต่ใน onCreateView (ในส่วนของฉัน) การรวมกลุ่มที่ส่งเป็นโมฆะดังนั้นฉันจึงไปกับตัวเลือก setArguments / getArguments และทำงานได้ (สำหรับทุกคนที่อ่านข้อความนี้)
Jrop

1
น่าสนใจฉันไม่เคยเห็นวิธีการนี้มาก่อน มันมีข้อดีกว่าวิธีอื่น ๆ ในการทำให้ชิ้นส่วนเป็นอินสแตนซ์หรือไม่?
IgorGanapolsky

23
จากเอกสารของนักพัฒนาซอฟต์แวร์ ,instantiate() Creates a new instance of a Fragment with the given class name. This is the same as calling its empty constructor.
ไบรอันโบว์แมน

2
แม้ว่าพวกเขาจะพูดถึงเช่นเดียวกับการเรียกตัวสร้างที่ว่างเปล่า "args.setClassLoader (f.getClass () getClassLoader ().);" ถูกเรียกภายใต้การโต้แย้งของ Bundle
GökhanBarış Aker

49

ในขณะที่ @ yydl ให้เหตุผลที่น่าสนใจว่าทำไมnewInstanceวิธีการจึงดีกว่า:

หาก Android ตัดสินใจที่จะสร้าง Fragment ของคุณขึ้นใหม่ในภายหลังมันจะเรียกตัวสร้างแบบไม่มีอาร์กิวเมนต์ของแฟรกเมนต์ของคุณ ดังนั้นการสร้างมากเกินไปไม่ใช่วิธีแก้ปัญหา

ก็ยังคงเป็นไปได้มากทีเดียวที่จะใช้คอนสตรัค ในการดูว่าทำไมนี่เป็นครั้งแรกที่เราต้องดูว่าทำไม Android ใช้วิธีแก้ปัญหาข้างต้น

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

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

ในการแก้ปัญหา, Android ขอให้คุณเก็บข้อมูลโดยใช้Bundle(เรียกsetArguments()) YourFragmentซึ่งจากนั้นจะสามารถเข้าถึงได้จาก อาร์กิวเมนต์bundles ได้รับการคุ้มครองโดย Android และด้วยเหตุนี้มีการรับประกันว่าถาวร

วิธีหนึ่งในการตั้งค่าบันเดิลนี้คือการใช้newInstanceวิธีการคงที่:

public static YourFragment newInstance (int data) {
    YourFragment yf = new YourFragment()
    /* See this code gets executed immediately on your object construction */
    Bundle args = new Bundle();
    args.putInt("data", data);
    yf.setArguments(args);
    return yf;
}

อย่างไรก็ตามตัวสร้าง:

public YourFragment(int data) {
    Bundle args = new Bundle();
    args.putInt("data", data);
    setArguments(args);
}

สามารถทำสิ่งเดียวกับnewInstanceวิธีการ

ตามปกติแล้วสิ่งนี้จะล้มเหลวและเป็นหนึ่งในเหตุผลที่ Android ต้องการให้คุณใช้newInstanceวิธีนี้:

public YourFragment(int data) {
    this.data = data; // Don't do this
}

เพื่อเป็นการอธิบายเพิ่มเติมต่อไปนี้เป็นคลาส Fragment ของ Android:

/**
 * Supply the construction arguments for this fragment.  This can only
 * be called before the fragment has been attached to its activity; that
 * is, you should call it immediately after constructing the fragment.  The
 * arguments supplied here will be retained across fragment destroy and
 * creation.
 */
public void setArguments(Bundle args) {
    if (mIndex >= 0) {
        throw new IllegalStateException("Fragment already active");
    }
    mArguments = args;
}

โปรดทราบว่า Android ขอให้มีการตั้งค่าข้อโต้แย้งเฉพาะที่การก่อสร้างและรับประกันว่าสิ่งเหล่านี้จะถูกเก็บไว้

แก้ไข : ตามที่ระบุไว้ในความคิดเห็นโดย @JHH หากคุณให้ตัวสร้างที่กำหนดเองที่ต้องมีข้อโต้แย้งบางอย่างแล้ว Java จะไม่ให้ชิ้นส่วนของคุณด้วยตัวสร้างเริ่มต้นที่ไม่มี arg ดังนั้นสิ่งนี้จะทำให้คุณต้องกำหนดคอนสตรัคเตอร์หาเรื่องไม่ซึ่งเป็นรหัสที่คุณสามารถหลีกเลี่ยงได้ด้วยnewInstanceวิธีการจากโรงงาน

แก้ไข : Android ไม่อนุญาตให้ใช้ตัวสร้างที่โอเวอร์โหลดสำหรับชิ้นส่วนอีกต่อไป คุณต้องใช้newInstanceวิธีการ


เมื่อไหร่ที่จะแสดงให้เห็นถึงการใช้ Android: configChanges = "ปฐมนิเทศ | keyboardHidden | screenSize"?
ลุคอัลลิสัน

1
ตอนนี้ Android Studio โยนข้อผิดพลาดสำหรับตัวสร้างที่ไม่ใช่ค่าเริ่มต้นทั้งหมดในแฟรกเมนต์ดังนั้นจึงไม่ทำงานอีกต่อไป
Sheharyar

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

3
มีเหตุผลที่ลึกซึ้งอย่างหนึ่ง คุณมีอิสระในการสร้างตัวสร้างของคุณเองด้วยการขัดแย้ง แต่ยังคงมีความต้องการที่จะคอนสตรัคไม่มีหาเรื่องเช่นกัน เนื่องจากคลาสมีคอนสตรัคเตอร์แบบไม่มีอาร์กิวเมนต์โดยปริยายเสมอเว้นเสียแต่ว่าคอนสตรัคเตอร์ที่มี args ถูกกำหนดไว้อย่างชัดเจนหมายความว่าคุณจะต้องกำหนดทั้งคอนสตรัคเตอร์ของคุณและคอนสตรัคเตอร์no-ARG อย่างชัดเจนหรือระบบจะไม่สามารถเรียกใช้ คอนสตรัคเตอร์ no-arg ฉันเชื่อว่านี่คือเหตุผลที่คำแนะนำคือแทนที่จะใช้วิธีการคงที่จากโรงงาน - เพียงลดความเสี่ยงในการลืมกำหนด constructor ที่ไม่มีอาร์กิวเมนต์
JHH

@JHH ที่จะล้มเหลวในการรวบรวมเวลาดังนั้นมันจึงไม่ใช่ความเสี่ยง อย่างไรก็ตามปัญหาที่นี่คือตัวสร้างการบรรทุกเกินพิกัดเป็นกระบวนทัศน์การเขียนโปรแกรมที่สำคัญจะถูกปฏิเสธโดย Android
ps95

20

ฉันไม่เห็นด้วยกับ yydi ตอบว่า:

หาก Android ตัดสินใจที่จะสร้าง Fragment ของคุณขึ้นใหม่ในภายหลังมันจะเรียกตัวสร้างแบบไม่มีอาร์กิวเมนต์ของแฟรกเมนต์ของคุณ ดังนั้นการสร้างมากเกินไปไม่ใช่วิธีแก้ปัญหา

ฉันคิดว่ามันเป็นทางออกและเป็นสิ่งที่ดีนี่คือเหตุผลที่มันถูกพัฒนาโดยภาษาหลักของ Java

เป็นความจริงที่ว่าระบบ Android สามารถทำลายและสร้างใหม่Fragmentได้ ดังนั้นคุณสามารถทำสิ่งนี้:

public MyFragment() {
//  An empty constructor for Android System to use, otherwise exception may occur.
}

public MyFragment(int someInt) {
    Bundle args = new Bundle();
    args.putInt("someInt", someInt);
    setArguments(args);
}

มันจะช่วยให้คุณสามารถดึงsomeIntจากgetArguments()หลังบนแม้ว่าจะFragmentถูกสร้างขึ้นใหม่โดยระบบ นี่เป็นทางออกที่สง่างามกว่าคอนstaticสตรัคเตอร์

สำหรับผู้staticสร้างความคิดเห็นของฉันไม่มีประโยชน์และไม่ควรใช้ นอกจากนี้พวกเขาจะ จำกัด คุณหากในอนาคตคุณต้องการขยายFragmentและเพิ่มฟังก์ชันการทำงานให้กับตัวสร้าง ด้วยstaticConstructor คุณไม่สามารถทำได้

ปรับปรุง:

Android เพิ่มการตรวจสอบที่ตั้งค่าสถานะตัวสร้างที่ไม่ใช่ค่าเริ่มต้นทั้งหมดพร้อมข้อผิดพลาด
ฉันขอแนะนำให้ปิดการใช้งานด้วยเหตุผลดังกล่าวข้างต้น


4
ประโยชน์อีกประการของการมีวิธีการแบบคงที่ซึ่งฉันไม่ได้กล่าวถึงข้างต้นคือคุณไม่สามารถตั้งค่าคุณสมบัติโดยไม่ตั้งใจได้
yydl

4
นอกจากนี้เกี่ยวกับจุดของคุณเกี่ยวกับ "ขยายส่วนนี้" วิธีการนี้จะไม่ดีจริง ๆ ถ้าคุณเคยขยายชั้นเรียน การเรียก super จะทำให้การเรียก setArguments () มีผลเฉพาะกับเด็กหรือผู้ปกครอง แต่ไม่ใช่ทั้งสองอย่าง!
yydl

2
@yydle คุณสามารถหลีกเลี่ยงสถานการณ์นี้ได้โดยการเรียกใช้อาร์กิวเมนต์เพื่อเริ่มต้น Bundle ย่อย วิธี Java ดีกว่าเสมอ
Ilya Gazman

9
จริง แต่นั่นเป็นอีกเหตุผลหนึ่งที่กระตุ้นให้ผู้คนใช้รูปแบบที่ Google ได้แนะนำไว้ แน่นอนเราทุกคนเห็นพ้องกันว่าทางออกของคุณนั้นเป็นไปได้ทางเทคนิค 100% ในแบบเดียวกันมีหลายวิธีในการทำสิ่งต่าง ๆ มากมาย อย่างไรก็ตามคำถามก็คือว่าดีที่สุดหรือไม่ และฉันรู้สึกอย่างยิ่งว่าการใช้ตัวสร้างไม่ได้แสดงถึงลักษณะที่แท้จริงของสิ่งที่ควรจะทำงาน
yydl

3
ฉันเห็นด้วยกับ @yydl ว่าการสร้างแบบคงที่ดีกว่า ประโยชน์อีกอย่างหนึ่งคือการพึ่งพาการฉีดของการพึ่งพาใหม่ในอนาคต - คอนสตรัคเตอร์ไม่เหมาะสมสำหรับสิ่งนั้นและอาจทำให้เกิดการเปลี่ยนรหัสมากขึ้น (หรือเพิ่มคอนสตรัคเตอร์เพิ่มเติม)
Boon

19

บางรหัสkotlin :

companion object {
    fun newInstance(first: String, second: String) : SampleFragment {
        return SampleFragment().apply {
            arguments = Bundle().apply {
                putString("firstString", first)
                putString("secondString", second)
            }
        }
    }
}

และคุณสามารถรับข้อโต้แย้งด้วย:

val first: String by lazy { arguments?.getString("firstString") ?: "default"}
val second: String by lazy { arguments?.getString("secondString") ?: "default"}

3

วิธีปฏิบัติที่ดีที่สุดสำหรับอินสแตนซ์ของแฟรกเมนต์ที่มีข้อโต้แย้งใน Android คือมีวิธีคงที่จากโรงงานในแฟรกเมนต์ของคุณ

public static MyFragment newInstance(String name, int age) {
    Bundle bundle = new Bundle();
    bundle.putString("name", name);
    bundle.putInt("age", age);

    MyFragment fragment = new MyFragment();
    fragment.setArguments(bundle);

    return fragment;
}

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

คุณสามารถค้นหาข้อมูลเพิ่มเติมเกี่ยวกับแนวปฏิบัติที่ดีที่สุดในการสร้างอินสแตนซ์ของชิ้นส่วนด้วยอาร์กิวเมนต์ที่นี่


2

ตั้งแต่คำถามเกี่ยวกับแนวปฏิบัติที่ดีที่สุดฉันจะเพิ่มซึ่งบ่อยครั้งเป็นความคิดที่ดีที่จะใช้วิธีไฮบริดสำหรับสร้างชิ้นส่วนเมื่อทำงานกับบริการเว็บ REST บางแห่ง

เราไม่สามารถส่งผ่านวัตถุที่ซับซ้อนได้ตัวอย่างเช่นบางรุ่นของผู้ใช้สำหรับกรณีของการแสดงส่วนของผู้ใช้

แต่สิ่งที่เราสามารถทำได้คือการตรวจสอบonCreateผู้ใช้นั้น! = null และถ้าไม่ - จากนั้นก็นำเขาจากชั้นข้อมูลมิฉะนั้น - ใช้ที่มีอยู่

วิธีนี้เราจะเพิ่มความสามารถในการสร้างโดย userId ทั้งสองกรณีในส่วนของ Android และ snappiness สำหรับการกระทำของผู้ใช้เช่นเดียวกับความสามารถในการสร้างชิ้นส่วนโดยถือเพื่อคัดค้านตัวเองหรือเพียง id

บางสิ่งเช่นนี้:

public class UserFragment extends Fragment {
    public final static String USER_ID="user_id";
    private User user;
    private long userId;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        userId = getArguments().getLong(USER_ID);
        if(user==null){
            //
            // Recreating here user from user id(i.e requesting from your data model,
            // which could be services, direct request to rest, or data layer sitting
            // on application model
            //
             user = bringUser();
        }
    }

    public static UserFragment newInstance(User user, long user_id){
        UserFragment userFragment = new UserFragment();
        Bundle args = new Bundle();
        args.putLong(USER_ID,user_id);
        if(user!=null){
            userFragment.user=user;
        }
        userFragment.setArguments(args);
        return userFragment;

    }

    public static UserFragment newInstance(long user_id){
        return newInstance(null,user_id);
    }

    public static UserFragment newInstance(User user){
        return newInstance(user,user.id);
    }
}

3
คุณพูดว่า: "เราไม่สามารถส่งวัตถุที่ซับซ้อนได้ตัวอย่างเช่นบางรุ่นของผู้ใช้" - มันไม่จริงเราทำได้ เช่นนี้: User user = /*...*/ นำผู้ใช้ไปยังบันเดิล: Bundle bundle = new Bundle(); bundle.putParcelable("some_user", user); และนำผู้ใช้ออกมาจากข้อโต้แย้ง: User user = getArguments().getParcelable("some_user"); วัตถุนั้นจะต้องใช้ส่วนต่อประสาน Parcelable ลิงก์
Adam Varhegyi

3
ดีใช่ แต่เมื่อชั้นเรียนมีความซับซ้อนและมีผู้อ้างอิงไปยังวัตถุอื่น ... โดยส่วนตัวฉันชอบที่จะทำให้มันง่ายไม่ว่าฉันจะมีวัตถุหรือฉันไม่ต้องการแล้วจะได้รับมัน
Tigra

1

ใช้รหัสนี้ 100% แก้ปัญหาของคุณ

ใส่รหัสนี้ในส่วนแรกของการจัดเรียง

public static yourNameParentFragment newInstance() {

    Bundle args = new Bundle();
    args.putBoolean("yourKey",yourValue);
    YourFragment fragment = new YourFragment();
    fragment.setArguments(args);
    return fragment;
}

ตัวอย่างนี้ส่งข้อมูลบูลีน

และในSecendFragment

yourNameParentFragment name =yourNameParentFragment.newInstance();
   Bundle bundle;
   bundle=sellDiamondFragments2.getArguments();
  boolean a= bundle.getBoolean("yourKey");

ค่าต้องอยู่ในส่วนแรกเป็นแบบคงที่

รหัสความสุข


0

วิธีที่ดีที่สุดในการสร้างอินสแตนซ์ของแฟรกเมนต์คือใช้เมธอดFragment.instantiate ที่เป็นค่าเริ่มต้นหรือสร้างเมธอดจากโรงงานเพื่ออินสแตนซ์ของแฟรกเมนต์
ข้อควรระวัง: สร้างตัวสร้างที่ว่างเปล่าหนึ่งตัวในแฟรกเมนต์อื่น ๆ


0

เมื่อเร็ว ๆ นี้ฉันอยู่ที่นี่ แต่สักวันฉันเพิ่งรู้ว่าอาจช่วยคุณได้เล็กน้อย

หากคุณใช้งานจาวาก็ไม่มีอะไรเปลี่ยนแปลงอีกมากมาย แต่สำหรับนักพัฒนา kotlin ต่อไปนี้เป็นตัวอย่างบางส่วนที่ฉันคิดว่าสามารถทำให้คุณเป็นฐานในการทำงาน:

  • ส่วนย่อยของผู้ปกครอง:
inline fun <reified T : SampleFragment> newInstance(text: String): T {
    return T::class.java.newInstance().apply {
        arguments = Bundle().also { it.putString("key_text_arg", text) }
    }
}
  • โทรปกติ
val f: SampleFragment = SampleFragment.newInstance("ABC")
// or val f = SampleFragment.newInstance<SampleFragment>("ABC")
  • คุณสามารถขยายการดำเนินการของ parent parent ในคลาส fragment child โดย:
fun newInstance(): ChildSampleFragment {
    val child = UserProfileFragment.newInstance<ChildSampleFragment>("XYZ")
    // Do anything with the current initialized args bundle here
    // with child.arguments = ....
    return child
}

การเข้ารหัสที่มีความสุข


-2

setArguments()ไม่มีประโยชน์ มันทำให้เกิดความยุ่งเหยิงเท่านั้น

public class MyFragment extends Fragment {

    public String mTitle;
    public String mInitialTitle;

    public static MyFragment newInstance(String param1) {
        MyFragment f = new MyFragment();
        f.mInitialTitle = param1;
        f.mTitle = param1;
        return f;
    }

    @Override
    public void onSaveInstanceState(Bundle state) {
        state.putString("mInitialTitle", mInitialTitle);
        state.putString("mTitle", mTitle);
        super.onSaveInstanceState(state);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
        if (state != null) {
            mInitialTitle = state.getString("mInitialTitle");
            mTitle = state.getString("mTitle");
        } 
        ...
    }
}

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

@ Asagen ฉันชอบความคิดเห็นของคุณเกี่ยวกับการเปรียบเทียบค่าเริ่มต้นและค่าผู้ใช้ ฉันแก้ไขโค้ดและพิจารณาว่ามันยังคงเหมือนกันและชัดเจนgetArgumentsสิ่งwitout เกี่ยวกับonViewCreatedขอบเขต ... เราสามารถคืนค่าสถานะบันเดิลได้ แต่ฉันชอบทำให้onCreateViewเบาและรวดเร็วและทำการเริ่มต้นอย่างหนักทั้งหมดภายในonActivityCreatedเพราะFragment.getActivity()บางครั้งชอบที่จะกลับมาnullและเนื่องจากonAttach()การเปลี่ยนแปลงในเวอร์ชันใหม่ของ API 23
Vadim Star

ทั้งหมดที่คุณได้ที่นี่ย้าย setและเข้าไปในget Arguments saveInstanceStateคุณกำลังทำสิ่งเดียวกันกับที่ทำภายใต้ประทุน
OneCricketeer

1
@ cricket_007 หรือเพียงแค่ตรงข้าม การใช้saveInstanceStateคือ "ภายใต้ประทุน" และการใช้Argumentsคือการทำซ้ำของฟังก์ชั่นที่ทำให้คุณตรวจสอบซ้ำ: Argumentsค่าแรกแล้วsaveInstanceStateค่า เพราะคุณต้องใช้saveInstanceStateวิธีใด ๆ แล้วArguments... พวกมันไม่จำเป็น
Vadim Star

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

-12

ฉันเชื่อว่าฉันมีวิธีแก้ปัญหาอย่างง่ายสำหรับเรื่องนี้

public class MyFragment extends Fragment{

   private String mTitle;
   private List<MyObject> mObjects;

   public static MyFragment newInstance(String title, List<MyObject> objects)
   MyFragment myFrag = new MyFragment();
   myFrag.mTitle = title;
   myFrag.mObjects = objects;
   return myFrag;
   }

12
mObjects จะถูกล้างหากมีการสร้าง MyFragment ขึ้นใหม่ (ผู้ใช้ไปที่หน้าจอหลักของอุปกรณ์และเปิดแอปที่เปิดทิ้งไว้ที่ MyFragment ในภายหลัง) คุณสามารถเก็บ mObjects ได้โดยส่ง MyFragment เป็นชุดของอาร์กิวเมนต์
ynnadkrap

1
นอกจากนี้วิธีการแบบคงที่วิธีการเข้าถึงตัวแปรสมาชิกไม่คงที่อย่างไร
OrhanC1

2
@ynnadkrap คุณถูกต้องใช้มัดเป็นวิธีที่จะไปที่นี่
Stefan Bogaard

2
@ OrhanC1 ตามตัวอย่างรหัสนี้เมธอดสแตติกไม่ได้เข้าถึงตัวแปรสมาชิก อินสแตนซ์ของ MyFragment กำลังเข้าถึงสมาชิก ไม่มีข้อผิดพลาดที่นี่ อย่างไรก็ตามฉันไม่แนะนำคำตอบนี้ให้ใครเพราะเมื่อชิ้นส่วนของคุณจะถูกลบออกจากหน่วยความจำเพื่อเปิดพื้นที่บางส่วนโดยระบบปฏิบัติการ android แล้วหลังจากรีสตาร์ทกิจกรรมและชิ้นส่วนนี้จะถูกสร้างขึ้นด้วยตัวสร้างที่ว่างเปล่าเริ่มต้นโดยไม่ต้องกำหนดตัวแปรมด
Gunhan

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