วิธีรับชิ้นส่วนที่มีอยู่เมื่อใช้ FragmentPagerAdapter


99

ฉันมีปัญหาในการทำให้ชิ้นส่วนของฉันสื่อสารกันActivityโดยใช้FragmentPagerAdapterคลาสตัวช่วยซึ่งใช้การจัดการแท็บและรายละเอียดทั้งหมดของการเชื่อมต่อViewPagerกับTabHostไฟล์. ฉันได้ดำเนินการFragmentPagerAdapterเช่นเดียวกับที่เดียวกับที่มันมีให้โดยโครงการ Android ตัวอย่างSupport4Demos

คำถามหลักคือฉันจะได้รับชิ้นส่วนเฉพาะจากFragmentManagerเมื่อฉันไม่มีทั้ง Id หรือ Tag ได้อย่างไร FragmentPagerAdapterกำลังสร้างส่วนและสร้างรหัสและแท็กโดยอัตโนมัติ



@ jk2K คำถามนี้จะซ้ำกับคำถามที่ถูกถามได้อย่างไร
Davi

@Dawit ซ้ำเหมือนป้ายไม่เกี่ยวเวลาคำถามต่อมามียอดวิวสูงขึ้น
jk2K

คำตอบส่วนใหญ่ที่นี่ใช้ไม่ได้ในการผลิตดังนั้นโปรดตรวจสอบคำตอบของฉันในstackoverflow.com/a/54280113/2413303
EpicPandaForce

คำตอบ:


194

สรุปปัญหา

หมายเหตุ: ในคำตอบนี้ฉันจะอ้างอิง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...
}

3
นี่เป็นวิธีแก้ปัญหาที่ดีมาก แต่ดูเหมือนว่าจะสูญเสียประสิทธิภาพหากคุณไม่รู้ว่าจะส่งผ่านชิ้นส่วนไปกี่ชิ้น
Riot Goes Woof

3
@Zorpix คุณสามารถจัดเก็บส่วนสร้างใน HashMap: map.put (ตำแหน่ง createdFragment);
Tom Bevelander

12
สิ่งนี้สมควรได้รับเครื่องหมายถูก! วิธีที่ชาญฉลาดและครอบคลุมมากในการบรรลุเป้าหมายนี้ คุณช่วยฉันได้มากขอบคุณ!
young_souvlaki

2
ตอนแรกวิธีแก้ปัญหานี้ดูซับซ้อนเกินไปฉันจึงข้ามไป ในที่สุดฉันก็กลับมาหามันเพราะคำตอบอื่น ๆ ไม่เป็นที่พอใจ และมันก็ไม่ยากอย่างที่คิด
Suragch

1
ไม่จำเป็นที่จะแทนที่อะไรและคุณจริงไม่ควรinstantiateItemจะเอาชนะ วิธีการที่เหมาะสมที่จะทำคือการโทร instantiateItemในonCreateวิธีการของกิจกรรมของคุณที่ล้อมรอบด้วยและstartUpdate finishUpdateดูคำตอบของฉันสำหรับรายละเอียด
morgwai

82

ฉันพบคำตอบสำหรับคำถามของฉันตามโพสต์ต่อไปนี้: การนำชิ้นส่วนกลับมาใช้ใหม่ใน Fragmentpageradapter

ฉันได้เรียนรู้บางสิ่ง:

  1. getItem(int position)ในFragmentPagerAdapterชื่อที่ค่อนข้างทำให้เข้าใจผิดว่าวิธีนี้ใช้ทำอะไรได้บ้าง สร้างส่วนใหม่โดยไม่ส่งคืนชิ้นส่วนที่มีอยู่ ตามความหมายวิธีนี้ควรเปลี่ยนชื่อเป็นอย่างcreateItem(int position)ใน Android SDK ดังนั้นวิธีนี้ไม่ได้ช่วยให้เราได้เศษ
  2. จากคำอธิบายในโพสต์การสนับสนุน FragmentPagerAdapterholds อ้างอิงถึง FragmentPagerAdapterholds เก่าคุณควรปล่อยให้การสร้างแฟรกเมนต์เป็นFragmentPagerAdapterและหมายความว่าคุณไม่มีการอ้างอิงถึง Fragment หรือแท็ก หากคุณมีแท็กส่วนแม้ว่าคุณสามารถดึงการอ้างอิงถึงจากโดยการเรียกFragmentManager findFragmentByTag()เราต้องการวิธีค้นหาแท็กของส่วนย่อยในตำแหน่งหน้าที่กำหนด

