ViewPager PagerAdapter ไม่อัปเดตมุมมอง


597

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

อย่างไรก็ตามฉันพบปัญหาในการอัปเดต ViewPager ด้วยมุมมองใหม่

ฉันได้ลองทุกอย่างเช่นโทรmAdapter.notifyDataSetChanged()แล้วmViewPager.invalidate()สร้างอะแดปเตอร์ใหม่ทุกครั้งที่ฉันต้องการใช้รายการข้อมูลใหม่

ไม่มีสิ่งใดช่วยได้ textviews ยังคงไม่เปลี่ยนแปลงจากข้อมูลต้นฉบับ

อัปเดต: ฉันทำโครงการทดสอบเล็กน้อยและเกือบจะสามารถอัปเดตมุมมองได้ ฉันจะวางชั้นเรียนด้านล่าง

สิ่งที่ไม่ปรากฏให้เห็นในการอัพเดตคือมุมมองที่ 2 ซึ่งยังคงอยู่ 'B' ควรจะแสดง 'Y' หลังจากกดปุ่มอัปเดต

public class ViewPagerBugActivity extends Activity {

    private ViewPager myViewPager;
    private List<String> data;

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

        data = new ArrayList<String>();
        data.add("A");
        data.add("B");
        data.add("C");

        myViewPager = (ViewPager) findViewById(R.id.my_view_pager);
        myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

        Button updateButton = (Button) findViewById(R.id.update_button);
        updateButton.setOnClickListener(new OnClickListener() {

            @Override
            public void onClick(View v) {
                updateViewPager();
            }
        });
    }

    private void updateViewPager() {
        data.clear();
        data.add("X");
        data.add("Y");
        data.add("Z");
        myViewPager.getAdapter().notifyDataSetChanged();
    }

    private class MyViewPagerAdapter extends PagerAdapter {

        private List<String> data;
        private Context ctx;

        public MyViewPagerAdapter(Context ctx, List<String> data) {
            this.ctx = ctx;
            this.data = data;
        }

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

        @Override
        public Object instantiateItem(View collection, int position) {
            TextView view = new TextView(ctx);
            view.setText(data.get(position));
            ((ViewPager)collection).addView(view);
            return view;
        }

        @Override
        public void destroyItem(View collection, int position, Object view) {
             ((ViewPager) collection).removeView((View) view);
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public Parcelable saveState() {
            return null;
        }

        @Override
        public void restoreState(Parcelable arg0, ClassLoader arg1) {
        }

        @Override
        public void startUpdate(View arg0) {
        }

        @Override
        public void finishUpdate(View arg0) {
        }
    }
}

1
โปรดดูstackoverflow.com/questions/12510404/…สำหรับข้อผิดพลาดที่อาจเกิดขึ้นกับ FragmentStatePagerAdapter
samis

2
ขออภัยที่ขุดขึ้นมานี้ คำถามของคุณจริงๆช่วยฉันขอบคุณมาก! สิ่งหนึ่งที่ฉันไม่ได้รับคือการอ้างอิงถึงข้อมูลใน PagerAdapter จะเปลี่ยนไปอย่างไรเมื่อคุณอัปเดตในกิจกรรม
Ewanw

ทำให้อะแดปเตอร์ของคุณขยาย BaseAdapter อ้างอิงสิ่งนี้: stackoverflow.com/questions/13664155/ …

1
เห็นได้ชัดว่าถ้าคุณตั้งอะแดปเตอร์อีกครั้งเป็นเพจเจอร์มุมมองของคุณแล้วมันจะรีเซ็ต
EpicPandaForce

ฉันมีปัญหาในเพจเจอร์stackoverflow.com/questions/41217237/…
chris

คำตอบ:


856

มีหลายวิธีในการบรรลุเป้าหมายนี้

ตัวเลือกแรกนั้นง่ายกว่า แต่ไม่มีประสิทธิภาพมากกว่า

แทนที่getItemPositionในPagerAdapterสิ่งนี้:

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

วิธีนี้เมื่อคุณโทรnotifyDataSetChanged()มุมมองเพจเจอร์จะลบมุมมองทั้งหมดและโหลดซ้ำทั้งหมด เพื่อให้ได้ผลการบรรจุซ้ำ

ตัวเลือกที่สองที่แนะนำโดย Alvaro Luis Bustamante (ก่อนหน้านี้ alvarolb)คือsetTag()วิธีการในการinstantiateItem()สร้างอินสแตนซ์มุมมองใหม่ จากนั้นแทนที่จะใช้notifyDataSetChanged()คุณสามารถใช้findViewWithTag()เพื่อค้นหามุมมองที่คุณต้องการอัปเดต

วิธีที่สองนั้นยืดหยุ่นและมีประสิทธิภาพสูง รุ่งโรจน์เพื่อ alvarolb สำหรับการวิจัยเดิม


4
ดีกว่าการทำงานของฉันดูเหมือนจะไม่มีผลข้างเคียงใด ๆ ขอบคุณ
C0deAttack

3
ฉันมีปัญหาที่คล้ายกัน viewPager ของฉันจะแสดงผลลัพธ์ searc ใหม่ แต่จะไม่กลับไปเป็นรายการแรกในเคอร์เซอร์ ไม่มีใครรู้วิธีรีเซ็ตตำแหน่งเมื่ออัปเดตชุดข้อมูลหรือไม่
mAndroid

1
@mAndroid คุณควรยื่นคำถามแยกต่างหากพร้อมรายละเอียดเพิ่มเติม
rui.araujo

1
การคืนค่า POSITION_NONE ทำให้แอปพลิเคชันโดยรวมมีประสิทธิภาพมากขึ้นเนื่องจากทำให้ Android ลบมุมมองที่เกี่ยวข้องกับตำแหน่งที่สอบถาม มุมมองจะต้องถูกสร้างขึ้นใหม่อีกครั้งก่อนที่จะแสดง แก้ไขปัญหาของมุมมองที่ไม่ได้รับการปรับปรุง แต่อาจไม่ใช่วิธีที่ดีที่สุดสำหรับเค้าโครงที่ซับซ้อน
wufoo

