อัปเดต ViewPager แบบไดนามิกหรือไม่


316

ฉันไม่สามารถอัปเดตเนื้อหาใน ViewPager

การใช้วิธีที่ถูกต้องคืออะไร instantiateItem () และ getItem () ในคลาส FragmentPagerAdapter?

ฉันใช้ getItem () เพื่อยกตัวอย่างและส่งคืนชิ้นส่วนของฉัน:

@Override
public Fragment getItem(int position) {
    return new MyFragment(context, paramters);
}

มันใช้งานได้ดี ยกเว้นฉันไม่สามารถเปลี่ยนเนื้อหาได้

ดังนั้นฉันจึงพบสิ่งนี้: ViewPager PagerAdapter ไม่อัปเดตมุมมอง

"วิธีการของฉันคือใช้เมธอด setTag () สำหรับมุมมองอินสแตนซ์ใด ๆ ในเมธอด instantiateItem ()"

ตอนนี้ฉันต้องการใช้ instantiateItem () เพื่อทำเช่นนั้น แต่ฉันไม่รู้ว่าฉันต้องส่งคืนอะไร (ชนิดคือ Object) และความสัมพันธ์กับ getItem (ตำแหน่ง int) คืออะไร

ฉันอ่านเอกสารอ้างอิง :

  • public abstract Fragment getItem (ตำแหน่ง int)

    ส่งคืน Fragment ที่เกี่ยวข้องกับตำแหน่งที่ระบุ

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

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

    container มุมมองที่มีอยู่ซึ่งจะแสดงหน้านั้น position ตำแหน่งหน้าเว็บที่จะสร้างอินสแตนซ์

    ผลตอบแทน

    ส่งคืนวัตถุที่เป็นตัวแทนของหน้าใหม่ สิ่งนี้ไม่จำเป็นต้องเป็นมุมมอง แต่สามารถเป็นคอนเทนเนอร์อื่นของหน้า

แต่ฉันก็ยังไม่เข้าใจ

นี่คือรหัสของฉัน ฉันใช้แพ็คเกจสนับสนุน v4

ViewPagerTest

public class ViewPagerTest extends FragmentActivity {
    private ViewPager pager;
    private MyFragmentAdapter adapter; 

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.pager1);

        pager = (ViewPager)findViewById(R.id.slider);

        String[] data = {"page1", "page2", "page3", "page4", "page5", "page6"};

        adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        pager.setAdapter(adapter);

        ((Button)findViewById(R.id.button)).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                reload();
            }
        });
    }

    private void reload() {
        String[] data = {"changed1", "changed2", "changed3", "changed4", "changed5", "changed6"};
        //adapter = new MyFragmentAdapter(getSupportFragmentManager(), 6, this, data);
        adapter.setData(data);
        adapter.notifyDataSetChanged();
        pager.invalidate();

        //pager.setCurrentItem(0);
    }
}

MyFragmentAdapter

class MyFragmentAdapter extends FragmentPagerAdapter {
    private int slideCount;
    private Context context;
    private String[] data;

    public MyFragmentAdapter(FragmentManager fm, int slideCount, Context context, String[] data) {
        super(fm);
        this.slideCount = slideCount;
        this.context = context;
        this.data = data;
    }

    @Override
    public Fragment getItem(int position) {
        return new MyFragment(data[position], context);
    }

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

    public void setData(String[] data) {
        this.data = data;
    }

    @Override
    public int getItemPosition(Object object) {
        return POSITION_NONE;
    }
}

MyFragment

public final class MyFragment extends Fragment {
    private String text;

    public MyFragment(String text, Context context) {
        this.text = text;
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.slide, null);
        ((TextView)view.findViewById(R.id.text)).setText(text);

        return view;
    }
}

นี่คือบางคนที่มีปัญหาที่คล้ายกันไม่มีคำตอบ http://www.mail-archive.com/android-developers@googlegroups.com/msg200477.html


เพื่อให้เข้าใจองค์ประกอบที่เกี่ยวข้องได้ดียิ่งขึ้นมันอาจช่วยในการอ่านบทช่วยสอนของฉันเกี่ยวกับ ViewPager แบบแท็บที่มีประวัติการนำทางด้านหลังแยกกัน มันมาพร้อมกับตัวอย่างการทำงานบน GitHub และแหล่งข้อมูลเพิ่มเติม: medium.com/@nilan/ …
marktani

มีปัญหาบางอย่างstackoverflow.com/questions/40149039/…
อัลเบิร์

คุณสามารถตรวจสอบเช่นใน GitHub ฉันหวังว่าตัวอย่างนี้จะช่วยคุณ
Cabezas

ทางออกที่ดีง่าย ๆ : stackoverflow.com/a/35450575/2162226
Gene Bo

