ดึงชิ้นส่วนจาก ViewPager


248

ฉันใช้ViewPagerพร้อมกับ a FragmentStatePagerAdapterเพื่อโฮสต์แฟรกเมนต์ที่แตกต่างกันสามรายการ

  • [Fragment1]
  • [Fragment2]
  • [Fragment3]

เมื่อฉันต้องการที่จะได้รับFragment1จากในViewPagerFragmentActivity

ปัญหาคืออะไรและฉันจะแก้ไขได้อย่างไร

คำตอบ:


504

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

สิ่งที่เกี่ยวกับการแก้ปัญหานี้การเอาชนะinstantiateItem()และdestroyItem()ของคุณFragment(State)PagerAdapter:

public class MyPagerAdapter extends FragmentStatePagerAdapter {
    SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();

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

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

    @Override
    public Fragment getItem(int position) {
        return MyFragment.newInstance(...); 
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Fragment fragment = (Fragment) super.instantiateItem(container, position);
        registeredFragments.put(position, fragment);
        return fragment;
    }

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        registeredFragments.remove(position);
        super.destroyItem(container, position, object);
    }

    public Fragment getRegisteredFragment(int position) {
        return registeredFragments.get(position);
    }
}

ดูเหมือนว่าจะใช้ได้กับฉันเมื่อต้องรับมือกับ Fragments ที่มีอยู่ ชิ้นส่วนที่ยังไม่ได้รับการ instantiated จะกลับ null getRegisteredFragmentเมื่อโทร แต่ผมเคยใช้นี้มากที่สุดที่จะได้รับในปัจจุบันFragmentออกจากViewPager: และนี้จะไม่กลับมาadapater.getRegisteredFragment(viewPager.getCurrentItem())null

ฉันไม่ได้ตระหนักถึงข้อเสียอื่น ๆ ของการแก้ปัญหานี้ ถ้ามีฉันอยากจะรู้


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

7
จะเกิดอะไรขึ้นเมื่อMyPagerAdapterถูกทำลายเนื่องจากวงจรชีวิต (เช่นการหมุน) จะไม่registerdFragmentsสูญหาย การจะActivity/ Fragmentใช้MyPagerAdapterต้องบันทึกไว้ในonSaveInstanceStateแล้วต้องอัปเดตกับใหม่FragmentManagerโทษ?
Steven Byle

10
ถ้าฉันไม่เข้าใจผิด (และคุณควรตรวจสอบสิ่งนี้) วิธี getItem จะไม่ถูกเรียก - ตามที่คุณรู้ - แต่วิธี instantiateItem จะถูกเรียกใช้สำหรับทุกส่วนที่จะถูกกู้คืนโดยกรอบหลังจากการหมุน
ถนนบอสตัน

6
ใช่ฉันได้ตรวจสอบแล้วว่าgetItemจะไม่ถูกเรียกอีกครั้งเมื่อหมุน (สำหรับ frags ใด ๆ ที่สร้างขึ้นแล้ว) เนื่องจากFragmentManagerคืนสถานะของสถานะที่Fragmentsเก็บไว้ในเพจเจอร์ หากinstantiateItemมีการเรียกเมื่อแต่ละFragmentคืนมีการแก้ไขปัญหานี้ในความเป็นจริงแล้วจะปลอดภัยและพิสูจน์ได้ในอนาคตมากกว่าคำตอบที่ได้รับการยอมรับ ฉันจะลองทำเอง
Steven Byle

6
@Dori ฉันไม่ทำลายมัน โซลูชันของฉันไม่ได้ใช้ผลลัพธ์จากการเรียก 'getItem ()' แต่จะใช้ผลลัพธ์ที่เก็บไว้จากการโทร 'instantiateItem ()' แทน และวงจรชีวิตของชิ้นส่วนจะไม่แตกเพราะชิ้นส่วนที่เก็บไว้จะถูกลบออกจากแถวเรียงในการโทร 'destroyItem ()'
Streets Of Boston

85

สำหรับการแยกส่วนออกจาก ViewPager มีคำตอบมากมายที่นี่และในเธรด / บล็อก SO อื่น ๆ ที่เกี่ยวข้อง ทุกคนที่ฉันเห็นถูกทำลายและพวกเขามักจะตกอยู่ในหนึ่งในสองประเภทที่ระบุไว้ด้านล่าง มีบางการแก้ปัญหาที่ถูกต้องอื่น ๆ ถ้าคุณเพียงต้องการที่จะคว้าส่วนปัจจุบันเช่นนี้คำตอบอื่น ๆ ในหัวข้อนี้

