เหตุใดฉันจึงต้องการหลีกเลี่ยงสิ่งก่อสร้างที่ไม่ใช่ค่าเริ่มต้นในเศษ


173

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

Avoid non-default constructors in fragments: use a default constructor plus Fragment#setArguments(Bundle) instead

มีใครบอกฉันได้ไหมว่าทำไมนี่จึงไม่ใช่ความคิดที่ดี

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

public static class MenuFragment extends ListFragment {
    public ListView listView1;
    Categories category;

    //this is my "non-default" constructor
    public MenuFragment(Categories category){
        this.category = category;
    }....

หากไม่ใช้ตัวสร้างที่ไม่ใช่ค่าเริ่มต้น


2
เป็นไปได้ที่ซ้ำกันของแนวปฏิบัติที่ดีที่สุดสำหรับการสร้างอินสแตนซ์ Android ใหม่และstackoverflow.com/questions/10450348/…และstackoverflow.com/questions/11602433/ …และอื่น ๆ ที่น่าจะเป็นไปได้
CommonsWare

3
ไม่พวกเขาไม่ได้ช่วย พวกเขาไม่ตอบคำถามของฉัน แต่ขอบคุณไม่น้อย :)
BlackHatSamurai

31
@BlaineOmega จริง ๆ แล้วสิ่งนี้โดยเฉพาะ: stackoverflow.com/a/11602478/321697ตอบคำถามของคุณอย่างแน่นอน ในการเปลี่ยนทิศทางหรือเหตุการณ์อื่น ๆ ที่ทำให้ Fragment ถูกสร้างขึ้นใหม่ Android จะใช้ตัวสร้างเริ่มต้นเช่นเดียวกับ Bundle ที่ส่งผ่านเป็นอาร์กิวเมนต์ หากคุณกำลังใช้ตัวสร้างแบบกำหนดเองทันทีที่มีการสร้างชิ้นส่วนขึ้นใหม่เนื่องจากหนึ่งในเหตุการณ์เหล่านี้สิ่งใดก็ตามที่คุณทำในตัวสร้างแบบกำหนดเองจะหายไป
Kevin Coppock

1
ขอบคุณ แต่นั่นเป็นคำตอบว่าทำไม แต่ไม่ใช่วิธีการ
BlackHatSamurai

ที่อยู่ในลิงค์แรกและลิงก์ที่สองในความคิดเห็นดั้งเดิมของฉัน
CommonsWare

คำตอบ:


110

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

Bundle args = new Bundle();
args.putLong("key", value);
yourFragment.setArguments(args);

หลังจากนั้นในข้อมูลการเข้าถึงชิ้นส่วนของคุณ:

Type value = getArguments().getType("key");

นั่นคือทั้งหมดที่


3
จะส่งผ่านวัตถุได้อย่างไร ฉันต้องการผ่านวัตถุบริบทหรือวัตถุอื่น ๆ
Adil Malik

12
การรวมกลุ่มสามารถดำเนินการวัตถุ Java ที่เป็นอนุกรมเช่นเดียวกับParcelableวัตถุ นอกจากนี้คุณไม่ควรผ่านContextเพราะข้อมูลที่สามารถเข้าถึงได้ผ่านทางส่วนของวิธีการgetActivity()
krakatoa

ในส่วนที่ต้องทำเช่นนี้Type value = getArguments().getType("key");?
มูฮัมหมัดบาบา

4
@ Muhammad Babar: ถ้าฉันเป็นคุณฉันจะเพิ่มเข้าไปในnewInstance()วิธีการ ตัวอย่างเช่นpublic static FragmentName newInstance(your variables){}. ตามที่แนะนำในเอกสาร Android อย่าสร้างตัวสร้างพร้อมพารามิเตอร์เนื่องจากค่าเริ่มต้นหนึ่งตัว (ไม่มีพารามิเตอร์) จะถูกเรียกโดยอัตโนมัติหลังจากรีสตาร์ทแฟรกเมนต์ของคุณ
nistv4n

@MuhammadBabar onCreateView ไม่เป็นไร
chanjianyi

272

ดูเหมือนว่าไม่มีคำตอบที่จริงตอบ "ทำไมต้องใช้ชุดรวมสำหรับการส่งพารามิเตอร์มากกว่าตัวสร้างเริ่มต้นที่ไม่ใช่"