สารละลาย

เพิ่มวิธีการช่วยเหลือต่อไปนี้ในคลาสของคุณเพื่อดึงแท็กแฟรกเมนต์และส่งไปยังfindFragmentByTag()เมธอด

private String getFragmentTag(int viewPagerId, int fragmentPosition)
{
     return "android:switcher:" + viewPagerId + ":" + fragmentPosition;
}

บันทึก! นี่เป็นวิธีเดียวกับที่FragmentPagerAdapterใช้เมื่อสร้างชิ้นส่วนใหม่ ดูลิงก์นี้http://code.google.com/p/openintents/source/browse/trunk/compatibility/AndroidSupportV2/src/android/support/v2/app/FragmentPagerAdapter.java#104


Btw มีข้อมูลเพิ่มเติมเกี่ยวกับหัวข้อนี้ใน Q&A: stackoverflow.com/questions/6976027/…
Thomas

1
พารามิเตอร์อินพุต viewId คืออะไร? มุมมองไหน?
Nilzor

@Nilzor viewId คือ id ของ ViewPager
Dr.jacky

บางทีแทนที่จะเดาแท็กแฟรกเมนต์อาจบอกแท็กถึงกิจกรรมในonAttach()?
อ่าง

4
นี่ไม่ใช่วิธีที่ถูกต้อง คำตอบของ @Tony Chan คือวิธีที่ดีที่สุดและถูกต้อง
Morteza Rastgoo

17

คุณไม่จำเป็นต้องลบล้างinstantiateItemหรือพึ่งพาความเข้ากันได้กับmakeFragmentNameวิธีการภายในด้วยการสร้างแท็กแฟรกเมนต์ด้วยตนเอง
instantiateItemเป็นวิธีการสาธารณะดังนั้นคุณสามารถและควรเรียกonCreateวิธีนี้ในกิจกรรมของคุณที่ล้อมรอบด้วยการโทรstartUpdateและfinishUpdateวิธีการตามที่อธิบายไว้ในPagerAdapter javadoc :

การเรียกใช้เมธอด PagerAdapter startUpdate (ViewGroup) บ่งชี้ว่าเนื้อหาของ ViewPager กำลังจะเปลี่ยนแปลง การเรียกใช้อย่างน้อยหนึ่งครั้งเพื่อสร้างอินสแตนซ์รายการ (ViewGroup, int) และ / หรือ destroyItem (ViewGroup, int, Object) จะตามมาและการสิ้นสุดการอัปเดตจะส่งสัญญาณโดยการเรียกให้ FinishUpdate (ViewGroup)

จากนั้นคุณสามารถจัดเก็บข้อมูลอ้างอิงไปยังอินสแตนซ์ของแฟรกเมนต์ของคุณใน local vars ได้ด้วยวิธีการข้างต้นหากคุณต้องการ ดูตัวอย่าง:

public class MyActivity extends AppCompatActivity {

    Fragment0 tab0; Fragment1 tab1;

    @Override protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.myLayout);
        ViewPager viewPager = (ViewPager) findViewById(R.id.myViewPager);
        MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
        viewPager.setAdapter(adapter);
        ((TabLayout) findViewById(R.id.tabs)).setupWithViewPager(viewPager);

        adapter.startUpdate(viewPager);
        tab0 = (Fragment0) adapter.instantiateItem(viewPager, 0);
        tab1 = (Fragment1) adapter.instantiateItem(viewPager, 1);
        adapter.finishUpdate(viewPager);
    }

    class MyPagerAdapter extends FragmentPagerAdapter {

        public MyPagerAdapter(FragmentManager manager) {super(manager);}

        @Override public int getCount() {return 2;}

        @Override public Fragment getItem(int position) {
            if (position == 0) return new Fragment0();
            if (position == 1) return new Fragment1();
            return null;  // or throw some exception
        }

        @Override public CharSequence getPageTitle(int position) {
            if (position == 0) return getString(R.string.tab0);
            if (position == 1) return getString(R.string.tab1);
            return null;  // or throw some exception
        }
    }
}

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

