สรุปปัญหา
หมายเหตุ: ในคำตอบนี้ฉันจะอ้างอิงFragmentPagerAdapterและซอร์สโค้ด FragmentStatePagerAdapterแต่การแก้ปัญหาทั่วไปนอกจากนี้ยังควรนำไปใช้
หากคุณกำลังอ่านสิ่งนี้คุณอาจรู้อยู่แล้วว่าFragmentPagerAdapter/ FragmentStatePagerAdapterมีไว้เพื่อสร้างขึ้นFragmentsสำหรับคุณViewPagerแต่จากกิจกรรมสันทนาการ (ไม่ว่าจะจากการหมุนอุปกรณ์หรือระบบฆ่าแอปของคุณเพื่อฟื้นหน่วยความจำ) สิ่งเหล่านี้Fragmentsจะไม่ถูกสร้างขึ้นอีก แต่จะสร้างขึ้นมาแทนอินสแตนซ์ที่ดึงมาจากไฟล์FragmentManager . ตอนนี้บอกว่าคุณActivityต้องการได้รับการอ้างอิงถึงสิ่งเหล่านี้Fragmentsเพื่อทำงานกับพวกเขา คุณไม่ได้มีidหรือtagเหล่านี้สร้างขึ้นFragmentsเพราะพวกเขาตั้งภายในFragmentPagerAdapter ดังนั้นปัญหาคือจะอ้างอิงถึงพวกเขาได้อย่างไรโดยไม่มีข้อมูลนั้น ...
ปัญหาเกี่ยวกับแนวทางแก้ไขปัจจุบัน: อาศัยรหัสภายใน
จำนวนมากของการแก้ปัญหาที่ผมเคยเห็นเกี่ยวกับเรื่องนี้และที่คล้ายกันคำถามพึ่งพาได้รับการอ้างอิงไปยังมีอยู่FragmentโดยการโทรFragmentManager.findFragmentByTag()และการลอกเลียนแบบแท็กสร้างขึ้นภายใน: "android:switcher:" + viewId + ":" + idปัญหานี้คือคุณต้องใช้ซอร์สโค้ดภายในซึ่งอย่างที่เราทราบกันดีว่าไม่รับประกันว่าจะยังคงเหมือนเดิมตลอดไป วิศวกร Android ของ Google สามารถตัดสินใจเปลี่ยนtagโครงสร้างซึ่งจะทำลายโค้ดของคุณได้อย่างง่ายดายทำให้คุณไม่สามารถค้นหาข้อมูลอ้างอิงที่มีอยู่Fragmentsได้
โซลูชันทางเลือกโดยไม่ต้องอาศัยภายใน tag
นี่คือตัวอย่างง่ายๆของวิธีรับการอ้างอิงถึงสิ่งที่Fragmentsส่งคืนโดยFragmentPagerAdapterที่ไม่ได้ขึ้นอยู่กับtagsชุดภายในของไฟล์Fragments. กุญแจสำคัญคือการแทนที่instantiateItem()และบันทึกการอ้างอิงในการมีแทนgetItem()ใน
public class SomeActivity extends Activity {
private FragmentA m1stFragment;
private FragmentB m2ndFragment;
// other code in your Activity...
private class CustomPagerAdapter extends FragmentPagerAdapter {
// other code in your custom FragmentPagerAdapter...
public CustomPagerAdapter(FragmentManager fm) {
super(fm);
}
@Override
public Fragment getItem(int position) {
// Do NOT try to save references to the Fragments in getItem(),
// because getItem() is not always called. If the Fragment
// was already created then it will be retrieved from the FragmentManger
// and not here (i.e. getItem() won't be called again).
switch (position) {
case 0:
return new FragmentA();
case 1:
return new FragmentB();
default:
// This should never happen. Always account for each position above
return null;
}
}
// Here we can finally safely save a reference to the created
// Fragment, no matter where it came from (either getItem() or
// FragmentManger). Simply save the returned Fragment from
// super.instantiateItem() into an appropriate reference depending
// on the ViewPager position.
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// save the appropriate reference depending on position
switch (position) {
case 0:
m1stFragment = (FragmentA) createdFragment;
break;
case 1:
m2ndFragment = (FragmentB) createdFragment;
break;
}
return createdFragment;
}
}
public void someMethod() {
// do work on the referenced Fragments, but first check if they
// even exist yet, otherwise you'll get an NPE.
if (m1stFragment != null) {
// m1stFragment.doWork();
}
if (m2ndFragment != null) {
// m2ndFragment.doSomeWorkToo();
}
}
}
หรือถ้าคุณชอบที่จะทำงานร่วมกับtagsแทนตัวแปรสมาชิกระดับ / การอ้างอิงไปยังFragmentsคุณยังสามารถคว้าtagsชุดโดยFragmentPagerAdapterในลักษณะเดียวกัน: หมายเหตุ: นี้ไม่ได้นำไปใช้FragmentStatePagerAdapterเพราะมันไม่ได้ตั้งค่าเมื่อมีการสร้างของtagsFragments
@Override
public Object instantiateItem(ViewGroup container, int position) {
Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
// get the tags set by FragmentPagerAdapter
switch (position) {
case 0:
String firstTag = createdFragment.getTag();
break;
case 1:
String secondTag = createdFragment.getTag();
break;
}
// ... save the tags somewhere so you can reference them later
return createdFragment;
}
โปรดทราบว่าวิธีนี้ไม่ได้อาศัยการเลียนแบบtagชุดภายในFragmentPagerAdapterและใช้ API ที่เหมาะสมในการดึงข้อมูลเหล่านี้แทน วิธีนี้แม้ว่าการtagเปลี่ยนแปลงในเวอร์ชันอนาคตSupportLibraryคุณจะยังคงปลอดภัย
อย่าลืมว่าขึ้นอยู่กับการออกแบบของคุณActivityสิ่งที่Fragmentsคุณพยายามดำเนินการอาจจะยังไม่มีอยู่จริงหรืออาจยังไม่มีอยู่ดังนั้นคุณต้องพิจารณาด้วยการnullตรวจสอบก่อนใช้ข้อมูลอ้างอิงของคุณ
นอกจากนี้หากคุณทำงานด้วยFragmentStatePagerAdapterคุณก็ไม่ต้องการที่จะอ้างอิงถึงคุณอย่างหนักหน่วงFragmentsเพราะคุณอาจมีข้อมูลเหล่านี้จำนวนมากและการอ้างอิงที่ยากจะเก็บไว้ในความทรงจำโดยไม่จำเป็น บันทึกการFragmentอ้างอิงในWeakReferenceตัวแปรแทนการบันทึกมาตรฐาน แบบนี้:
WeakReference<Fragment> m1stFragment = new WeakReference<Fragment>(createdFragment);
// ...and access them like so
Fragment firstFragment = m1stFragment.get();
if (firstFragment != null) {
// reference hasn't been cleared yet; do work...
}