11
คำตอบที่ดีกว่า (โดยไม่ลบรายการทั้งหมด) -> stackoverflow.com/a/10852046/898056
yhpark

484

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

ไม่กี่วันที่ผ่านมาผมได้ทำงานร่วมกับPagerAdapterและViewPagerและฉันพบต่อไปนี้:

notifyDataSetChanged()วิธีการเกี่ยวกับการPagerAdapterจะแจ้งViewPagerว่าหน้าต้นแบบที่มีการเปลี่ยนแปลง ตัวอย่างเช่นหากคุณสร้าง / ลบหน้าเว็บแบบไดนามิก (การเพิ่มหรือลบรายการออกจากรายการของคุณ) ViewPagerควรดูแลสิ่งนั้น ในกรณีนี้ฉันคิดว่าการViewPagerพิจารณาว่าควรจะลบมุมมองใหม่หรือสร้างอินสแตนซ์โดยใช้getItemPosition()และgetCount()วิธีการ

ผมคิดว่าViewPagerหลังจากที่มีการเรียกใช้มันมุมมองของเด็กและการตรวจสอบตำแหน่งของพวกเขาด้วยnotifyDataSetChanged() getItemPosition()หากสำหรับมุมมองที่เด็กส่งคืนเมธอดนี้POSITION_NONEแสดงViewPagerว่าเข้าใจว่ามุมมองนั้นถูกลบไปแล้วdestroyItem()และลบมุมมองนี้ออก

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

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

ลองนึกภาพตัวอย่างว่าคุณมี 100 หน้าที่มี 100 TextViewวินาทีและคุณต้องการอัปเดตหนึ่งค่าเป็นระยะ ด้วยวิธีการที่อธิบายไว้ก่อนหน้านี้หมายความว่าคุณกำลังลบและเริ่มต้น 100 TextViewวินาทีในการอัพเดทแต่ละครั้ง มันไม่ได้ทำให้ความรู้สึก...


11
+1 - การแก้ปัญหาของคุณไม่เพียงทำงานได้อย่างมีเสน่ห์ที่ฉันพบเจอในปัญหาที่คุณอธิบายตรงนี้เมื่อฉันต้องอัปเดตหน้าเว็บที่มีแท็บที่มีรายการดูตัวอย่างข้อความและรูปภาพ (OutOfErrorExceptions และอื่น ๆ ... ) ขอบคุณ!
schlingel

3
@alvarolb: สวัสดีฉันไม่ทราบว่าคุณหมายถึงsetTagในวิธีการที่onInstantiateItemคุณต้องการที่จะปรับปรุงคำตอบนี้ด้วยการเข้ารหัสบางส่วน? ขอบคุณ
Huy Tower

35
มันจะสมบูรณ์แบบถ้าคุณเขียนตัวอย่าง
Sami Eltamawy

13
ดังที่มีคนพูดว่า: ตัวอย่าง woulb ได้รับการชื่นชม!
Jey10

15
6 ปีแล้วไม่มีใครสามารถเป็นตัวอย่างได้
Oussaki

80

เปลี่ยนFragmentPagerAdapterไปFragmentStatePagerAdapterไป

แทนที่getItemPosition()เมธอดและส่งคืนPOSITION_NONEวิธีการและผลตอบแทน

ในที่สุดมันจะฟังnotifyDataSetChanged()วิทยุติดตามตัวในมุมมอง


ฉันคิดว่าคุณควรจะใช้ getItemPosition ทั้งหมดอย่างถูกต้องและส่งคืน POSITION_NONE หากไม่พบชิ้นส่วน
tkhduracell

46

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

SparseArray<View> views = new SparseArray<View>();

@Override
public Object instantiateItem(View container, int position) {
    View root = <build your view here>;
    ((ViewPager) container).addView(root);
    views.put(position, root);
    return root;
}

@Override
public void destroyItem(View collection, int position, Object o) {
    View view = (View)o;
    ((ViewPager) collection).removeView(view);
    views.remove(position);
    view = null;
}

จากนั้นอีกครั้งโดยการเอาชนะnotifyDataSetChangedวิธีที่คุณสามารถรีเฟรชมุมมอง ...

@Override
public void notifyDataSetChanged() {
    int key = 0;
    for(int i = 0; i < views.size(); i++) {
       key = views.keyAt(i);
       View view = views.get(key);
       <refresh view with new data>
    }
    super.notifyDataSetChanged();
}

คุณสามารถใช้รหัสที่คล้ายกันในinstantiateItemและnotifyDataSetChangedเพื่อรีเฟรชมุมมองของคุณ ในรหัสของฉันฉันใช้วิธีการเดียวกันแน่นอน


8
คุณสามารถให้ตัวอย่างอะแดปเตอร์เต็มรูปแบบได้หรือไม่ สิ่งนี้เสร็จสิ้นโดยใช้ Fragments หรือไม่?

9
<รีเฟรชมุมมองด้วยข้อมูลใหม่> ?? เราต้องเพิ่มเขาดูที่นี่หรืออะไร?
Akarsh M

วิธีนี้จะอัพเดตมุมมองทั้งหมด หากคุณต้องการอัปเดตหนึ่งมุมมองจากนั้นคุณสามารถเขียนวิธี informDataItemChanged ของคุณเองเช่นนี้: void public informDataItemChanged (int pos) {TextView tv = (TextView) views.get (pos) .findViewById (R.id.tv); tv.setText (list.get (POS)); super.notifyDataSetChanged (); }
Kai Wang

รหัสนี้ขัดข้องหากขนาดของเพจเจอร์ใหม่น้อยกว่าขนาดเก่า
Anton Duzenko

@AntonDuzenko ถูกต้องการลบมุมมองค่อนข้างยาก
MLProgrammer-CiM

28

มีปัญหาเดียวกัน สำหรับฉันมันทำงานเพื่อขยาย FragmentStatePagerAdapter และแทนที่วิธีการด้านล่าง:

@Override
public Parcelable saveState() {
    return null;
}

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

}