ถ้าใช้FragmentPagerAdapterดูด้านล่าง หากใช้งานFragmentStatePagerAdapterคุ้มค่ามองหาที่นี้ ดัชนีการโลภที่ไม่ได้เป็นดัชนีปัจจุบันใน FragmentStateAdapter นั้นไม่ได้มีประโยชน์เท่าที่เป็นไปตามธรรมชาติของมันดัชนีเหล่านี้จะถูกดึงลงอย่างสมบูรณ์โดยไม่มีการดู / ออกนอกขอบเขตของ ScreenLimit

รูปแบบที่ไม่มีความสุข

ผิด: รักษารายการชิ้นส่วนภายในของคุณเองเพิ่มเมื่อFragmentPagerAdapter.getItem()ถูกเรียก

  • มักจะใช้SparseArrayหรือMap
  • ไม่ได้เป็นหนึ่งในตัวอย่างมากมายที่ฉันได้เห็นเหตุการณ์วงจรชีวิตดังนั้นการแก้ปัญหานี้จึงเปราะบาง ตามที่getItemเรียกว่าเฉพาะในครั้งแรกที่หน้าเว็บถูกเลื่อนไปที่ (หรือได้รับถ้าViewPager.setOffscreenPageLimit(x)> 0) ในViewPagerหากโฮสติ้งActivity/ Fragmentถูกฆ่าหรือรีสตาร์ทแล้วภายในSpaseArrayจะถูกลบออกเมื่อ FragmentPagerActivity ที่กำหนดเองถูกสร้างขึ้น แต่อยู่เบื้องหลัง ชิ้นส่วนภายใน ViewPagers จะถูกสร้างขึ้นใหม่และgetItemจะไม่ถูกเรียกสำหรับดัชนีใด ๆ ดังนั้นความสามารถในการรับแฟรกเมนต์จากดัชนีจะหายไปตลอดกาล คุณสามารถทำสิ่งนี้ได้โดยการบันทึกและเรียกคืนการอ้างอิงส่วนเหล่านี้ผ่านทางFragmentManager.getFragment()และ putFragmentแต่สิ่งนี้เริ่มทำให้ IMHO ยุ่ง

ผิด: สร้าง id แท็กของคุณเองที่ตรงกับสิ่งที่ใช้ภายใต้ประทุน FragmentPagerAdapterและใช้สิ่งนี้เพื่อดึงชิ้นส่วนของหน้าจากFragmentManager

  • นี่คือตราบเท่าที่ดีขึ้นในขณะที่มัน copes กับปัญหาการสูญเสีย-ส่วนการอ้างอิงในการแก้ปัญหาภายในอาร์เรย์แรก แต่เป็นแหลมที่ถูกต้องออกมาในคำตอบข้างต้นและที่อื่น ๆ ในสุทธิ - มันรู้สึก hacky เป็นของส่วนตัววิธีการภายในเพื่อViewPagerที่จะทำได้ เปลี่ยนแปลงได้ตลอดเวลาหรือรุ่นระบบปฏิบัติการใด ๆ

วิธีการที่สร้างขึ้นใหม่สำหรับการแก้ปัญหานี้คือ

private static String makeFragmentName(int viewId, long id) {
    return "android:switcher:" + viewId + ":" + id;
}

เส้นทางที่มีความสุข: ViewPager.instantiateItem()

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

เส้นทางที่มีความสุข: สร้างของคุณเอง FragmentViewPager

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

/**
 * @param containerViewId the ViewPager this adapter is being supplied to
 * @param id pass in getItemId(position) as this is whats used internally in this class
 * @return the tag used for this pages fragment
 */
public static String makeFragmentName(int containerViewId, long id) {
    return "android:switcher:" + containerViewId + ":" + id;
}

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

/**
 * @return may return null if the fragment has not been instantiated yet for that position - this depends on if the fragment has been viewed
 * yet OR is a sibling covered by {@link android.support.v4.view.ViewPager#setOffscreenPageLimit(int)}. Can use this to call methods on
 * the current positions fragment.
 */
public @Nullable Fragment getFragmentForPosition(int position)
{
    String tag = makeFragmentName(mViewPager.getId(), getItemId(position));
    Fragment fragment = getSupportFragmentManager().findFragmentByTag(tag);
    return fragment;
}

นี่เป็นวิธีแก้ไขปัญหาที่ง่ายและแก้ไขปัญหาในอีกสองโซลูชันที่พบได้ทุกที่บนเว็บ