Google เพิ่งเปิดตัว ViewPager2 ซึ่งทำให้การอัพเดต Fragment / View แบบไดนามิกง่ายขึ้น คุณสามารถดูตัวอย่างได้ที่github.com/googlesamples/android-viewpager2หรือคำตอบนี้ได้ที่stackoverflow.com/a/57393414/1333448
Yves Delerm

คำตอบ:


605

เมื่อใช้ FragmentPagerAdapter หรือ FragmentStatePagerAdapter เป็นการดีที่สุดที่จะจัดการ แต่เพียงผู้เดียวgetItem()และไม่ได้สัมผัสinstantiateItem()เลย The instantiateItem()- destroyItem()- isViewFromObject()อินเตอร์เฟสบน PagerAdapter เป็นอินเตอร์เฟสระดับล่างที่ FragmentPagerAdapter ใช้เพื่อทำให้การใช้งานง่ายขึ้นgetItem()อินเตอร์เฟซ

ก่อนที่จะเข้าไปในนี้ฉันควรชี้แจงว่า

หากคุณต้องการสลับแฟรกเมนต์จริงที่แสดงอยู่คุณต้องหลีกเลี่ยง FragmentPagerAdapter และใช้ FragmentStatePagerAdapter

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

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

getItemPosition()ทางออกที่เหมาะสมคือการแทนที่ เมื่อnotifyDataSetChanged()เรียกใช้ ViewPager จะโทรออกgetItemPosition()รายการทั้งหมดในอแด็ปเตอร์เพื่อดูว่าจำเป็นต้องย้ายไปที่ตำแหน่งอื่นหรือลบออก

ตามปกติแล้วการgetItemPosition()ส่งคืนจะPOSITION_UNCHANGEDหมายถึง "วัตถุนี้อยู่ในสภาพที่ดีอย่าทำลายหรือลบทิ้ง" การคืนPOSITION_NONEแก้ไขปัญหาโดยแทนที่จะพูดว่า "วัตถุนี้ไม่ได้เป็นรายการที่ฉันกำลังแสดงอยู่ให้ลบออก" ดังนั้นจึงมีผลในการลบและสร้างใหม่ทุกรายการในอะแดปเตอร์ของคุณ

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

หากคุณต้องการประสิทธิภาพที่ดีขึ้นคุณสามารถใช้การgetItemPosition()ดำเนินการที่ดีขึ้น นี่คือตัวอย่างสำหรับเพจเจอร์ที่สร้างแฟรกเมนต์จากรายการสตริง:

ViewPager pager = /* get my ViewPager */;
// assume this actually has stuff in it
final ArrayList<String> titles = new ArrayList<String>();