นี่เป็นทางออกเดียวสำหรับฉันเช่นกัน เคสของฉันไม่ได้อัพเดตแฟรกเมนต์ แต่ลบหรือเพิ่มแฟรกเมนต์แบบไดนามิก โดยไม่ต้องขยาย FragmentStatePagerAdapter, ViewPager ส่งกลับชิ้นส่วนแคชซึ่งเป็นตำแหน่งที่ไม่ถูกต้อง ขอบคุณ!
Sira Lam

ทำงานได้ดีกับห้องสมุดสนับสนุน 28.0.0
Oleksii K.

แทนที่จะทำอะไร วิธีการสร้างออบเจ็กต์สถานะที่เกี่ยวข้องกับอแด็ปเตอร์
Phani Rithvij

ขอบคุณเพื่อน. วิธีนี้ใช้ได้เฉพาะ
Parthi

20

หลังจากชั่วโมงของแห้วขณะที่พยายามทุกการแก้ปัญหาดังกล่าวข้างต้นที่จะเอาชนะปัญหานี้และยังพยายามแก้ปัญหาจำนวนมากในคำถามอื่น ๆ ที่คล้ายกันเช่นนี้ , นี้และนี้ซึ่งทั้งหมดล้มเหลวด้วยผมที่จะแก้ปัญหานี้และจะทำให้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();

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


@Tomer ปัญหาที่คุณเผชิญอยู่คืออะไร?
Sami Eltamawy


9

ฉันมีปัญหาเดียวกันและวิธีแก้ปัญหาของฉันใช้FragmentPagerAdapterกับการเอาชนะFragmentPagerAdapter#getItemId(int position):

@Override
public long getItemId(int position) {
    return mPages.get(position).getId();
}

ตามค่าเริ่มต้นวิธีนี้จะส่งคืนตำแหน่งของรายการ ฉันคิดว่าการViewPagerตรวจสอบว่าitemIdมีการเปลี่ยนแปลงและสร้างหน้าใหม่หากมันเป็น แต่เวอร์ชันที่ไม่ใช่ overriden จะส่งคืนตำแหน่งเดียวกันกับitemIdแม้ว่าหน้าจะแตกต่างกันจริงและ ViewPager ไม่ได้กำหนดว่าหน้านั้นจะถูกแทนที่หนึ่งหน้าและจำเป็นต้องสร้างขึ้นใหม่

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

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


1
มันคือ. ฉันตรวจสอบแล้ว: แค่ POSITION_NONE ไม่เพียงพอ ขอบคุณสำหรับคำตอบที่ยอดเยี่ยมของคุณ! :)
Slava

ฟังก์ชั่นนี้ไม่ได้มีอยู่ในPagerAdapterระบบ คลาสนี้คืออะไร
Saket

@ Saket คุณพูดถูกคลาสเป็นFragmentPagerAdapter
atlascoder

6

ฉันพบการตัดสินใจที่น่าสนใจของปัญหานี้ แทนที่จะใช้FragmentPagerAdapterซึ่งเก็บไว้ในหน่วยความจำชิ้นส่วนทั้งหมดเราสามารถใช้FragmentStatePagerAdapter ( android.support.v4.app.FragmentStatePagerAdapter ) ซึ่งจะโหลดชิ้นส่วนใหม่ทุกครั้งเมื่อเราเลือก

การรับรู้ของอะแดปเตอร์ทั้งสองเหมือนกัน ดังนั้นเราต้องเปลี่ยน " ขยาย FragmentPagerAdapter " บน " ขยาย FragmentStatePagerAdapter "


จุดที่ดีมันจะช่วยให้คนอื่น ๆ ในการแก้ปัญหาหน่วยความจำของ viewPager ที่เกี่ยวข้องกับส่วน
Ashu Kumar

คำตอบที่ดีนี่ช่วยฉันได้มาก
Sergio Marani

5

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

ดังนั้นใน MyViewPagerAdapter ของคุณเพิ่มตัวแปรเช่น:

private View updatableView;

ใน instantiateItem ของคุณ:

 public Object instantiateItem(View collection, int position) {
        updatableView = new TextView(ctx); //My change is here
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);
        return view;
    }

ดังนั้นด้วยวิธีนี้คุณสามารถสร้างฟังก์ชั่นที่จะอัปเดตมุมมองของคุณ:

public updateText(String txt)
{
    ((TextView)updatableView).setText(txt);
}

หวังว่านี่จะช่วยได้!


ฉันรู้ว่าฉันไม่ได้ตั้งค่าข้อมูลด้วยinstantiateItemเหตุนี้มุมมองของฉันจึงไม่ได้รับการอัปเดต จากคำตอบของคุณฉันรู้ว่ามัน +1
Phani Rithvij

5

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

ในส่วนของฉัน (หน้า):

  • ฉันใช้รหัสทั้งหมดที่เติมแบบฟอร์มจาก onCreateView และวางไว้ในฟังก์ชั่นที่เรียกว่า PopulateForm ซึ่งอาจเรียกได้จากทุกที่แทนที่จะเป็นเฟรมเวิร์ก ฟังก์ชั่นนี้จะพยายามรับมุมมองปัจจุบันโดยใช้ getView และหากเป็นโมฆะฟังก์ชันจะคืนค่า เป็นสิ่งสำคัญที่ PopulateForm มีเพียงรหัสที่แสดง - รหัสอื่น ๆ ทั้งหมดที่สร้างผู้ฟัง FocusChange และสิ่งที่ชอบยังคงอยู่ใน OnCreate
  • สร้างบูลีนซึ่งสามารถใช้เป็นแฟล็กที่ระบุว่าจะต้องโหลดฟอร์มซ้ำ ของฉันคือ mbReloadForm
  • แทนที่ OnResume () เพื่อเรียก PopulateForm () ถ้าตั้งค่า mbReloadForm