6
สิ่งนี้จะใช้งานได้ แต่ต้องคัดลอกคลาส ViewPager ลงในซอร์สโค้ดของคุณเอง การใช้ 'instantiateItem ()' และ 'destroyItem ()' ของเพจเจอร์อะแดปเตอร์ของ ViewPager จะทำงานและจะให้เกียรติวงจรชีวิตของชิ้นส่วน (2 วิธีการเหล่านี้ถูกเรียกเช่นกันเมื่อสร้างชิ้นส่วนจากสถานะที่บันทึกไว้ - อินสแตนซ์) คุณถูกต้องการพึ่งพา 'getItem ()' ที่ถูกเรียกไม่ใช่ความคิดที่ดีและการใช้ API ส่วนตัวก็ไม่ใช่
ถนนของบอสตัน

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

หาก setOffscreenPageLimit = 1 (ค่าเริ่มต้น) FragmentStatePageAdapter จะเริ่มแลกเปลี่ยนส่วนในและนอกหน่วยความจำเมื่อเข้าถึงมุมมอง โดยการเรียกใช้ getItem () จะมีการสร้างอินสแตนซ์ใหม่ของหน้า / แฟรกเมนต์ใหม่ เมื่อมีการเข้ามาอีกครั้งจะมีวิธีการสั่งให้ระบบดำเนินการต่ออินสแตนซ์ที่สร้างขึ้นก่อนหน้านี้แทนที่จะสร้างขึ้นใหม่หรือไม่?
คาร์ล

หากเกินขีด จำกัด ของหน้ากระดาษจะต้องสร้างขึ้นมาใหม่ - แต่ผ่านไปในสถานะที่บันทึกไว้ หากต้องการให้คนเก่าอยู่ใกล้คุณก็สามารถเพิ่มจำนวนหน้าได้
Dori

@Dori ไม่มีสิ่งใดgetItemId(pos)อยู่ภายในFragmentStatePagerAdapter
Jemshit Iskenderov

70

เพิ่มวิธีถัดไปใน FragmentPagerAdapter ของคุณ:

public Fragment getActiveFragment(ViewPager container, int position) {
String name = makeFragmentName(container.getId(), position);
return  mFragmentManager.findFragmentByTag(name);
}

private static String makeFragmentName(int viewId, int index) {
    return "android:switcher:" + viewId + ":" + index;
}

getActiveFragment (0) ต้องทำงาน

นี่เป็นวิธีการดำเนินการใน ViewPager https://gist.github.com/jacek-marchwicki/d6320ba9a910c514424d หากสิ่งที่ล้มเหลวคุณจะเห็นบันทึกการชนที่ดี


ขอบคุณมาก:) . ฉันก็หว่านมันด้วยวิธีอื่นซึ่งฉันใช้ตัวจัดการเพื่อโพสต์ชิ้นส่วน มันไม่เหมาะ แต่มันทำให้ปัญหาแย่ลงในที่สุด อย่างไรก็ตามคุณเป็นคนดี :)
Qun Qin

12
ฉันใช้วิธีนี้ แต่ฉันเชื่อว่ามันหยุดทำงานเมื่อฉันย้ายไปที่ API 17 (?) ฉันจะแนะนำวิธีการอื่นและต้องการได้ถ้า Google เพียงแค่เผยแพร่ API เพื่อดึงชิ้นส่วนจาก ViewPager ที่ได้รับตำแหน่ง
gcl1

ฉันมีปัญหาเล็กน้อยเรียกชิ้นส่วนที่สองวิธีการสร้างใหม่
Vasil Valchev

2
เนื่องจากบางเมธอด makeFragmentName เวอร์ชัน API ถูกเปลี่ยนเป็นสตริงคงที่แบบส่วนตัว makeFragmentName (int viewId, id ยาว) {ส่งคืน "android: switcher:" + viewId + ":" + id; } ดังนั้นเมธอด getActiveFragment ตอนนี้ควรมีลักษณะเช่นนี้ (ใช้ getItemId แทนตำแหน่งสำหรับอาร์กิวเมนต์ที่สอง) public Fragment getActiveFragment (คอนเทนเนอร์ ViewPager ตำแหน่ง int) {ชื่อสตริง = makeFragmentName (container.getId (), getItemId (ตำแหน่ง)); ส่งคืน mFragmentManager.findFragmentByTag (ชื่อ); }
Eugene Popovich