เหตุผลที่คุณควรจะได้รับการส่งผ่านพารามิเตอร์ผ่านมัดเป็นเพราะเมื่อระบบคืนค่าfragment(เช่นเกี่ยวกับการเปลี่ยนแปลงการตั้งค่า) bundleก็จะเรียกคืนโดยอัตโนมัติ

การเรียกกลับที่ชอบonCreateหรือonCreateViewควรอ่านพารามิเตอร์จากbundle- ด้วยวิธีนี้คุณรับประกันได้ว่าจะเรียกคืนสถานะของfragmentอย่างถูกต้องให้อยู่ในสถานะเดียวกันกับที่fragmentถูกกำหนดค่าเริ่มต้นด้วย (โปรดทราบว่าสถานะนี้อาจแตกต่างจากonSaveInstanceState bundleที่ผ่านไปonCreate/onCreateView)

คำแนะนำในการใช้newInstance()วิธีการสแตติกเป็นเพียงคำแนะนำ คุณสามารถใช้คอนสตรัคเตอร์ที่ไม่ใช่ค่าเริ่มต้น แต่ต้องแน่ใจว่าคุณใส่พารามิเตอร์การเริ่มต้นในส่วนbundleของเนื้อหาของตัวสร้าง และอ่านพารามิเตอร์เหล่านั้นในonCreate()หรือonCreateView()วิธีการ


2
อธิบายได้ดี ขอบคุณ ถ้าฉันเป็นคนถามคำถามฉันจะให้เห็บคุณ
Karue Benson Karue

5
คุณไม่สามารถใช้ตัวสร้างที่ไม่ใช่ค่าเริ่มต้น (ไม่ว่าจะด้วยเหตุผลใดก็ตาม) .... มันให้ข้อผิดพลาดคอมไพเลอร์ (เคยเป็นคำเตือน)
MPavlak

51

คุณFragmentไม่ควรมีคอนสตรัคเตอร์เพราะFragmentManagerมันสร้างอินสแตนซ์ได้อย่างไร คุณควรมีnewInstance()วิธีการสแตติกที่กำหนดไว้กับพารามิเตอร์ที่คุณต้องการจากนั้นรวมเข้าด้วยกันและตั้งเป็นอาร์กิวเมนต์ของแฟรกเมนต์ซึ่งคุณสามารถเข้าถึงได้ในภายหลังด้วยBundleพารามิเตอร์

ตัวอย่างเช่น:

public static MyFragment newInstance(int title, String message) {
    MyFragment fragment = new MyFragment();
    Bundle bundle = new Bundle(2);
    bundle.putInt(EXTRA_TITLE, title);
    bundle.putString(EXTRA_MESSAGE, message);
    fragment.setArguments(bundle);
    return fragment ;
}

และอ่านข้อโต้แย้งเหล่านี้ได้ที่onCreate:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    title = getArguments().getInt(EXTRA_TITLE);
    message = getArguments().getString(EXTRA_MESSAGE);

    //...
}

วิธีนี้หากแยกออกและเชื่อมต่ออีกครั้งสถานะของวัตถุสามารถจัดเก็บผ่านอาร์กิวเมนต์ได้เช่นเดียวbundlesกับที่แนบมากับIntents


9

หากคุณใช้พารามิเตอร์สำหรับบางคลาส ลองนี้

SomeClass mSomeInstance;
public static final MyFragment newInstance(SomeClass someInstance){
    MyFragment f = new MyFragment();
    f.mSomeInstance = someInstance;
    return f;
}

5
นี่เป็นข้อเสนอแนะที่ไม่ดี เมื่อชิ้นส่วนถูกสร้างใหม่โดย a FragmentManagerคุณจะสูญเสีย mSomeInstance
Yaroslav Mytkalyk

ตกลง SomeClass ควรจะเป็นพัสดุและเก็บไว้ในชุดโดยใช้ setArguments ()
Jake_

1

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

ในโครงการของฉันฉันใช้ Kotlin และใช้แฟรกเมนต์กับตัวสร้าง no-arg หลักและตัวสร้างรองสำหรับอาร์กิวเมนต์ที่เพิ่งเก็บไว้ในบันเดิลและตั้งค่าเป็นอาร์กิวเมนต์ Fragment ทุกอย่างทำงานได้ดี


0

ถ้าแฟรกเมนต์ใช้คอนสตรัคเตอร์ที่ไม่ใช่ค่าเริ่มต้นหลังจากเปลี่ยนการกำหนดค่าแฟรกเมนต์จะสูญเสียข้อมูลทั้งหมด

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