ในกิจกรรมของฉันที่ฉันทำการโหลดหน้า:

  • ไปที่หน้า 0 ก่อนที่จะเปลี่ยนแปลงอะไร ฉันใช้ FragmentStatePagerAdapter ดังนั้นฉันจึงรู้ว่าส่วนใหญ่สองหรือสามหน้าได้รับผลกระทบมากที่สุด การเปลี่ยนเป็นหน้า 0 ช่วยให้มั่นใจได้ว่าฉันมีปัญหาในหน้า 0, 1 และ 2 เท่านั้น
  • ก่อนล้างรายการเก่าให้ใช้ขนาด () วิธีนี้ทำให้คุณทราบว่ามีกี่หน้าที่ได้รับผลกระทบจากข้อบกพร่อง ถ้า> 3 ลดลงเหลือ 3 - ถ้าคุณใช้ PagerAdapter ที่แตกต่างกันคุณจะต้องดูว่าคุณต้องจัดการกับหน้าเว็บกี่หน้า (อาจจะทั้งหมดหรือไม่)
  • โหลดข้อมูลใหม่และโทร pageAdapter.notifyDataSetChanged ()
  • ตอนนี้สำหรับแต่ละหน้าที่ได้รับผลกระทบดูว่าหน้านั้นเปิดใช้งานอยู่หรือไม่โดยใช้ pager.getChildAt (i) - สิ่งนี้จะบอกคุณว่าคุณมีมุมมองหรือไม่ ถ้าเป็นเช่นนั้นโทร pager.PopulateView () หากไม่มีให้ตั้งค่าสถานะ ReloadForm

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

หวังว่านี่จะช่วยใครซักคน!


5

วิธีแก้ปัญหาทั้งหมดนี้ไม่ได้ช่วยฉัน ดังนั้นฉันจึงพบวิธีแก้ปัญหาการทำงาน: คุณสามารถทำได้setAdapterทุกครั้ง แต่ก็ไม่เพียงพอ คุณควรทำสิ่งเหล่านี้ก่อนที่จะเปลี่ยนอะแดปเตอร์:

FragmentManager fragmentManager = slideShowPagerAdapter.getFragmentManager();
FragmentTransaction transaction = fragmentManager.beginTransaction();
List<Fragment> fragments = fragmentManager.getFragments();
for (Fragment f : fragments) {
    transaction.remove(f);
}
transaction.commit();

และหลังจากนี้:

viewPager.setAdapter(adapter);

ขอบคุณ! ในกรณีของฉันฉันใช้ViewPagerชิ้นส่วนภายในดังนั้นแทนที่ด้วยslideShowPagerAdapter.getFragmentManager() getChildFragmentManager()อาจgetFragmentManager()จะช่วยในกรณีของคุณ ผมใช้ไม่ได้FragmentPagerAdapter FragmentStatePagerAdapterโปรดดูstackoverflow.com/a/25994654/2914140เพื่อดูวิธีแฮ็ค
CoolMind

5

วิธีที่ง่ายกว่ามาก: ใช้ a FragmentPagerAdapterและห่อมุมมองเพจของคุณไว้บนชิ้นส่วน พวกเขาจะได้รับการปรับปรุง


5

ขอบคุณ rui.araujo และ Alvaro Luis Bustamante ตอนแรกฉันพยายามใช้วิธีของ rui.araujo เพราะมันง่าย มันใช้งานได้ แต่เมื่อมีการเปลี่ยนแปลงข้อมูลหน้าจะวาดใหม่อย่างชัดเจน มันแย่มากดังนั้นฉันพยายามใช้วิธีของ Alvaro Luis Bustamante มันสมบูรณ์แบบ. นี่คือรหัส:

@Override
protected void onStart() {
    super.onStart();
}

private class TabPagerAdapter extends PagerAdapter {
    @Override
    public int getCount() {
        return 4;
    }

    @Override
    public boolean isViewFromObject(final View view, final Object object) {
        return view.equals(object);
    }

    @Override
    public void destroyItem(final View container, final int position, final Object object) {
        ((ViewPager) container).removeView((View) object);
    }

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View view = LayoutInflater.from(
                getBaseContext()).inflate(R.layout.activity_approval, null, false);
        container.addView(view);
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        view.setTag(position);
        new ShowContentListTask(listView, position).execute();
        return view;
    }
}

และเมื่อข้อมูลมีการเปลี่ยนแปลง:

for (int i = 0; i < 4; i++) {
    View view = contentViewPager.findViewWithTag(i);
    if (view != null) {
        ListView listView = (ListView) view.findViewById(R.id.list_view);
        new ShowContentListTask(listView, i).execute();
    }
}

4

ในกรณีที่ใครก็ตามที่ใช้อะแดปเตอร์ที่ใช้FragmentStatePagerAdapter (ซึ่งจะอนุญาตให้ ViewPager สร้างหน้าขั้นต่ำที่จำเป็นสำหรับการแสดงผล, มากที่สุด 2 สำหรับกรณีของฉัน), @ rui.araujo คำตอบของการเขียนทับ getItemPosition ในอะแดปเตอร์ของคุณ สามารถปรับปรุงได้

ในรหัสหลอก:

public int getItemPosition(Object object) {
    YourFragment f = (YourFragment) object;
    YourData d = f.data;
    logger.info("validate item position on page index: " + d.pageNo);

    int dataObjIdx = this.dataPages.indexOf(d);

    if (dataObjIdx < 0 || dataObjIdx != d.pageNo) {
        logger.info("data changed, discard this fragment.");
        return POSITION_NONE;
    }

    return POSITION_UNCHANGED;
}

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

3

ฉันมีปัญหาที่คล้ายกันซึ่งฉันมีสี่หน้าและหนึ่งในหน้าปรับปรุงมุมมองในอีกสามคน ฉันสามารถอัปเดตวิดเจ็ต (SeekBars, TextViews, ฯลฯ ) ในหน้าถัดจากหน้าปัจจุบัน mTabsAdapter.getItem(position)สองหน้าสุดท้ายจะมีเครื่องมือเตรียมเมื่อโทร

เพื่อแก้ปัญหาของฉันฉันใช้ก่อนที่จะเรียกsetSelectedPage(index) getItem(position)นี่จะยกตัวอย่างหน้าซึ่งทำให้ฉันสามารถเปลี่ยนค่าและวิดเจ็ตในแต่ละหน้า