สิ่งสำคัญที่ควรทราบคือแม้ว่าคุณจะไม่จำเป็นต้องได้รับการอ้างอิงถึงชิ้นส่วนของคุณคุณก็ยังควรเรียกinstantiateItemแท็บทั้งหมดของคุณที่ล้อมรอบด้วยstartUpdate/ finishUpdateในonCreateวิธีการของคุณดังนี้:

    adapter.startUpdate(viewPager);
    // ignoring return values of the below 2 calls, just side effects matter:
    adapter.instantiateItem(viewPager, 0);
    adapter.instantiateItem(viewPager, 1);
    adapter.finishUpdate(viewPager);

ถ้าคุณไม่ได้ทำเช่นนั้นแล้วคุณจะเสี่ยงว่ากรณีส่วนของคุณจะไม่ได้รับการมุ่งมั่นที่จะFragmentManager: เมื่อกิจกรรมของคุณจะกลายเป็นเบื้องหน้าinstantiateItemจะถูกเรียกโดยอัตโนมัติเพื่อให้ได้ชิ้นส่วนของคุณ แต่startUpdate/ finishUpdate อาจ ไม่ได้ (ขึ้นอยู่กับรายละเอียดการปฏิบัติ) และสิ่งที่พวกเขาโดยทั่วไป do คือเริ่มต้น / กระทำ a FragmentTransaction.
ซึ่งอาจส่งผลให้การอ้างอิงไปยังอินสแตนซ์แฟรกเมนต์ที่สร้างขึ้นสูญหายไปอย่างรวดเร็ว (เช่นเมื่อคุณหมุนหน้าจอ) และสร้างใหม่บ่อยเกินความจำเป็น ขึ้นอยู่กับว่าชิ้นส่วนของคุณ "หนัก" แค่ไหนชิ้นส่วนนั้นอาจมีผลด้านประสิทธิภาพที่ไม่สำคัญ
ยิ่งไปกว่านั้นในกรณีเช่นนี้ส่วนของชิ้นส่วนที่เก็บไว้ในตัวแทนท้องถิ่นอาจกลายเป็นของเก่า: หากแพลตฟอร์ม Android พยายามที่จะได้มาจากFragmentManagerเหตุผลใดก็ตามมันจะล้มเหลวและจะสร้างและใช้งานใหม่ในขณะที่ตัวแทนของคุณจะยังคงอ้างอิงถึงเวอร์ชันเก่า


1
อาจเป็นทางออกที่ดีที่สุดในบางกรณี แต่จะเกิดอะไรขึ้นถ้า FragmentManger จะฆ่า Fragment แล้วท่องซ้ำ?
woltran

1
@woltran FragmentManagerไม่สามารถสุ่มฆ่าได้ ( ทำลายเป็นคำที่ถูกต้องที่นี่) ของคุณFragment(คิดว่าจะเกิดอะไรขึ้นถ้าตัดสินใจฆ่าสิ่งFragmentที่กำลังแสดงอยู่;)) โดยทั่วไปวงจรชีวิตของ a Fragmentจะถูกผูกไว้กับมันActivity(ดูรายละเอียดในgithub.com/xxv/android-lifecycle ) -> a FragmentจะถูกทำลายActivityได้ก็ต่อเมื่อมันถูกทำลาย ในกรณีดังกล่าวเมื่อนำทางผู้ใช้กลับไปรับActivityของมันonCreateจะถูกเรียกอีกครั้งและตัวอย่างใหม่ของFragmentจะถูกสร้างขึ้น
morgwai

นี่คือคำตอบที่แท้จริง
MJ Studio

คุณควรสร้างแฟรกเมนต์จริงๆแทนที่จะพึ่งพาพวกมันที่สร้างขึ้นเมื่อผู้ใช้เลื่อน ViewPager เป็นต้น
Yar

@ ใช่คุณควรจริงๆ เอกสารที่ตัดตอนมาที่ฉันระบุไว้อย่างชัดเจนและส่วน "ข้อมูลเพิ่มเติมบางส่วน" จะอธิบายถึงสาเหตุ
morgwai

11

วิธีที่ฉันทำคือกำหนด Hashtable ของ WeakReferences ดังนี้:

protected Hashtable<Integer, WeakReference<Fragment>> fragmentReferences;

จากนั้นฉันเขียน getItem () วิธีการดังนี้:

@Override
public Fragment getItem(int position) {

    Fragment fragment;
    switch(position) {
    case 0:
        fragment = new MyFirstFragmentClass();
        break;

    default:
        fragment = new MyOtherFragmentClass();
        break;
    }

    fragmentReferences.put(position, new WeakReference<Fragment>(fragment));

    return fragment;
}