FragmentManager fm = getSupportFragmentManager();
pager.setAdapter(new FragmentStatePagerAdapter(fm) {
    public int getCount() {
        return titles.size();
    }

    public Fragment getItem(int position) {
        MyFragment fragment = new MyFragment();
        fragment.setTitle(titles.get(position));
        return fragment;
    }

    public int getItemPosition(Object item) {
        MyFragment fragment = (MyFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        if (position >= 0) {
            return position;
        } else {
            return POSITION_NONE;
        }
    }
});

ด้วยการใช้งานนี้จะมีเพียงเศษที่แสดงชื่อใหม่เท่านั้นที่จะได้รับการแสดง ส่วนใด ๆ ที่แสดงชื่อเรื่องที่ยังคงอยู่ในรายการจะถูกย้ายไปยังตำแหน่งใหม่ในรายการแทนและส่วนที่มีชื่อเรื่องที่ไม่อยู่ในรายการจะถูกทำลาย

จะเกิดอะไรขึ้นถ้าแฟรกเมนต์ไม่ได้ถูกสร้างขึ้นมาใหม่ แต่จำเป็นต้องอัพเดตใหม่ การอัพเดทชิ้นส่วนที่มีชีวิตนั้นจัดการได้ดีที่สุดโดยตัวชิ้นส่วนเอง นั่นคือข้อดีของการมีแฟรกเมนต์เพราะมันคือคอนโทรลเลอร์ของมันเอง แฟรกเมนต์สามารถเพิ่มผู้ฟังหรือผู้สังเกตการณ์ให้กับออบเจ็กต์อื่นonCreate()แล้วลบออกonDestroy()ดังนั้นจัดการการอัพเดตเอง คุณไม่จำเป็นต้องใส่รหัสอัพเดททั้งหมดไว้ข้างในgetItem()เหมือนที่คุณทำในอะแดปเตอร์สำหรับ ListView หรือ AdapterView ประเภทอื่น ๆ

สิ่งสุดท้ายสิ่งหนึ่ง - เพียงเพราะ FragmentPagerAdapter ไม่ทำลายส่วนไม่ได้หมายความว่า getItemPosition ไร้ประโยชน์อย่างสมบูรณ์ใน FragmentPagerAdapter คุณยังสามารถใช้การโทรกลับนี้เพื่อจัดเรียงชิ้นส่วนของคุณใหม่ใน ViewPager แม้ว่าจะไม่ลบออกอย่างสมบูรณ์จาก FragmentManager


4
ใช้งานไม่ได้ แต่ยังไม่อัปเดตอะไร ... โพสต์รหัสของฉัน
Ixx

64
ตกลงเข้าใจปัญหาแล้วคุณต้องใช้ FragmentStatePagerAdapter แทนที่จะเป็น FragmentPagerAdapter FragmentPagerAdapter จะไม่ลบส่วนต่าง ๆ ออกจาก FragmentManager แต่จะแยกและแนบเท่านั้น ตรวจสอบเอกสารเพื่อดูข้อมูลเพิ่มเติม - โดยทั่วไปคุณต้องการใช้ FragmentPagerAdapter สำหรับชิ้นส่วนที่ถาวรเท่านั้น ฉันแก้ไขตัวอย่างด้วยการแก้ไข
Bill Phillips

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

12
คุณพูดถูกการเปลี่ยนคลาสของอะแดปเตอร์เป็น FragmentStatePagerAdapter แก้ไขปัญหาทั้งหมดของฉัน ขอบคุณ!
Ixx

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

142

แทนที่จะกลับPOSITION_NONEจากgetItemPosition()และก่อให้เกิดการพักผ่อนหย่อนใจมุมมองแบบเต็มทำเช่นนี้:

//call this method to update fragments in ViewPager dynamically
public void update(UpdateData xyzData) {
    this.updateData = xyzData;
    notifyDataSetChanged();
}

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

แฟรกเมนต์ของคุณควรใช้UpdateableFragmentอินเตอร์เฟส:

public class SomeFragment extends Fragment implements
    UpdateableFragment{

    @Override
    public void update(UpdateData xyzData) {
         // this method will be called for every fragment in viewpager
         // so check if update is for this fragment
         if(forMe(xyzData)) {
           // do whatever you want to update your UI
         }
    }
}

และอินเทอร์เฟซ:

public interface UpdateableFragment {
   public void update(UpdateData xyzData);
}

คลาสข้อมูลของคุณ:

public class UpdateData {
    //whatever you want here
}

1
นี่หมายความว่า MainActivity ของคุณจำเป็นต้องใช้อินเทอร์เฟซหรือไม่? โปรดช่วยฉันออกจากที่นี่ ในการใช้งานปัจจุบันของฉันฉันใช้ MainActivity class เป็นเครื่องมือสื่อสารระหว่างแฟรกเมนต์ดังนั้นฉันจึงสับสนเกี่ยวกับโซลูชันนี้
Rakeeb Rajbhandari

1
ตกลงฉันได้ตอบที่สถานที่อื่นด้วยการใช้งานเต็มรูปแบบที่ฉันกำลังปรับปรุงFragmentแบบไดนามิกดู: stackoverflow.com/questions/19891828//
M-WaJeEh

@ M-WaJeEh อาจเป็นตัวอย่างได้โปรด !!! ฉันมีมุมมองแบบรายการพร้อมรายชื่อเมืองและหลังจากเลือกเมืองจะเปิด viewpager (3 หน้า) พร้อมข้อมูลเกี่ยวกับเมืองนี้ มันใช้งานได้ดี แต่หลังจากเลือกd page on refreshes the 1-st page only after swiping pages? thw 2วิวเพจเจอร์อื่นของเมืองจะแสดงเฉพาะเพจ3 มิตินั้นว่างเปล่าเสมอ ขอบคุณ
SERG

4
ในทุกสิ่งที่ฉันเห็นมา 2.5 วันที่ผ่านมาในชีวิตการทำงานของฉัน .. นี่เป็นสิ่งเดียวที่ทำงานให้ฉัน .. อาริกาโตะทุกประเภทขอบคุณสปาชิบา sukran ..
Orkun Ozen

2
Mother of Dragons! มันใช้งานได้อย่างมีเสน่ห์เป็นทางออกเดียวสำหรับฉัน ... ขอบคุณมาก !!
Sebastián Guerrero

24

สำหรับผู้ที่ยังประสบปัญหาเดียวกันกับที่ฉันเคยพบมาก่อนเมื่อฉันมีViewPagerชิ้นส่วน 7 ชิ้น เริ่มต้นสำหรับชิ้นส่วนเหล่านี้เพื่อโหลดเนื้อหาภาษาอังกฤษจากAPIบริการ แต่ปัญหาที่นี่ที่ฉันต้องการเปลี่ยนภาษาจากกิจกรรมการตั้งค่าและหลังจากเสร็จสิ้นกิจกรรมการตั้งค่าฉันต้องการViewPagerในกิจกรรมหลักเพื่อรีเฟรชชิ้นส่วนเพื่อให้ตรงกับการเลือกภาษาจากผู้ใช้และโหลด เนื้อหาภาษาอาหรับหากผู้ใช้เลือกภาษาอาหรับที่นี่สิ่งที่ฉันได้ทำงานตั้งแต่ครั้งแรก

1- คุณต้องใช้ FragmentStatePagerAdapter ดังกล่าวข้างต้น

2- บน mainActivity i แทนที่ onResume และทำสิ่งต่อไปนี้

if (!(mPagerAdapter == null)) {
    mPagerAdapter.notifyDataSetChanged();
}

3 ฉัน overrided getItemPosition()ใน mPagerAdapter POSITION_NONEและทำให้มันกลับมา

@Override
public int getItemPosition(Object object) {
    return POSITION_NONE;
}

ทำงานเหมือนจับใจ


1
ใช้งานได้ยกเว้นในหนึ่ง situtaion เมื่อคุณลบรายการสุดท้ายใน viewPager จากนั้นจะไม่ลบออกจากรายการ
Vlado Pandžić

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

15

ฉันพบปัญหานี้แล้วและแก้ไขได้ในวันนี้ดังนั้นฉันจึงเขียนสิ่งที่ฉันได้เรียนรู้มาและหวังว่ามันจะมีประโยชน์สำหรับคนที่ยังใหม่กับ Android ViewPagerและอัปเดตเหมือนฉัน ฉันกำลังใช้FragmentStatePagerAdapterใน API ระดับ 17 และปัจจุบันมีเพียง 2 ส่วน ฉันคิดว่าต้องมีบางสิ่งที่ไม่ถูกต้องโปรดแก้ไขฉันด้วยขอบคุณ ป้อนคำอธิบายรูปภาพที่นี่

  1. ข้อมูลที่เป็นอนุกรมจะต้องโหลดเข้าสู่หน่วยความจำ ซึ่งสามารถทำได้โดยใช้CursorLoader/ /AsyncTask Threadการโหลดอัตโนมัติขึ้นอยู่กับรหัสของคุณหรือไม่ หากคุณใช้ a CursorLoaderจะเป็นการโหลดอัตโนมัติเนื่องจากมีผู้สังเกตข้อมูลที่ลงทะเบียนไว้

  2. หลังจากที่คุณเรียกviewpager.setAdapter(pageradapter)ใช้อะแดปเตอร์getCount()จะถูกเรียกอย่างต่อเนื่องเพื่อสร้างชิ้นส่วน ดังนั้นหากกำลังโหลดข้อมูลgetCount()สามารถส่งคืนค่า 0 ดังนั้นคุณไม่จำเป็นต้องสร้างชิ้นส่วนจำลองโดยไม่แสดงข้อมูล

  3. หลังจากที่ข้อมูลถูกโหลดอะแดปเตอร์จะไม่สร้างชิ้นส่วนโดยอัตโนมัติตั้งแต่getCount()ยังคงเป็น 0 เพื่อให้เราสามารถกำหนดจำนวนข้อมูลที่โหลดจริงจะถูกส่งกลับจากนั้นเรียกของอะแดปเตอร์getCount() เริ่มสร้างแฟรกเมนต์ (เพียง 2 แฟรกเมนต์แรก) ตามข้อมูลในหน่วยความจำ มันเสร็จสิ้นก่อนที่จะถูกส่งกลับ จากนั้นมีชิ้นส่วนที่ถูกต้องที่คุณต้องการnotifyDataSetChanged()ViewPagernotifyDataSetChanged()ViewPager

  4. หากข้อมูลในฐานข้อมูลและหน่วยความจำทั้งคู่มีการอัพเดท (เขียนผ่าน) หรือเพียงแค่ข้อมูลในหน่วยความจำได้รับการปรับปรุง (เขียนกลับ) หรือมีการอัพเดทเฉพาะข้อมูลในฐานข้อมูล ในสองกรณีที่ผ่านมาหากข้อมูลไม่ได้ถูกโหลดโดยอัตโนมัติจากฐานข้อมูลไปยังหน่วยความจำ (ดังกล่าวข้างต้น) ViewPagerและอะแดปเตอร์เพจเจอร์เพียงแค่จัดการกับข้อมูลในหน่วยความจำ

  5. notifyDataSetChanged()ดังนั้นเมื่อข้อมูลในหน่วยความจำที่มีการปรับปรุงเราก็ต้องเรียกของอะแดปเตอร์ เนื่องจากแฟรกเมนต์ถูกสร้างขึ้นแล้วอะแด็ปเตอร์onItemPosition()จะถูกเรียกใช้ก่อนที่จะnotifyDataSetChanged()ส่งคืน ไม่จำเป็นต้องทำอะไรgetItemPosition()เลย จากนั้นข้อมูลจะถูกอัพเดต


ฉันพบสิ่งที่ต้องทำหลังจากที่คุณอัปเดตข้อมูล (ใน memeory) เพียงโทรnotifyDataSetChanged()แล้วเพิ่งอัปเดตโดยอัตโนมัติเนื่องจากส่วน (ด้วย chartview) ที่ฉันใช้จะรีเฟรชตัวเอง ถ้ามันไม่ได้เป็นเช่นชิ้นส่วนคงที่ฉันจะมีการโทรonItemPositon()และผลตอบแทนPOSITION_NONE ขออภัยเกี่ยวกับที่
oscarthecat

ใครบ้างมีความคิดว่าเครื่องมือใดที่ใช้สร้างไดอะแกรมนั้น
JuiCe

12

ลองใช้destroyDrawingCache()ViewPager notifyDataSetChanged()ในรหัสของคุณ


3
กันที่นี่ ใช้งานได้ดีขอบคุณ ฉันมีช่วงเวลาที่ยากลำบากเป็นพิเศษเพราะฉันไม่ได้ใช้Fragmentsเพจเจอร์ของฉัน ขอบคุณมาก!
เซท

11

หลังจากชั่วโมงของแห้วขณะที่พยายามทุกการแก้ปัญหาดังกล่าวข้างต้นที่จะเอาชนะปัญหานี้และยังพยายามแก้ปัญหาจำนวนมากในคำถามอื่น ๆ ที่คล้ายกันเช่นนี้ , นี้และนี้ซึ่งทั้งหมดล้มเหลวด้วยผมที่จะแก้ปัญหานี้และจะทำให้ViewPagerการทำลายเก่าFragmentและเติมpagerด้วย ใหม่Fragments ฉันได้แก้ไขปัญหาดังต่อไปนี้:

1)ให้ViewPagerชั้นเรียนขยายออกFragmentPagerAdapterไปดังต่อไปนี้:

 public class myPagerAdapter extends FragmentPagerAdapter {

2)สร้างรายการสำหรับViewPagerที่จัดเก็บtitleและfragmentดังต่อไปนี้:

public class PagerItem {
private String mTitle;
private Fragment mFragment;


public PagerItem(String mTitle, Fragment mFragment) {
    this.mTitle = mTitle;
    this.mFragment = mFragment;
}
public String getTitle() {
    return mTitle;
}
public Fragment getFragment() {
    return mFragment;
}
public void setTitle(String mTitle) {
    this.mTitle = mTitle;
}

public void setFragment(Fragment mFragment) {
    this.mFragment = mFragment;
}

}

3)สร้าง Construct ของอินสแตนซ์ViewPagerของฉันFragmentManagerเพื่อเก็บไว้ในของฉันclassดังต่อไปนี้:

private FragmentManager mFragmentManager;
private ArrayList<PagerItem> mPagerItems;

public MyPagerAdapter(FragmentManager fragmentManager, ArrayList<PagerItem> pagerItems) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mPagerItems = pagerItems;
}

4)สร้างวิธีการตั้งค่าadapterข้อมูลใหม่ด้วยข้อมูลใหม่โดยลบทั้งหมดก่อนหน้าfragmentจากfragmentManagerตัวเองโดยตรงเพื่อให้การadapterตั้งค่าใหม่fragmentจากรายการใหม่อีกครั้งดังต่อไปนี้:

public void setPagerItems(ArrayList<PagerItem> pagerItems) {
    if (mPagerItems != null)
        for (int i = 0; i < mPagerItems.size(); i++) {
            mFragmentManager.beginTransaction().remove(mPagerItems.get(i).getFragment()).commit();
        }
    mPagerItems = pagerItems;
}