หลังจากทั้งหมดของการปรับปรุงผมจะใช้ตามด้วยsetSelectedPage(position)notifyDataSetChanged()

คุณสามารถเห็นการสั่นไหวเล็กน้อยใน ListView บนหน้าการอัพเดตหลัก แต่ไม่มีอะไรที่สังเกตได้ ฉันไม่ได้ทดสอบอย่างละเอียด แต่มันก็แก้ปัญหาได้ทันที


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

3

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

จากนั้นใน CustomViewPager ให้สร้างวิธีที่เรียกว่า updateViewAt (ตำแหน่ง int) มุมมองสามารถรับได้จาก ArrayList mItems ที่กำหนดในคลาส ViewPager (คุณต้องตั้งค่า Id สำหรับมุมมองที่ไอเท็มอินสแตนซ์และเปรียบเทียบ id นี้กับตำแหน่งในเมธอด updateViewAt () จากนั้นคุณสามารถอัปเดตมุมมองได้ตามความจำเป็น


2

ฉันเดาว่าฉันมี logics ของ ViewPager แล้ว

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

ในกรณีของฉันฉันมีเลย์เอาต์ 2 บานพร้อมรายการของไอเท็มยอดนิยมที่บานหน้าต่างด้านซ้ายและมุมมองแบบกวาดนิ้ว (ViewPager) ทางด้านขวา ดังนั้นฉันจะจัดเก็บลิงค์ไปยังรายการยอดนิยมปัจจุบันของฉันใน PagerAdapter ของฉันและอยู่ในส่วนต่างๆของหน้าตัวอย่าง เมื่อรายการด้านบนที่เลือกในการเปลี่ยนแปลงรายการที่ฉันเก็บรายการสูงสุดใหม่ใน PagerAdapter และโทรnotifyDataSetChanged () และในgetItemPosition ()แทนที่ ฉันได้เปรียบเทียบไอเท็มยอดนิยมจากอะแดปเตอร์ของฉันกับไอเท็มยอดนิยมจากแฟรกเมนต์ของฉัน และเฉพาะในกรณีที่ไม่เท่ากันฉันส่งคืน POSITION_NONE จากนั้น PagerAdapter จะคืนค่าส่วนย่อยทั้งหมดที่คืนค่า POSITION_NONE

บันทึก. การจัดเก็บไอเท็มอันดับต้น ๆ แทนที่จะเป็นข้อมูลอ้างอิงอาจเป็นแนวคิดที่ดีกว่า

ข้อมูลโค้ดด้านล่างเป็นบิตเชิงคณิตศาสตร์ แต่ฉันปรับมันจากรหัสที่ใช้งานได้จริง

public class SomeFragment extends Fragment {
  private TopItem topItem;
}

public class SomePagerAdapter extends FragmentStatePagerAdapter {
  private TopItem topItem;

  public void changeTopItem(TopItem newTopItem) {
    topItem = newTopItem;
    notifyDataSetChanged();
  }

  @Override
  public int getItemPosition(Object object) {
    if (((SomeFragment) object).getTopItemId() != topItem.getId()) {
      return POSITION_NONE;
    }
    return super.getItemPosition(object);
  }
}

ขอบคุณสำหรับนักวิจัยก่อนหน้าทั้งหมด!


2

รหัสด้านล่างใช้ได้กับฉัน

สร้างคลาสที่ขยายคลาส FragmentPagerAdapter ดังต่อไปนี้

public class Adapter extends FragmentPagerAdapter {

private int tabCount;
private Activity mActivity;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;
private int container_id;
private ViewGroup container;
private List<Object> object;

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

public Adapter(FragmentManager fm, int numberOfTabs , Activity mA) {
    super(fm);
    mActivity = mA;
    mFragmentManager = fm;
    object = new ArrayList<>();
    mFragmentTags = new HashMap<Integer, String>();
    this.tabCount = numberOfTabs;
}

@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return Fragment0.newInstance(mActivity);
        case 1:
            return Fragment1.newInstance(mActivity);
        case 2:
            return Fragment2.newInstance(mActivity);
        default:
            return null;
    }}


@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Log.e("Already defined","Yes");
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        Log.e("Fragment Tag","" + position + ", " + tag);
        mFragmentTags.put(position, tag);
    }else{
        Log.e("Already defined","No");
    }
    container_id = container.getId();
    this.container = container;
    if(position == 0){
        this.object.add(0,object);
    }else if(position == 1){
        this.object.add(1,object);
    }else if(position == 2){
        this.object.add(2,object);
    }
    return object;
}

@Override
public void destroyItem(ViewGroup container, int position, Object object) {
    super.destroyItem(container, position, object);
    if (object instanceof Fragment) {
        Log.e("Removed" , String.valueOf(position));
    }
}

@Override
public int getItemPosition (Object object)
{   int index = 0;
    if(this.object.get(0) == object){
        index = 0;
    }else if(this.object.get(1) == object){
        index = 1;
    }else if(this.object.get(2) == object){
        index = 2;
    }else{
        index = -1;
    }
    Log.e("Index" , "..................." + String.valueOf(index));
    if (index == -1)
        return POSITION_NONE;
    else
        return index;
}

public String getFragmentTag(int pos){
    return "android:switcher:"+R.id.pager+":"+pos;
}

public void NotifyDataChange(){
    this.notifyDataSetChanged();
}

public int getcontainerId(){
    return container_id;
}

public ViewGroup getContainer(){
    return this.container;
}

public List<Object> getObject(){
    return this.object;
}

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

จากนั้นภายในแต่ละแฟรกเมนต์ที่คุณสร้างให้สร้างเมธอด updateFragment ในวิธีนี้คุณเปลี่ยนสิ่งที่คุณต้องเปลี่ยนในส่วน ตัวอย่างเช่นในกรณีของฉัน Fragment0 มี GLSurfaceView ซึ่งแสดงวัตถุ 3 มิติตามเส้นทางไปยังไฟล์ .ply ดังนั้นภายในเมธอด updateFragment ของฉันฉันเปลี่ยนพา ธ ไปเป็นไฟล์เร่นี้

จากนั้นสร้างอินสแตนซ์ ViewPager

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