จากนั้นคุณสามารถเขียนวิธีการ:

public Fragment getFragment(int fragmentId) {
    WeakReference<Fragment> ref = fragmentReferences.get(fragmentId);
    return ref == null ? null : ref.get();
}

ดูเหมือนว่าจะทำงานได้ดีและฉันพบว่าแฮ็กน้อยกว่าไฟล์

"android:switcher:" + viewId + ":" + position

เคล็ดลับเนื่องจากไม่ได้ขึ้นอยู่กับวิธีการใช้งาน FragmentPagerAdapter แน่นอนว่า Fragment ถูกปล่อยโดย FragmentPagerAdapter หรือยังไม่ได้สร้าง getFragment จะคืนค่า null

หากใครพบสิ่งผิดปกติเกี่ยวกับแนวทางนี้ยินดีรับฟังความคิดเห็น


int fragmentIdควรเปลี่ยนชื่อเป็นint position
lmaooooo

7
ฉันใช้วิธีการที่คล้ายกันมาก แต่จะล้มเหลวเมื่อเพจเจอร์ถูกสร้างขึ้นจากบันเดิลที่บันทึกไว้ เช่น: กิจกรรมเข้าสู่พื้นหลังและกลับมาที่เบื้องหน้าหลังจากเรียก onSavedStateInstance () ในกรณีนี้จะไม่มีการเรียกเมธอด getItem ()
Anoop

อะไรคือเหตุผลในการสร้างแผนที่ของคุณเองเนื่องจากมีหนึ่งใน FragmentManager ซึ่งอัปเดตอยู่เสมอ ดูคำตอบของฉันสำหรับรายละเอียด
morgwai

นอกจากนี้ความจริงที่ว่าชิ้นส่วนถูกทำลายไม่ได้รับประกันว่าจะไม่มีการอ้างอิงที่ชัดเจนถึงมัน (แม้ว่าจะเป็นไปได้ แต่ก็ไม่รับประกัน) ซึ่งในกรณีนี้แผนที่ของคุณจะยังคงมีชิ้นส่วนเก่าอยู่
morgwai

1
สิ่งนี้จะทำงานไม่ถูกต้องหลังจากระบบสร้าง Fragments ขึ้นมาใหม่
EpicPandaForce

10

ฉันสร้างวิธีการนี้ซึ่งใช้ได้ผลสำหรับฉันเพื่อรับการอ้างอิงถึงส่วนปัจจุบัน

public static Fragment getCurrentFragment(ViewPager pager, FragmentPagerAdapter adapter) {
    try {
        Method m = adapter.getClass().getSuperclass().getDeclaredMethod("makeFragmentName", int.class, long.class);
        Field f = adapter.getClass().getSuperclass().getDeclaredField("mFragmentManager");
        f.setAccessible(true);
        FragmentManager fm = (FragmentManager) f.get(adapter);
        m.setAccessible(true);
        String tag = null;
        tag = (String) m.invoke(null, pager.getId(), (long) pager.getCurrentItem());
        return fm.findFragmentByTag(tag);
    } catch (NoSuchMethodException e) {
        e.printStackTrace();
    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (IllegalAccessException e) {
        e.printStackTrace();
    } catch (InvocationTargetException e) {
        e.printStackTrace();
    } catch (NoSuchFieldException e) {
        e.printStackTrace();
    } 
    return null;
}

ยินดีที่ได้จำการสร้าง Method and Field นอกวิธีการเพื่อประสิทธิภาพที่ดีขึ้น
Marcos Vasconcelos

2

วิธีแก้ปัญหาที่แนะนำโดย @ personne3000 นั้นดี แต่มีปัญหาอย่างหนึ่ง: เมื่อกิจกรรมไปที่พื้นหลังและถูกฆ่าโดยระบบ (เพื่อให้ได้หน่วยความจำว่าง) จากนั้นคืนค่าfragmentReferencesจะว่างเปล่าเพราะgetItemจะไม่เป็น เรียกว่า.

ชั้นเรียนด้านล่างจัดการสถานการณ์ดังกล่าว:

public abstract class AbstractHolderFragmentPagerAdapter<F extends Fragment> extends FragmentPagerAdapter {

    public static final String FRAGMENT_SAVE_PREFIX = "holder";
    private final FragmentManager fragmentManager; // we need to store fragment manager ourselves, because parent's field is private and has no getters.

    public AbstractHolderFragmentPagerAdapter(FragmentManager fm) {
        super(fm);
        fragmentManager = fm;
    }

    private SparseArray<WeakReference<F>> holder = new SparseArray<WeakReference<F>>();

    protected void holdFragment(F fragment) {
        holdFragment(holder.size(), fragment);
    }

    protected void holdFragment(int position, F fragment) {
        if (fragment != null)
            holder.put(position, new WeakReference<F>(fragment));
    }

    public F getHoldedItem(int position) {
        WeakReference<F> ref = holder.get(position);
        return ref == null ? null : ref.get();
    }

    public int getHolderCount() {
        return holder.size();
    }

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) { // code inspired by Google's FragmentStatePagerAdapter implementation
        super.restoreState(state, loader);
        Bundle bundle = (Bundle) state;
        for (String key : bundle.keySet()) {
            if (key.startsWith(FRAGMENT_SAVE_PREFIX)) {
                int index = Integer.parseInt(key.substring(FRAGMENT_SAVE_PREFIX.length()));
                Fragment f = fragmentManager.getFragment(bundle, key);
                holdFragment(index, (F) f);
            }
        }
    }

    @Override
    public Parcelable saveState() {
        Bundle state = (Bundle) super.saveState();
        if (state == null)
            state = new Bundle();

        for (int i = 0; i < holder.size(); i++) {
            int id = holder.keyAt(i);
            final F f = getHoldedItem(i);
            String key = FRAGMENT_SAVE_PREFIX + id;
            fragmentManager.putFragment(state, key, f);
        }
        return state;
    }
}

