สรุปปัญหา
หมายเหตุ: ในคำตอบนี้ฉันจะอ้างอิง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
เพราะมันไม่ได้ตั้งค่าเมื่อมีการสร้างของtags
Fragments
@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...
}