และตัวอย่างของ Adpater

adapter = new Adapter(getSupportFragmentManager(), 3, this);

จากนั้นทำสิ่งนี้

viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);

จากนั้นภายในคลาสคุณได้เริ่มต้นคลาสของอะแดปเตอร์ด้านบนและสร้าง viewPager ทุกครั้งที่คุณต้องการอัปเดตหนึ่งในแฟรกเมนต์ของคุณ (ในกรณีของเราแฟรกเมนต์ 0) ใช้สิ่งต่อไปนี้:

adapter.NotifyDataChange();

adapter.destroyItem(adapter.getContainer(), 0, adapter.getObject().get(0)); // destroys page 0 in the viewPager.

fragment0 = (Fragment0) getSupportFragmentManager().findFragmentByTag(adapter.getFragmentTag(0)); // Gets fragment instance used on page 0.

fragment0.updateFragment() method which include the updates on this fragment

adapter.instantiateItem(adapter.getContainer(), 0); // re-initialize page 0.

วิธีนี้เป็นไปตามเทคนิคที่แนะนำโดย Alvaro Luis Bustamante


1

1. ก่อนอื่นคุณต้องตั้งค่าเมธอด getItemposition ในคลาส Pageradapter ของคุณ 2. คุณต้องอ่านตำแหน่งที่แน่นอนของ View Pager ของคุณ 3. จากนั้นส่งตำแหน่งนั้นเป็นตำแหน่งข้อมูลของหนึ่งใหม่ของคุณปุ่มอัปเดต 4.Write onclick ภายใน setonPageChange ผู้ฟัง

รหัสโปรแกรมนั้นเล็กน้อยฉันปรับเปลี่ยนเพื่อตั้งองค์ประกอบตำแหน่งเฉพาะเท่านั้น

public class MyActivity extends Activity {

private ViewPager myViewPager;
private List<String> data;
public int location=0;
public Button updateButton;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    data = new ArrayList<String>();
    data.add("A");
    data.add("B");
    data.add("C");
    data.add("D");
    data.add("E");
    data.add("F");

    myViewPager = (ViewPager) findViewById(R.id.pager);
    myViewPager.setAdapter(new MyViewPagerAdapter(this, data));

      updateButton = (Button) findViewById(R.id.update);

    myViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        @Override
        public void onPageScrolled(int i, float v, int i2) {
             //Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
        }

        @Override
        public void onPageSelected( int i) {
          // here you will get the position of selected page
            final int k = i;
             updateViewPager(k);

        }

        @Override
        public void onPageScrollStateChanged(int i) {

        }
    });
}

private void updateViewPager(final int i) {  
    updateButton.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {

            Toast.makeText(MyActivity.this, i+"  Is Selected  "+data.size(), Toast.LENGTH_SHORT).show();
            data.set(i, "Replaced "+i);         
            myViewPager.getAdapter().notifyDataSetChanged();
        }
    });

}

private class MyViewPagerAdapter extends PagerAdapter {

    private List<String> data;
    private Context ctx;

    public MyViewPagerAdapter(Context ctx, List<String> data) {
        this.ctx = ctx;
        this.data = data;
    }

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

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

    @Override
    public Object instantiateItem(View collection, int position) {          

        TextView view = new TextView(ctx);
        view.setText(data.get(position));
        ((ViewPager)collection).addView(view);            
        return view;
    }

    @Override
    public void destroyItem(View collection, int position, Object view) {
         ((ViewPager) collection).removeView((View) view);
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Parcelable saveState() {
        return null;
    }

    @Override
    public void restoreState(Parcelable arg0, ClassLoader arg1) {
    }

    @Override
    public void startUpdate(View arg0) {
    }

    @Override
    public void finishUpdate(View arg0) {
    }
}
}

1

สิ่งที่ได้ผลสำหรับฉันกำลังจะไป viewPager.getAdapter().notifyDataSetChanged();

และในอะแดปเตอร์ใส่รหัสของคุณสำหรับการปรับปรุงมุมมองภายในgetItemPositionเช่นนั้น

@Override
public int getItemPosition(Object object) {

    if (object instanceof YourViewInViewPagerClass) { 
        YourViewInViewPagerClass view = (YourViewInViewPagerClass)object;
        view.setData(data);
    }

    return super.getItemPosition(object);
}

อาจไม่ใช่วิธีที่ถูกต้องที่สุดในการดำเนินเรื่องนี้ แต่ใช้งานได้ ( return POSITION_NONEเคล็ดลับทำให้เกิดความผิดพลาดสำหรับฉันดังนั้นจึงไม่ใช่ตัวเลือก)


1

คุณสามารถอัพเดทชิ้นส่วนทั้งหมดแบบไดนามิกคุณสามารถดูได้ในสามขั้นตอน

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

public class MyPagerAdapter extends FragmentPagerAdapter {
private static int NUM_ITEMS = 3;
private Map<Integer, String> mFragmentTags;
private FragmentManager mFragmentManager;

public MyPagerAdapter(FragmentManager fragmentManager) {
    super(fragmentManager);
    mFragmentManager = fragmentManager;
    mFragmentTags = new HashMap<Integer, String>();
}

// Returns total number of pages
@Override
public int getCount() {
    return NUM_ITEMS;
}

// Returns the fragment to display for that page
@Override
public Fragment getItem(int position) {
    switch (position) {
        case 0:
            return FirstFragment.newInstance();
        case 1:
            return SecondFragment.newInstance();
        case 2:
            return ThirdFragment.newInstance();
        default:
            return null;
    }
}

// Returns the page title for the top indicator
@Override
public CharSequence getPageTitle(int position) {
    return "Page " + position;
}

@Override
public Object instantiateItem(ViewGroup container, int position) {
    Object object = super.instantiateItem(container, position);
    if (object instanceof Fragment) {
        Fragment fragment = (Fragment) object;
        String tag = fragment.getTag();
        mFragmentTags.put(position, tag);
    }
    return object;
}

public Fragment getFragment(int position) {
    Fragment fragment = null;
    String tag = mFragmentTags.get(position);
    if (tag != null) {
        fragment = mFragmentManager.findFragmentByTag(tag);
    }
    return fragment;
}}