1
มันใช้งานได้จริง แต่มันอันตรายจริงๆเพราะมันส่งผลต่อการใช้งานภายในของเพจเจอร์มุมมองและถ้า google จะเปลี่ยนการใช้งาน (และพวกเขาสามารถ) สิ่งเลวร้ายที่จะเกิดขึ้น ผมหวังว่า goole จะทำให้มันพร้อมที่จะได้รับส่วนสด
เชม

38

อีกวิธีง่ายๆ:

    public class MyPagerAdapter extends FragmentPagerAdapter {
        private Fragment mCurrentFragment;

        public Fragment getCurrentFragment() {
            return mCurrentFragment;
        }
//...    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            if (getCurrentFragment() != object) {
                mCurrentFragment = ((Fragment) object);
            }
            super.setPrimaryItem(container, position, object);
        }
    }

1
ดูเหมือนว่าสมบูรณ์แบบสำหรับความต้องการส่วนปัจจุบันของฉัน
danny117

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

1
ฟังดูเหมือนเป็นคำตอบที่ดี แต่อย่างที่setPrimaryItem()เรียกหลังจาก ViewPager.OnPageChangeListener#onPageSelected()ฉันไม่สามารถใช้ :-(
hardysim

21

ฉันรู้ว่านี่มีคำตอบไม่กี่ข้อ แต่อาจช่วยคนได้ ฉันได้ใช้วิธีการแก้ปัญหาที่ค่อนข้างง่ายเมื่อฉันต้องการที่จะได้รับจากฉันFragment ViewPagerในActivityหรือFragmentถือของViewPagerคุณคุณสามารถใช้รหัสนี้เพื่อวนรอบทุก ๆFragmentมัน

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    Fragment viewPagerFragment = fragmentPagerAdapter.getItem(i);
    if(viewPagerFragment != null) {
        // Do something with your Fragment
        // Check viewPagerFragment.isResumed() if you intend on interacting with any views.
    }
}
  • ถ้าคุณรู้ว่าตำแหน่งของคุณFragmentในคุณก็สามารถโทรViewPagergetItem(knownPosition)

  • หากคุณไม่ทราบตำแหน่งของคุณFragmentในViewPagerคุณสามารถมีบุตรหลานของคุณFragmentsใช้อินเตอร์เฟซด้วยวิธีการเช่นเดียวกับที่ใช้งานgetUniqueId()และการใช้งานที่แตกต่าง หรือคุณสามารถวนรอบทั้งหมดFragmentsและตรวจสอบประเภทของคลาสเช่นif(viewPagerFragment instanceof FragmentClassYouWant)

!!! แก้ไข !!!

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

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {
    String name = makeFragmentName(mViewPager.getId(), i);
    Fragment viewPagerFragment = getChildFragmentManager().findFragmentByTag(name);
    // OR Fragment viewPagerFragment = getFragmentManager().findFragmentByTag(name);
    if(viewPagerFragment != null) {

        // Do something with your Fragment
        if (viewPagerFragment.isResumed()) {
            // Interact with any views/data that must be alive
        }
        else {
            // Flag something for update later, when this viewPagerFragment
            // returns to onResume
        }
    }
}

และคุณจะต้องใช้วิธีนี้

private static String makeFragmentName(int viewId, int position) {
    return "android:switcher:" + viewId + ":" + position;
}

1
คุณเพิ่งพบFragmentByTag () แต่เราสามารถตั้งค่า TAG ด้วยแฟรกเมนต์ได้ที่ไหน?
VinceStyling

@vince ViewPagerตัวเองตั้งแท็กเหล่านั้น การแก้ปัญหานี้จะเปราะบางเพราะอาศัยการรู้ "ซ่อน" ViewPagerตั้งชื่อการประชุมของ คำตอบของ Streets of Boston เป็นคำตอบที่ครอบคลุมมากขึ้นซึ่งตอนนี้ฉันใช้ในโครงการของฉัน (แทนที่จะเป็นวิธีนี้)
Steven Byle

มันใช้งานได้ดี! แต่ฉันลบเงื่อนไข "if (viewPagerFragment.isResumed ())" ออกเพราะฉันจำเป็นต้องอัพเดตหน้า ListFragment ของฉันที่เป็นหน้าจอ จากนั้นเมื่อผู้ใช้กลับไปที่หน้า ListFragment ผู้ใช้จะได้รับการอัปเดตด้วยข้อมูลใหม่ทั้งหมด
JDJ

10

สำหรับกรณีของฉันไม่มีวิธีการแก้ปัญหาข้างต้นทำงาน