5)จากคอนเทนเนอร์ActivityหรือFragmentไม่เริ่มต้นอะแดปเตอร์ใหม่ด้วยข้อมูลใหม่ ตั้งค่าข้อมูลใหม่ด้วยวิธีการsetPagerItemsด้วยข้อมูลใหม่ดังต่อไปนี้:

ArrayList<PagerItem> pagerItems = new ArrayList<PagerItem>();
    pagerItems.add(new PagerItem("Fragment1", new MyFragment1()));
    pagerItems.add(new PagerItem("Fragment2", new MyFragment2()));

    mPagerAdapter.setPagerItems(pagerItems);
    mPagerAdapter.notifyDataSetChanged();

ฉันหวังว่ามันจะช่วย


คุณสามารถช่วยฉันในปัญหาstackoverflow.com/questions/40149039//
อัลเบิร์

คุณสามารถช่วยฉันด้วยปัญหาstackoverflow.com/questions/41547995/
viper

10

ด้วยเหตุผลบางอย่างไม่มีคำตอบใดที่เหมาะกับฉันดังนั้นฉันจึงต้องแทนที่เมธอด restoreState โดยไม่ต้องเรียก super ใน fragmentStatePagerAdapter ของฉัน รหัส:

private class MyAdapter extends FragmentStatePagerAdapter {

    // [Rest of implementation]

    @Override
    public void restoreState(Parcelable state, ClassLoader loader) {}

}

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

OMG .. มันใช้งานได้ผล .. มันได้ผล .. รุ่งโรจน์: D ในกรณีของฉันฉันจำเป็นต้องลบวัตถุไอเท็มปัจจุบันของ viewpager แต่แฟรกเมนต์ปัจจุบันไม่ได้อัพเดตหลังจากลองใช้วิธีการ / ขั้นตอนทั้งหมดข้างต้น
ราหุลคูรานา