ตอนนี้ในกิจกรรมของคุณ:

public class MainActivity extends AppCompatActivity implements ViewPager.OnPageChangeListener{

MyPagerAdapter mAdapterViewPager;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    ViewPager viewPager = (ViewPager) findViewById(R.id.vpPager);
    mAdapterViewPager = new MyPagerAdapter(getSupportFragmentManager());
    viewPager.setAdapter(mAdapterViewPager);
    viewPager.addOnPageChangeListener(this);
}

@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

}

@Override
public void onPageSelected(int position) {

    Fragment fragment = mAdapterViewPager.getFragment(position);
    if (fragment != null) {
        fragment.onResume();
    }
}

@Override
public void onPageScrollStateChanged(int state) {

}}

ในที่สุดในส่วนของคุณบางสิ่งเช่นนั้น:

public class YourFragment extends Fragment {

// newInstance constructor for creating fragment with arguments
public static YourFragment newInstance() {

    return new YourFragment();
}

// Store instance variables based on arguments passed
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

// Inflate the view for the fragment based on layout XML
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    return inflater.inflate(R.layout.fragment, container, false);
}


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

    //to refresh your view
    refresh();

}}

คุณสามารถดูรหัสที่สมบูรณ์ที่นี่

ขอบคุณ Alvaro Luis Bustamante


1

การกลับมาPOSITION_NONEเป็นสิ่งที่เรียบง่าย แต่เป็นวิธีที่ไม่มีประสิทธิภาพเพียงเล็กน้อยเพราะนั่นทำให้เกิดการสร้างอินสแตนซ์ของหน้าทั้งหมดที่มีอินสแตนซ์แล้ว

ฉันสร้างไลบรารี ArrayPagerAdapterเพื่อเปลี่ยนรายการใน PagerAdapters แบบไดนามิก

ภายในอะแดปเตอร์ของห้องสมุดนี้กลับมาPOSITION_NONEในgetItemPosiition()เมื่อจำเป็นเท่านั้น

คุณสามารถเปลี่ยนรายการแบบไดนามิกเช่นการติดตามโดยใช้ห้องสมุดนี้

@Override
protected void onCreate(Bundle savedInstanceState) {
        /** ... **/
    adapter = new MyStatePagerAdapter(getSupportFragmentManager()
                            , new String[]{"1", "2", "3"});
    ((ViewPager)findViewById(R.id.view_pager)).setAdapter(adapter);
     adapter.add("4");
     adapter.remove(0);
}

class MyPagerAdapter extends ArrayViewPagerAdapter<String> {

    public MyPagerAdapter(String[] data) {
        super(data);
    }

    @Override
    public View getView(LayoutInflater inflater, ViewGroup container, String item, int position) {
        View v = inflater.inflate(R.layout.item_page, container, false);
        ((TextView) v.findViewById(R.id.item_txt)).setText(item);
        return v;
    }
}

ห้องสมุด Thils ยังสนับสนุนหน้าที่สร้างขึ้นโดย Fragments


1

นี่คือสำหรับทุกคนเช่นฉันซึ่งต้องอัปเดต Viewpager จากบริการ (หรือเธรดพื้นหลังอื่น ๆ ) และไม่มีข้อเสนอใด ๆ ที่ทำงานได้: หลังจากตรวจสอบบิตของฉันแล้วฉันก็รู้ว่าเมธอด informDataSetChanged () getItemPosition (Object Object) เรียกว่าสิ้นสุดทั้งหมดโดยไม่ต้องดำเนินการเพิ่มเติม จากนั้นฉันพบในเอกสารของคลาส PagerAdapter หลัก (ไม่ได้อยู่ในเอกสารของคลาสย่อย) "การเปลี่ยนแปลงชุดข้อมูลจะต้องเกิดขึ้นในเธรดหลักและต้องจบด้วยการเรียกไปยัง AlertDataSetChanged ()" ดังนั้นวิธีแก้ปัญหาการทำงานในกรณีนี้คือ (ใช้ FragmentStatePagerAdapter และ getItemPosition (วัตถุ Object) ตั้งค่าให้ส่งคืน POSITION_NONE):

จากนั้นโทรไปที่ informDataSetChanged ():

runOnUiThread(new Runnable() {
         @Override
         public void run() {
             pager.getAdapter().notifyDataSetChanged();
         }
     });

1

คุณสามารถเพิ่มการแปลงเพจเจอร์ใน Viewpager เช่นนี้

myPager.setPageTransformer(true, new MapPagerTransform());

ในรหัสด้านล่างฉันเปลี่ยนสีมุมมองของฉันเมื่อรันไทม์เมื่อเลื่อนเพจเจอร์

public class MapPagerTransform implements ViewPager.PageTransformer {


    public void transformPage(View view, float position) {
     LinearLayout  showSelectionLL=(LinearLayout)view.findViewById(R.id.showSelectionLL);

     if (position < 0) {

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else if (position >0){

                showSelectionLL.setBackgroundColor(Color.WHITE);
     }else {
                showSelectionLL.setBackgroundColor(Color.RED);
     }
   }
}

1

ฉันรู้ว่าฉันเขื่อนสาย แต่ก็ยังสามารถช่วยใครได้ ฉันแค่ขยายคำตอบ accetping และฉันได้เพิ่มความคิดเห็นไว้ด้วย

ดี,

คำตอบนั้นบอกว่ามันไม่มีประสิทธิภาพ

ดังนั้นเพื่อให้รีเฟรชได้อย่างเดียวเมื่อต้องการคุณสามารถทำได้

private boolean refresh;

public void refreshAdapter(){
    refresh = true;
    notifyDataSetChanged();
}

@Override
public int getItemPosition(@NonNull Object object) {
    if(refresh){
        refresh = false;
        return POSITION_NONE;
    }else{
        return super.getItemPosition(object);
    }
}

1

ViewPager ไม่ได้ถูกออกแบบมาเพื่อรองรับการเปลี่ยนแปลงมุมมองแบบไดนามิก

ฉันได้รับการยืนยันเมื่อพบข้อผิดพลาดอื่นที่เกี่ยวข้องกับhttps://issuetracker.google.com/issues/36956111และโดยเฉพาะ https://issuetracker.google.com/issues/36956111#comment56

คำถามนี้เป็นบิตเก่า แต่ Google เมื่อเร็ว ๆ นี้ได้รับการแก้ไขปัญหานี้ด้วยการViewPager2 มันจะช่วยให้การเปลี่ยนโซลูชั่นทำด้วยมือ (unmaintained และอาจ buggy) โซลูชั่นโดยมาตรฐาน นอกจากนี้ยังป้องกันการสร้างมุมมองใหม่โดยไม่จำเป็นเช่นเดียวกับคำตอบบางอย่าง

สำหรับตัวอย่าง ViewPager2 คุณสามารถตรวจสอบhttps://github.com/googlesamples/android-viewpager2

หากคุณต้องการใช้ ViewPager2 คุณจะต้องเพิ่มการพึ่งพาต่อไปนี้ในไฟล์ build.gradle ของคุณ:

  dependencies {
     implementation 'androidx.viewpager2:viewpager2:1.0.0-beta02'
  }

จากนั้นคุณสามารถแทนที่ ViewPager ของคุณในไฟล์ xml ด้วย:

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/pager"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1" />

หลังจากนั้นคุณจะต้องแทนที่ ViewPager โดย ViewPager2 ในกิจกรรมของคุณ

ViewPager2 ต้องการ RecyclerView.Adapter หรือ FragmentStateAdapter ในกรณีของคุณสามารถเป็น RecyclerView.Adapter

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;

import java.util.ArrayList;

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {

    private Context context;
    private ArrayList<String> arrayList = new ArrayList<>();

    public MyAdapter(Context context, ArrayList<String> arrayList) {
        this.context = context;
        this.arrayList = arrayList;
    }

    @NonNull
    @Override
    public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        return new MyViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
        holder.tvName.setText(arrayList.get(position));
    }

    @Override
    public int getItemCount() {
        return arrayList.size();
    }

    public class MyViewHolder extends RecyclerView.ViewHolder {
        TextView tvName;

        public MyViewHolder(@NonNull View itemView) {
            super(itemView);
            tvName = itemView.findViewById(R.id.tvName);
        }
    }
} 

ในกรณีที่คุณใช้ TabLayout คุณสามารถใช้ TabLayoutMediator:

        TabLayoutMediator tabLayoutMediator = new TabLayoutMediator(tabLayout, viewPager, true, new TabLayoutMediator.OnConfigureTabCallback() {
            @Override
            public void onConfigureTab(@NotNull TabLayout.Tab tab, int position) {
                // configure your tab here
                tab.setText(tabs.get(position).getTitle());
            }
        });

        tabLayoutMediator.attach();

จากนั้นคุณจะสามารถรีเฟรชมุมมองของคุณโดยการแก้ไขข้อมูลอะแดปเตอร์ของคุณและเรียกวิธีการ



0

ฉันคิดว่าฉันได้ทำวิธีง่ายๆในการแจ้งการเปลี่ยนแปลงชุดข้อมูล:

ก่อนอื่นให้เปลี่ยนวิธีการทำงานของฟังก์ชั่น instantiateItem:

    @Override
    public Object instantiateItem(final ViewGroup container, final int position) {
        final View rootView = mInflater.inflate(...,container, false);
        rootView.setTag(position);
        updateView(rootView, position);
        container.addView(rootView, LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
        mViewPager.setObjectForPosition(rootView, position);
        return rootView;
    }

สำหรับ "updateView" ให้เติมมุมมองด้วยข้อมูลทั้งหมดที่คุณต้องการเติม (setText, setBitmapImage, ... )

ตรวจสอบว่า destroyView ทำงานเช่นนี้:

    @Override
    public void destroyItem(final ViewGroup container, final int position, final Object obj) {
        final View viewToRemove = (View) obj;
        mViewPager.removeView(viewToRemove);
    }

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

    public void notifyDataSetChanged(final ViewPager viewPager, final NotifyLocation fromPos,
            final NotifyLocation toPos) {
        final int offscreenPageLimit = viewPager.getOffscreenPageLimit();
        final int fromPosInt = fromPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : fromPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        final int toPosInt = toPos == NotifyLocation.CENTER ? mSelectedPhotoIndex
                : toPos == NotifyLocation.MOST_LEFT ? mSelectedPhotoIndex - offscreenPageLimit
                        : mSelectedPhotoIndex + offscreenPageLimit;
        if (fromPosInt <= toPosInt) {
            notifyDataSetChanged();
            for (int i = fromPosInt; i <= toPosInt; ++i) {
                final View pageView = viewPager.findViewWithTag(i);
                mPagerAdapter.updateView(pageView, i);
            }
        }
    }

public enum NotifyLocation {
    MOST_LEFT, CENTER, MOST_RIGHT
}

ตัวอย่างเช่นหากคุณต้องการแจ้งมุมมองทั้งหมดที่แสดงโดย viewPager ว่ามีบางอย่างเปลี่ยนไปคุณสามารถโทร:

notifyDataSetChanged(mViewPager,NotifyLocation.MOST_LEFT,NotifyLocation.MOST_RIGHT);

แค่นั้นแหละ.


0

สำหรับสิ่งที่คุ้มค่าใน KitKat + ดูเหมือนว่าadapter.notifyDataSetChanged()เพียงพอที่จะทำให้มุมมองใหม่ปรากฏขึ้นโดยมีเงื่อนไขว่าคุณsetOffscreenPageLimitสูงพอสมควร viewPager.setOffscreenPageLimit(2)ฉันสามารถที่จะได้รับพฤติกรรมที่ต้องการโดยการทำ


0

ที่จริงผมใช้notifyDataSetChanged()บนViewPagerและCirclePageIndicatorและหลังจากนั้นผมเรียกdestroyDrawingCache()บนViewPagerและทำงาน .. ไม่มีการแก้ปัญหาอื่น ๆ ทำงานให้ฉัน


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