อย่างไรก็ตามเนื่องจากฉันใช้ Child Fragment Manager ใน Fragment จึงมีการใช้สิ่งต่อไปนี้:

Fragment f = getChildFragmentManager().getFragments().get(viewPager.getCurrentItem());

สิ่งนี้จะใช้ได้เฉพาะในกรณีที่แฟรกเมนต์ของคุณในตัวจัดการสอดคล้องกับไอเท็มวิวเพจ


นี่คือคำตอบที่สมบูรณ์แบบสำหรับการรับแฟรกเมนต์เก่าจากเพจเจอร์เมื่อกิจกรรมหรือแฟรกเมนต์ถูกสร้างใหม่
Smeet

7

เพื่อให้ได้รับส่วนที่มองเห็นได้ในปัจจุบันจาก ViewPager ฉันใช้คำสั่งง่ายๆนี้และมันใช้ได้ดี

      public Fragment getFragmentFromViewpager()  
      {
         return ((Fragment) (mAdapter.instantiateItem(mViewPager, mViewPager.getCurrentItem())));
      }

3

ฉันจัดการโดยทำรายการชิ้นส่วนทั้งหมดก่อนList<Fragment> fragments; ) ที่ฉันจะใช้จากนั้นเพิ่มมันลงในเพจเจอร์เพื่อให้ง่ายต่อการจัดการกับแฟรกเมนต์ที่ดูอยู่ในปัจจุบัน

ดังนั้น:

@Override
onCreate(){
    //initialise the list of fragments
    fragments = new Vector<Fragment>();

    //fill up the list with out fragments
    fragments.add(Fragment.instantiate(this, MainFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, MenuFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, StoresFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, AboutFragment.class.getName()));
    fragments.add(Fragment.instantiate(this, ContactFragment.class.getName()));


    //Set up the pager
    pager = (ViewPager)findViewById(R.id.pager);
    pager.setAdapter(new MyFragmentPagerAdapter(getSupportFragmentManager(), fragments));
    pager.setOffscreenPageLimit(4);
}

ดังนั้นสิ่งนี้จึงถูกเรียกว่า:

public Fragment getFragment(ViewPager pager){   
    Fragment theFragment = fragments.get(pager.getCurrentItem());
    return theFragment;
}

ดังนั้นฉันสามารถโยนมันในคำสั่ง if ที่จะทำงานเฉพาะถ้ามันอยู่ในส่วนที่ถูกต้อง

Fragment tempFragment = getFragment();
if(tempFragment == MyFragmentNo2.class){
    MyFragmentNo2 theFrag = (MyFragmentNo2) tempFragment;
    //then you can do whatever with the fragment
    theFrag.costomFunction();
}

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


1
คำตอบที่ดีที่สุดสำหรับฉัน
Sonu Kumar

3

นี่เป็นไปตามคำตอบของสตีเว่นข้างต้น นี่จะส่งคืนอินสแตนซ์จริงของแฟรกเมนต์ที่แนบกับกิจกรรมพาเรนต์แล้ว

FragmentPagerAdapter fragmentPagerAdapter = (FragmentPagerAdapter) mViewPager.getAdapter();
    for(int i = 0; i < fragmentPagerAdapter.getCount(); i++) {

        Fragment viewPagerFragment = (Fragment) mViewPager.getAdapter().instantiateItem(mViewPager, i);
        if(viewPagerFragment != null && viewPagerFragment.isAdded()) {

            if (viewPagerFragment instanceof FragmentOne){
                FragmentOne oneFragment = (FragmentOne) viewPagerFragment;
                if (oneFragment != null){
                    oneFragment.update(); // your custom method
                }
            } else if (viewPagerFragment instanceof FragmentTwo){
                FragmentTwo twoFragment = (FragmentTwo) viewPagerFragment;

                if (twoFragment != null){
                    twoFragment.update(); // your custom method
                }
            }
        }
    }

2

ฉันไม่สามารถหาวิธีที่ง่ายและสะอาดในการทำสิ่งนี้ อย่างไรก็ตามวิดเจ็ต ViewPager เป็นอีกหนึ่ง ViewGroup ซึ่งโฮสต์แฟรกเมนต์ของคุณ ViewPager มีแฟรกเมนต์เหล่านี้ในฐานะลูก ๆ ดังนั้นคุณสามารถทำซ้ำได้มากกว่าพวกเขา (โดยใช้. getChildCount () และ. getChildAt ()) และดูว่าอินสแตนซ์ส่วนที่คุณกำลังค้นหานั้นโหลดเข้ามาใน ViewPager หรือไม่และได้รับการอ้างอิง เช่นคุณสามารถใช้ฟิลด์ ID คงที่ที่ไม่ซ้ำกันเพื่อแยกส่วนย่อย