1

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

นี่คือแนวทางที่ไม่ได้อาศัยการใช้ FragmentPagerAdapter เพื่อรับแท็ก แทนที่ instantiateItem () ซึ่งจะส่งคืนส่วนที่สร้างจาก getItem () หรือพบจากตัวจัดการส่วน

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object value =  super.instantiateItem(container, position);

    if (position == 0) {
        someFragment = (SomeFragment) value;
    } else if (position == 1) {
        anotherFragment = (AnotherFragment) value;
    }

    return value;
}

0

ดูโพสต์นี้เกี่ยวกับการส่งคืนชิ้นส่วนจาก FragmentPagerAdapter ขึ้นอยู่กับว่าคุณรู้ดัชนีของส่วนของคุณ - แต่จะถูกตั้งค่าใน getItem () (ในการสร้างอินสแตนซ์เท่านั้น)


0

ฉันจัดการเพื่อแก้ปัญหานี้โดยใช้รหัสแทนแท็ก (ฉันใช้ฉันกำหนด FragmentStatePagerAdapter ซึ่งใช้ FragmentStatePagerAdapter ที่กำหนดเองของฉันซึ่งฉันแทนที่เมธอด onAttach โดยที่คุณบันทึก id ไว้ที่ใดที่หนึ่ง:

@Override
public void onAttach(Context context){
    super.onAttach(context);
    MainActivity.fragId = getId();
}

จากนั้นคุณก็สามารถเข้าถึงส่วนย่อยได้อย่างง่ายดายภายในกิจกรรม:

Fragment f = getSupportFragmentManager.findFragmentById(fragId);

0

ฉันไม่รู้ว่านี่เป็นแนวทางที่ดีที่สุดหรือเปล่า แต่ไม่มีอะไรอื่นที่ใช้ได้ผลสำหรับฉัน ตัวเลือกอื่น ๆ ทั้งหมดรวมถึง getActiveFragment ส่งคืนค่าว่างหรือทำให้แอปหยุดทำงาน

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

ในส่วน:

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);
    try {
        mListener = (OnListInteractionListener) activity;
        mListener.setListFrag(this);
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString()
                + " must implement OnFragmentInteractionListener");
    }
}

จากนั้นในกิจกรรม:

@Override
public void setListFrag(MyListFragment lf) {
    if (mListFragment == null) {
        mListFragment = lf;
    }
}

และสุดท้ายในกิจกรรม onCreate ():

if (savedInstanceState != null) {
    if (mListFragment != null)
        mListFragment.setListItems(items);
}

วิธีนี้จะแนบส่วนที่มองเห็นได้จริงเข้ากับกิจกรรมโดยไม่ต้องสร้างใหม่