3

ฉันปรับเปลี่ยนโซลูชันเล็กน้อยโดย Bill Phillips เพื่อให้เหมาะกับความต้องการของฉัน

private class PagerAdapter extends FragmentStatePagerAdapter{
    Bundle oBundle;
    FragmentManager oFragmentManager;
    ArrayList<Fragment> oPooledFragments;

public PagerAdapter(FragmentManager fm) {
    super(fm);
    oFragmentManager=fm;

    }
@Override
public int getItemPosition(Object object) {

    Fragment oFragment=(Fragment)object;
    oPooledFragments=new ArrayList<>(oFragmentManager.getFragments());
    if(oPooledFragments.contains(oFragment))
        return POSITION_NONE;
    else
        return POSITION_UNCHANGED;
    } 
}

เพื่อให้การgetItemPosition()ส่งคืนPOSITION_NONEสำหรับแฟรกเมนต์เหล่านั้นซึ่งปัจจุบันอยู่ในFragmentManagerเมื่อgetItemPositionถูกเรียก (โปรดทราบว่าสิ่งนี้FragmentStatePagerและสิ่งที่ViewPagerเกี่ยวข้องมีอยู่ในส่วนที่ไม่ได้อยู่ในกิจกรรม)


2

ฉันมีปัญหาที่คล้ายกัน แต่ไม่ต้องการที่จะเชื่อใจในโซลูชันที่มีอยู่ (ชื่อแท็กที่มีการเข้ารหัสแบบยาก ฯลฯ ) และฉันไม่สามารถทำให้โซลูชันของ M-WaJeEh ทำงานได้สำหรับฉัน นี่คือทางออกของฉัน:

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

เพื่อหลีกเลี่ยงปัญหานี้ฉันใช้ instantiateItem (ViewGroup, int) และอัปเดตอาร์เรย์ที่นั่นเช่นนี้

        @Override
    public Object instantiateItem(ViewGroup container, int position) {
        Object o = super.instantiateItem(container, position);
        if(o instanceof FragmentX){
            myFragments[0] = (FragmentX)o;
        }else if(o instanceof FragmentY){
            myFragments[1] = (FragmentY)o;
        }else if(o instanceof FragmentZ){
            myFragments[2] = (FragmentZ)o;
        }
        return o;
    }

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


ฉันได้ทำบางสิ่งที่เกี่ยวข้องในstackoverflow.com/a/23427215/954643แม้ว่าคุณควรจะลบรายการออกจากmyFragmentsด้วยpublic void destroyItem(ViewGroup container, int position, Object object)เช่นกันดังนั้นคุณจึงไม่ได้รับการอ้างอิงการจัดการที่เก่าเกินไป
qix

1

ผมใช้EventBusห้องสมุดเพื่อปรับปรุงเนื้อหาในFragment ViewPagerตรรกะง่ายๆเช่นเดียวกับเอกสารของ EventBus วิธีการทำ ไม่จำเป็นต้องควบคุมFragmentPagerAdapterอินสแตนซ์ รหัสอยู่ที่นี่:

1: กำหนดเหตุการณ์

กำหนดข้อความที่ต้องการอัปเดต

public class UpdateCountEvent {
    public final int count;

    public UpdateCountEvent(int count) {
        this.count = count;
    }
}

2. เตรียมสมาชิก

เขียนรหัสด้านล่างในส่วนที่จำเป็นต้องมีการปรับปรุง

@Override
public void onStart() {
    super.onStart();
    EventBus.getDefault().register(this);
}

@Override
public void onStop() {
    EventBus.getDefault().unregister(this);  
    super.onStop();
}

public void onEvent(UpdateCountEvent event) {//get update message
    Toast.makeText(getActivity(), event.count, Toast.LENGTH_SHORT).show();
}

3. โพสต์กิจกรรม

เขียนรหัสด้านล่างในอื่น ๆActivityหรืออื่น ๆFragmentซึ่งจำเป็นต้องปรับปรุงพารามิเตอร์

//input update message
EventBus.getDefault().post(new UpdateCountEvent(count));

1

หากคุณต้องการใช้ FragmentStatePagerAdapter โปรดดูที่https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Tatus%20Owner%20Summary% 20Stars มีปัญหากับ FragmentStatePagerAdapter ที่อาจมีหรือไม่มีปัญหาในการใช้งานของคุณ

ลิงก์ยังมีโซลูชันเพียงเล็กน้อยเช่นกัน ... อาจมีความเหมาะสมกับความต้องการของคุณ


จาก URL นั้นฉันพบวิธีแก้ปัญหา ฉันใช้ FragmentStatePagerAdapter ที่แก้ไขแล้วจากgist.github.com/ypresto/8c13cb88a0973d071a64 สรุปในรหัสของฉันและมันก็เริ่มทำงานตามที่ควรจะเป็น
Rafael