โปรดทราบว่า ViewPager อาจไม่ได้โหลดส่วนที่คุณกำลังมองหาเพราะมันเป็นคอนเทนเนอร์เสมือนจริงเช่น ListView


1
ขอบคุณสำหรับคำตอบของคุณฉันดึงมันมาด้วย adpter.getItem (); ฉันเป็นน้องใหม่ในการเขียนโปรแกรม Android ขอบคุณมากจริงๆ ตอนนี้ฉันสามารถดึง ListFragment และฉันต้องการเพิ่ม footerView ลงไปในขณะที่ฉันทำสิ่งนี้ในกิจกรรมของฉันแล้วพิมพ์ logcat: "java.lang.IllegalStateException: มุมมองเนื้อหายังไม่ได้สร้าง" ฉันรู้ว่าทำไม แต่ฉันมีปัญหาในการจัดการ กับมัน
Qun Qin

1
FragmentActivity มีเมธอด onAttachFragment () ปริมาณงานนี้สำหรับเนื้อหาส่วนเล็กน้อยของ ViewPager หรือไม่
Vasil Valchev

2

FragmentPagerAdapter เป็นแฟรกเมนต์ของแฟรกเมนต์ หากต้องการค้นหาแฟรกเมนต์ตามตำแหน่งหากยังอยู่ในหน่วยความจำให้ใช้สิ่งนี้:

public Fragment findFragmentByPosition(int position) {
    FragmentPagerAdapter fragmentPagerAdapter = getFragmentPagerAdapter();
    return getSupportFragmentManager().findFragmentByTag(
            "android:switcher:" + getViewPager().getId() + ":"
                    + fragmentPagerAdapter.getItemId(position));
}

โค้ดตัวอย่างสำหรับ v4 support api


มันกลับมาเป็นโมฆะใน onresume () ความคิดใด ๆ ว่าทำไมและจอบเพื่อแก้ไขหรือไม่
seema

2

คุณไม่จำเป็นต้องโทรgetItem()หรือวิธีอื่น ๆ ในขั้นตอนต่อมาจะได้รับการอ้างอิงของการเป็นเจ้าภาพภายในFragment ViewPagerหากคุณต้องการอัปเดตข้อมูลภายในให้Fragmentใช้วิธีนี้:อัปเดต ViewPager แบบไดนามิกหรือไม่

กุญแจคือการตั้งค่าข้อมูลใหม่ภายในAdaperและโทรnotifyDataSetChanged()ที่จะโทรgetItemPosition()ผ่านการอ้างอิงของคุณFragmentและให้โอกาสคุณอัปเดต วิธีอื่นทั้งหมดกำหนดให้คุณต้องอ้างอิงถึงตัวคุณเองหรือแฮ็คอื่น ๆ ซึ่งไม่ใช่วิธีแก้ปัญหาที่ดี

@Override
public int getItemPosition(Object object) {
    if (object instanceof UpdateableFragment) {
        ((UpdateableFragment) object).update(xyzData);
    }
    //don't return POSITION_NONE, avoid fragment recreation. 
    return super.getItemPosition(object);
}

ฉันไม่เข้าใจที่นี่คุณต้องมีการอ้างอิงของ Fragment เพื่อใช้ getItemPosition () ดังนั้นคุณอาจไม่ใช้ getItemPosition ใช่มั้ย?

getItemPosition()เป็นวิธีในอะแดปเตอร์ของคุณ คุณจะไม่เรียกมันโดยตรง คุณมีการอ้างอิงของอะแดปเตอร์ของคุณเพื่อให้คุณโทรadapter.notifyDataSetChanged()ซึ่งจะเรียกgetItemPosition()ของอะแดปเตอร์ของคุณโดยผ่านการอ้างอิงของคุณFragments คุณสามารถเห็นการใช้งานเต็มรูปแบบได้ที่นี่stackoverflow.com/questions/19891828/…
M-WaJeEh

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

2
ตกลงฉันได้อัปเดตคำตอบของฉันเพื่อชี้แจงเพิ่มเติม แน่นอนว่าจำเป็นต้องมีการอ้างอิงเพื่ออัปเดต a และFragmentวิธีการรับการอ้างอิงนั้นสามารถอภิปรายได้ วิธีแก้ไขปัญหาอื่น ๆ ได้รับการอ้างอิงจากการFragmentManagerใช้สตริงที่คล้าย "android:switcher:"+idหรือย้อนกลับPOSITION_NONEจากการgetItemosition()ทำให้ชิ้นส่วนทั้งหมดสร้างขึ้นใหม่
M-WaJeEh