0

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

ฉันมีกิจกรรมการโฮสต์ที่ใช้ ViewPager กับ FragmentStatePagerAdapter เพื่อรับการอ้างอิงถึง Fragment ที่สร้างโดย FragmentStatePagerAdapter ฉันได้สร้างอินเทอร์เฟซการเรียกกลับภายในคลาสแฟรกเมนต์:

public interface Callbacks {
    public void addFragment (Fragment fragment);
    public void removeFragment (Fragment fragment);
}

ในกิจกรรมการโฮสต์ฉันใช้อินเทอร์เฟซและสร้าง LinkedHasSet เพื่อติดตามชิ้นส่วน:

public class HostingActivity extends AppCompatActivity implements ViewPagerFragment.Callbacks {

    private LinkedHashSet<Fragment> mFragments = new LinkedHashSet<>();

    @Override
    public void addFragment (Fragment fragment) {
        mFragments.add(fragment);
    }

    @Override
    public void removeFragment (Fragment fragment) {
        mFragments.remove(fragment);
    }
}

ภายในคลาส ViewPagerFragment ฉันได้เพิ่มแฟรกเมนต์ในรายการภายใน onAttach และลบออกภายใน onDetach:

public class ViewPagerFragment extends Fragment {

    private Callbacks mCallbacks;

    public interface Callbacks {
        public void addFragment (Fragment fragment);
        public void removeFragment (Fragment fragment);
    } 

    @Override
    public void onAttach (Context context) {
        super.onAttach(context);
        mCallbacks = (Callbacks) context;
        // Add this fragment to the HashSet in the hosting activity
        mCallbacks.addFragment(this);
    }

    @Override
    public void onDetach() {
        super.onDetach();
        // Remove this fragment from the HashSet in the hosting activity
        mCallbacks.removeFragment(this);
        mCallbacks = null;
    }
}

ภายในกิจกรรมการโฮสต์คุณจะสามารถใช้ mFragments เพื่อวนซ้ำผ่านส่วนย่อยที่มีอยู่ใน FragmentStatePagerAdapter


0

คลาสนี้ทำเคล็ดลับโดยไม่ต้องอาศัยแท็กภายใน คำเตือน: ควรเข้าถึง Fragments โดยใช้เมธอด getFragment ไม่ใช่ getItem

public class ViewPagerAdapter extends FragmentPagerAdapter {

    private final Map<Integer, Reference<Fragment>> fragments = new HashMap<>();
    private final List<Callable0<Fragment>> initializers = new ArrayList<>();
    private final List<String> titles = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager fm) {
        super(fm);
    }

    void addFragment(Callable0<Fragment> initializer, String title) {
        initializers.add(initializer);
        titles.add(title);
    }

    public Optional<Fragment> getFragment(int position) {
        return Optional.ofNullable(fragments.get(position).get());
    }

    @Override
    public Fragment getItem(int position) {
        Fragment fragment =  initializers.get(position).execute();
        return fragment;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        fragments.put(position, new WeakReference<>(fragment));
        return fragment;
    }

    @Override
    public int getCount() {
        return initializers.size();
    }

    @Override
    public CharSequence getPageTitle(int position) {
        return titles.get(position);
    }
}

-5

ลองใช้รหัสนี้ต่อไป

public class MYFragmentPAdp extends FragmentPagerAdapter {

    public MYFragmentPAdp(FragmentManager fm) {
        super(fm);
    }

    @Override
    public int getCount() {
        return 2;
    }

     @Override
     public Fragment getItem(int position) {
         if (position == 0)
             Fragment fragment = new Fragment1();
         else (position == 1)
             Fragment fragment = new Fragment2();
         return fragment;
     }
}

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

Fragment Fragment = YourCustomFragmentClass ใหม่ (); เขียนที่นี่ตรวจสอบสิ่งนี้
Najib Ahmed Puthawala

ฉันยังไม่เข้าใจว่าสิ่งนี้เปลี่ยนแปลงไปอย่างไรที่คุณกำลังสร้างส่วนใหม่แทนการรับชิ้นส่วนที่มีอยู่ ..
Ismar Slomic

คุณยังคงเริ่มต้นและส่งคืนสำหรับแฟรกเมนต์ที่คุณกำหนดเองเท่านั้นเช่น Fragment fragment = new YourFragment (); ส่งคืนส่วน;
Najib Ahmed Puthawala
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.