เย็น. ใช่. ลิงก์มีวิธีแก้ไขปัญหาน้อยเช่นกัน
cgr

1

ฉันมีปัญหาแบบเดียวกันและได้ค้นหาหลายครั้งเกินไป คำตอบใด ๆ ที่ให้ไว้ใน stackoverflow หรือทาง google ไม่ได้แก้ปัญหาของฉัน ปัญหาของฉันเป็นเรื่องง่าย ฉันมีรายการฉันแสดงรายการนี้ด้วย viewpager เมื่อฉันเพิ่มองค์ประกอบใหม่ให้กับส่วนหัวของรายการและฉันรีเฟรชการเปลี่ยนมุมมองของ viewpager ทางออกสุดท้ายของฉันคือทุกคนสามารถใช้งานได้ง่ายมาก เมื่อองค์ประกอบใหม่เพิ่มลงในรายการและต้องการรีเฟรชรายการ ตั้งค่าอะแด็ปเตอร์ viewpager เป็น null ก่อนจากนั้นสร้างอะแด็ปเตอร์ขึ้นมาใหม่และตั้งค่า i ให้เป็น viewpager

myVPager.setAdapter(null);
myFragmentAdapter = new MyFragmentAdapter(getSupportFragmentManager(),newList);
myVPager.setAdapter(myFragmentAdapter);

ตรวจสอบให้แน่ใจว่าอะแดปเตอร์ของคุณต้องขยาย FragmentStatePagerAdapter


1

นี่คือการใช้งานของฉันที่รวมข้อมูลจาก @Bill Phillips One ได้รับการแคชส่วนใหญ่เวลายกเว้นเมื่อมีการเปลี่ยนแปลงข้อมูล เรียบง่ายและดูเหมือนว่าจะทำงานได้ดี

MyFragmentStatePagerAdapter.java

private boolean mIsUpdating = false;
public void setIsUpdating(boolean mIsUpdating) {
        this.mIsUpdating = mIsUpdating;
    }


@Override
public int getItemPosition(@NonNull Object object) {

    if (mIsUpdating) {
        return POSITION_NONE;
    }
    else {
        return super.getItemPosition(object);
    }
}

MyActivity.java

mAdapter.setIsUpdating(true);
mAdapter.notifyDataSetChanged();
mAdapter.setIsUpdating(false);

0

ฉันได้ลองวิธีการที่แตกต่างกันมากมายไม่มีใครแก้ปัญหาของฉันได้ ด้านล่างนี้เป็นวิธีที่ฉันจะแก้ปัญหาด้วยการผสมผสานของโซลูชั่นที่คุณได้รับทั้งหมด ขอบคุณทุกคน

class PagerAdapter extends FragmentPagerAdapter {

    public boolean flag_refresh=false;

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

    @Override
    public Fragment getItem(int page) {
        FragmentsMain f;
        f=new FragmentsMain();
        f.page=page;
        return f;
    }

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

    @Override
    public int getItemPosition(Object item) {
        int page= ((FragmentsMain)item).page;

        if (page == 0 && flag_refresh) {
            flag_refresh=false;
            return POSITION_NONE;
        } else {
            return super.getItemPosition(item);
        }
    }

    @Override
    public void destroyItem(View container, int position, Object object) {

        ((ViewPager) container).removeView((View) object);
    }
}

ฉันต้องการรีเฟรชหน้า 0 หลังจาก onResume ()

 adapter=new PagerAdapter(getSupportFragmentManager());
 pager.setAdapter(adapter);

@Override
protected void onResume() {
    super.onResume();

    if (adapter!=null) {
        adapter.flag_refresh=true;
        adapter.notifyDataSetChanged();
    }
}

ใน FragmentsMain ของฉันมี "หน้า" จำนวนเต็มสาธารณะซึ่งสามารถบอกฉันได้ว่ามันเป็นหน้าที่ฉันต้องการรีเฟรช

public class FragmentsMain extends Fragment {

private Cursor cursor;
private static Context context;
public int page=-1;

0

ฉันรู้ว่าสายสำหรับพรรค ฉันได้แก้ไขปัญหาแล้วโดยโทรติดต่อTabLayout#setupWithViewPager(myViewPager);หลังจากนั้นFragmentPagerAdapter#notifyDataSetChanged();


0

โซลูชันนี้ใช้ไม่ได้กับทุกคน แต่ในกรณีของฉัน Fragment ทุกรายการใน ViewPager ของฉันเป็นคลาสที่แตกต่างกันและมีเพียงหนึ่งรายการเท่านั้นที่มีอยู่ในแต่ละครั้ง

ด้วยข้อ จำกัด นี้โซลูชันนี้มีความปลอดภัยและควรปลอดภัยที่จะใช้ในการผลิต

private void updateFragment(Item item) {

    List<Fragment> fragments = getSupportFragmentManager().getFragments();
    for (Fragment fragment : fragments) {

        if (fragment instanceof MyItemFragment && fragment.isVisible()) {
            ((MyItemFragment) fragment).update(item);
        }

    }
}

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


0

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