ขอบคุณสิ่งนี้สมเหตุสมผลมากและตรงกับประสบการณ์ของฉัน (ดังนั้นฉันอาจไม่ได้ทำอะไรผิดไปฮ่า)

2

ต้องขยายไปFragmentPagerAdapterสู่คลาสอะแดปเตอร์ ViewPager ของคุณ
ถ้าคุณใช้FragmentStatePagerAdapterแล้วคุณจะไม่สามารถที่จะหาคุณFragmentโดยตัวของมันID

public static String makeFragmentName(int viewPagerId, int index) {
  return "android:switcher:" + viewPagerId + ":" + index;
}

วิธีใช้วิธีนี้: -

Fragment mFragment = ((FragmentActivity) getContext()).getSupportFragmentManager().findFragmentByTag(
       AppMethodUtils.makeFragmentName(mViewPager.getId(), i)
);
InterestViewFragment newFragment = (InterestViewFragment) mFragment;

2

เฮ้ฉันได้ตอบคำถามนี้ที่นี่ โดยทั่วไปคุณจะต้องแทนที่

วัตถุสาธารณะ instantiateItem (คอนเทนเนอร์ ViewGroup ตำแหน่ง int)

วิธีการของ FragmentStatePagerAdapter


1

ทางออกที่ดีที่สุดคือการใช้ส่วนขยายที่เราสร้างขึ้นในCodePathเรียกSmartFragmentStatePagerAdapter การปฏิบัติตามคำแนะนำดังกล่าวทำให้การดึงข้อมูลชิ้นส่วนและชิ้นส่วนที่เลือกในปัจจุบันจาก ViewPager ง่ายขึ้นอย่างมาก นอกจากนี้ยังทำงานได้ดีขึ้นในการจัดการหน่วยความจำของชิ้นส่วนที่ฝังอยู่ภายในอะแดปเตอร์


0

วิธีที่ง่ายที่สุดและรัดกุมที่สุด หากแฟรกเมนต์ทั้งหมดของคุณอยู่ในViewPagerคลาสที่แตกต่างกันคุณสามารถดึงและแยกแยะได้ดังต่อไปนี้:

public class MyActivity extends Activity
{

    @Override
    public void onAttachFragment(Fragment fragment) {
        super.onAttachFragment(fragment);
        if (fragment.getClass() == MyFragment.class) {
            mMyFragment = (MyFragment) fragment;
        }
    }

}

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

0

ฉันใช้สิ่งนี้ง่ายด้วยวิธีที่แตกต่างกันเล็กน้อย

FragmentAdapter.getItem แบบกำหนดเองของฉันไม่ได้ส่งคืน MyFragment () ใหม่ แต่เป็นอินสแตนซ์ของ MyFragment ที่สร้างขึ้นในตัวสร้าง FragmentAdapter

ในกิจกรรมของฉันฉันได้รับชิ้นส่วนจากอะแดปเตอร์ตรวจสอบว่าเป็นอินสแตนซ์ของ Fragment ที่จำเป็นหรือไม่จากนั้นใช้วิธีการที่จำเป็น


0

สร้าง id ทรัพยากรจำนวนเต็มใน /values/integers.xml

<integer name="page1">1</integer>
<integer name="page2">2</integer>
<integer name="page3">3</integer>

จากนั้นในฟังก์ชัน PagerAdapter getItem:

public Fragment getItem(int position) {

        Fragment fragment = null;

        if (position == 0) {
            fragment = FragmentOne.newInstance();
            mViewPager.setTag(R.integer.page1,fragment);

        }
        else if (position == 1) {

            fragment = FragmentTwo.newInstance();
            mViewPager.setTag(R.integer.page2,fragment);

        } else if (position == 2) {

            fragment = FragmentThree.newInstance();
            mViewPager.setTag(R.integer.page3,fragment);

        }

        return fragment;
        }

จากนั้นในกิจกรรมให้เขียนฟังก์ชันนี้เพื่อรับการอ้างอิงชิ้นส่วน:

private Fragment getFragmentByPosition(int position) {
    Fragment fragment = null;

    switch (position) {
        case 0:
            fragment = (Fragment) mViewPager.getTag(R.integer.page1);
            break;

        case 1:

            fragment = (Fragment) mViewPager.getTag(R.integer.page2);
            break;

        case 2:
            fragment = (Fragment) mViewPager.getTag(R.integer.page3);
            break;
            }

            return fragment;
    }

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

Fragment fragment = getFragmentByPosition(position);

        if (fragment != null) {
                    FragmentOne fragmentOne = (FragmentOne) fragment;
                    }

0

วิธีง่ายๆในการวนซ้ำในตัวจัดการส่วนย่อย ค้นหา viewpager ที่มีการโต้แย้งตำแหน่งส่วนที่วางไว้ใน สาธารณะคง PlaceholderFragment newInstance (int sectionNumber)

public PlaceholderFragment getFragmentByPosition(Integer pos){
    for(Fragment f:getChildFragmentManager().getFragments()){
        if(f.getId()==R.id.viewpager && f.getArguments().getInt("SECTNUM") - 1 == pos) {
            return (PlaceholderFragment) f;
        }
    }
    return null;
}

0

ในส่วน

public int getArgument(){
   return mPage;
{
public void update(){

}

ใน FragmentActivity

List<Fragment> fragments = getSupportFragmentManager().getFragments();
for(Fragment f:fragments){
    if((f instanceof PageFragment)&&(!f.isDetached())){
          PageFragment pf = (PageFragment)f;   
          if(pf.getArgument()==pager.getCurrentItem())pf.update();
    }
}

0

ใน TabLayout มีหลายแท็บสำหรับ Fragment คุณสามารถค้นหาชิ้นส่วนด้วยแท็กโดยใช้ดัชนีของส่วน

สำหรับอดีต ดัชนีสำหรับ Fragment1 คือ 0 ดังนั้นในfindFragmentByTag()วิธีการส่งแท็กสำหรับ Viewpager หลังจากใช้ fragmentTransaction คุณสามารถเพิ่มเปลี่ยนชิ้นส่วน

String tag = "android:switcher:" + R.id.viewPager + ":" + 0; Fragment1 f = (Fragment1) getSupportFragmentManager().findFragmentByTag(tag);


-1

ตกลงสำหรับอะแดปเตอร์ FragmentStatePagerAdapterฉันแก้ปัญหากองทุน:

ใน FragmentActivity ของคุณ:

ActionBar mActionBar = getSupportActionBar(); 
mActionBar.addTab(mActionBar.newTab().setText("TAB1").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment1.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB2").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment2.class.getName())));
mActionBar.addTab(mActionBar.newTab().setText("TAB3").setTabListener(this).setTag(Fragment.instantiate(this, MyFragment3.class.getName())));

viewPager = (STViewPager) super.findViewById(R.id.viewpager);
mPagerAdapter = new MyPagerAdapter(getSupportFragmentManager(), mActionBar);
viewPager.setAdapter(this.mPagerAdapter);

และสร้าง methode ในชั้นเรียนของคุณ FragmentActivity - ดังนั้นวิธีการที่ให้คุณเข้าถึง Fragment ของคุณคุณเพียงแค่ต้องให้มันอยู่ในตำแหน่งของชิ้นส่วนที่คุณต้องการ:

public Fragment getActiveFragment(int position) {
String name = MyPagerAdapter.makeFragmentName(position);
return getSupportFragmentManager().findFragmentByTag(name);
}

ในอะแดปเตอร์ของคุณ:

public class MyPagerAdapter extends FragmentStatePagerAdapter {

private final ActionBar actionBar;
private final FragmentManager fragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager, com.actionbarsherlock.app.ActionBarActionBar mActionBar) {super(fragmentManager);
this.actionBar = mActionBar;
this.fragmentManager = fragmentManager;
}

@Override
public Fragment getItem(int position) {
getSupportFragmentManager().beginTransaction().add(mTchatDetailsFragment, makeFragmentName(position)).commit();
return (Fragment)this.actionBar.getTabAt(position);
}

@Override
public int getCount() {
return this.actionBar.getTabCount();
}

@Override
public CharSequence getPageTitle(int position) {
return this.actionBar.getTabAt(position).getText();
}

private static String makeFragmentName(int viewId, int index) {
return "android:fragment:" + index;
}

}

-1
Fragment yourFragment = yourviewpageradapter.getItem(int index);

indexเป็นสถานที่ของแฟรกเมนต์ในอะแด็ปเตอร์เหมือนกับที่คุณเพิ่มfragment1ก่อนดังนั้น retreive fragment1pass indexเป็น 0 และอื่น ๆ สำหรับการพักผ่อน

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