  1. คัดลอกแหล่งที่มาสำหรับ FragmentStatePagerAdapter (ดูเหมือนจะยังไม่ได้รับการปรับปรุงในยุค)

  2. แทนที่การแจ้งเตือน DataSetChanged ()

    @Override
    public void notifyDataSetChanged() {
        mFragments.clear();
        super.notifyDataSetChanged();
    }
  3. เพิ่มการตรวจสอบสติเพื่อ destroyItem () เพื่อป้องกันการล่ม:

    if (position < mFragments.size()) {
        mFragments.set(position, null);
    }

0

ฉันได้รับคำตอบทั้งหมดข้างต้นและโพสต์อื่น ๆ จำนวนมาก แต่ยังไม่พบสิ่งที่เหมาะกับฉัน (ที่มีประเภทส่วนต่าง ๆ พร้อมกับการเพิ่มและลบแท็บแบบไดนามิก) FWIW แนวทางต่อไปนี้เป็นสิ่งที่ใช้ได้ผลสำหรับฉัน (ในกรณีที่คนอื่นมีปัญหาเดียวกัน)

public class MyFragmentStatePageAdapter extends FragmentStatePagerAdapter {
    private static final String TAB1_TITLE = "Tab 1";
    private static final String TAB2_TITLE = "Tab 2";
    private static final String TAB3_TITLE = "Tab 3";

    private ArrayList<String> titles = new ArrayList<>();
    private Map<Fragment, Integer> fragmentPositions = new HashMap<>();


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


    public void update(boolean showTab1, boolean showTab2, boolean showTab3) {
        titles.clear();

        if (showTab1) {
            titles.add(TAB1_TITLE);
        }
        if (showTab2) {
            titles.add(TAB2_TITLE);
        }
        if (showTab3) {
            titles.add(TAB3_TITLE);
        }
        notifyDataSetChanged();
    }


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

    @Override
    public Fragment getItem(int position) {
        Fragment fragment = null;

        String tabName = titles.get(position);
        if (tabName.equals(TAB1_TITLE)) { 
            fragment =  Tab1Fragment.newInstance();
        } else if (tabName.equals(TAB2_TITLE)) {
            fragment =  Tab2Fragment.newInstance();
        } else if (tabName.equals(TAB3_TITLE)) {
            fragment =  Tab3Fragmen.newInstance();
        } 

        ((BaseFragment)fragment).setTitle(tabName);
        fragmentPositions.put(fragment, position);
        return fragment;
    }

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

    @Override
    public int getItemPosition(Object item) {
        BaseFragment fragment = (BaseFragment)item;
        String title = fragment.getTitle();
        int position = titles.indexOf(title);

        Integer fragmentPosition = fragmentPositions.get(item);
        if (fragmentPosition != null && position == fragmentPosition) {
            return POSITION_UNCHANGED;
        } else {
            return POSITION_NONE;
        }
    }

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

0

ใช้ FragmentStatePagerAdapter แทน FragmentPagerAdapter หากคุณต้องการสร้างใหม่หรือโหลดส่วนบนพื้นฐานดัชนีตัวอย่างเช่นถ้าคุณต้องการที่จะโหลดส่วนอื่นนอกเหนือจาก FirstFragment คุณสามารถตรวจสอบอินสแตนซ์และตำแหน่งกลับเช่นนี้

 public int getItemPosition(Object item) {
    if(item instanceof FirstFragment){
       return 0;
    }
    return POSITION_NONE;
 }

0

คุณต้องเปลี่ยนองค์ประกอบ mFragments ของ instantiateItem getItemPosition

    if (mFragments.size() > position) {
        Fragment f = mFragments.get(position);

        if (f != null) {
            int newPosition = getItemPosition(f);
            if (newPosition == POSITION_UNCHANGED) {
                return f;
            } else if (newPosition == POSITION_NONE) {
                mFragments.set(position, null);
            } else {
                mFragments.set(newPosition, f);
            }
        }
    }

ตาม AndroidX FragmentStatePagerAdapter.java เนื่องจากmFragmentsตำแหน่งองค์ประกอบของไม่เปลี่ยนแปลงเมื่อเรียกการแจ้งให้ทราบ DataSetChanged ()

ที่มา: https://github.com/cuichanghao/infivt/blob/master/library/src/main/java/cc/cuichanghao/library/FragmentStatePagerChangeableAdapter.java

ตัวอย่าง: https://github.com/cuichanghao/infivt/blob/master/app/src/main/java/cc/cuichanghao/infivt/MainActivityChangeablePager.kt

คุณสามารถเรียกใช้โครงการนี้เพื่อยืนยันวิธีการทำงาน https://github.com/cuichanghao/infivt

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