ViewPager.setOffscreenPageLimit (0) ไม่ทำงานตามที่คาดไว้


100

ชิ้นส่วนที่ฉันใช้ในViewPagerอินสแตนซ์ของฉันค่อนข้างใช้ทรัพยากรมากดังนั้นฉันจึงต้องการโหลดทีละชิ้นเท่านั้น เมื่อฉันลองทำสิ่งต่อไปนี้:

mViewPager.setOffscreenPageLimit(0);
mViewPager.setAdapter(mPagerAdapter);

ฉันFragmentStatePagerAdapter.getItem(int position)ฟังก์ชั่นการแทนที่เรียกว่า 3 mViewPager.setOffscreenPageLimit(1)ครั้งซึ่งเป็นสิ่งที่เกิดขึ้นเมื่อผมเรียก ฉันคาดว่ามันจะถูกเรียกเพียงครั้งเดียวเพราะฉันระบุ 0 หน้านอกหน้าจอ

ฉันเชื่อว่าฉันโทรทุกอย่างถูกต้องเพราะถ้าผมเรียกmViewPager.setOffscreenPageLimit(2), FragmentStatePagerAdapter.getItem(int position)เรียกว่า 5 ครั้งเท่าที่ผมจะคาดหวัง

ViewPager ต้องการเพจออฟสกรีนอย่างน้อย 1 เพจหรือว่าฉันทำอะไรผิดที่นี่?


9
ฉันได้เพิ่มคำขอคุณสมบัติ: code.google.com/p/android/issues/detail?id=56667
ทำเครื่องหมาย


ฉันมีเพียง 3 แท็บและการตั้งค่าที่ViewPager.setOffscreenPageLimit(2)ใช้ได้สำหรับฉัน ลองViewPager.setOffscreenPageLimit(totTabs - 1)
sibin

คำตอบ:


112

ViewPager ต้องการเพจออฟสกรีนอย่างน้อย 1 เพจหรือไม่

ใช่. หากฉันอ่านซอร์สโค้ดอย่างถูกต้องคุณควรได้รับคำเตือนเกี่ยวกับสิ่งนี้ใน LogCat เช่น:

Requested offscreen page limit 0 too small; defaulting to 1

6
นี่ไม่ใช่วิธีแก้ปัญหา! ฉันต้องการโหลดเพียง 1 ส่วนไม่ใช่ 2 แน่นอนว่าเป็นไปได้อย่างไร?
HGPB

14
@Haraldo: "นี่เป็นไปได้อย่างแน่นอน" - ViewPagerไม่ได้กับ ViewPagerสร้างส่วนเหล่านี้เพื่อให้มุมมองมีอยู่ดังนั้นผู้ใช้จึงสามารถปัดไปมาระหว่างพวกเขาได้โดยเอฟเฟกต์ภาพเคลื่อนไหวที่แสดงมุมมองเก่าจะเลื่อนออกจากหน้าจอและมุมมองใหม่จะเลื่อนไปที่หน้าจอ คุณสามารถลองเขียนของคุณเองViewPagerที่สามารถปัดระหว่างสิ่งที่ไม่มีอยู่จริง คุณสามารถอ่านเพิ่มเติมเกี่ยวกับเรื่องนี้ได้ที่code.google.com/p/android/issues/detail?id=56667#c3
CommonsWare

2
ใช่ฉันได้อ่านปัญหานี้แล้ว แค่ไม่สมเหตุสมผล ฉันคาดว่าจะสามารถsetOffscreenPageLimit(0)เป็น 0 จากนั้นฉันจะจัดการกับผลที่ตามมาได้ด้วยตัวเอง แฟรกเมนต์ว่างเริ่มต้นอาจเป็นตัวยึดได้เช่นจนกว่าจะโหลดแฟรกเมนต์ ซึ่งทั้งหมดนี้อาจเกิดขึ้นจากเธรด UI อย่างไรก็ตามฉันไม่ได้คิดเกี่ยวกับเรื่องนี้มากนัก
HGPB

5
@Haraldo: "แฟรกเมนต์ว่างเริ่มต้นอาจเป็นตัวยึดได้เช่นจนกว่าชิ้นส่วนจะโหลด" - คุณสามารถมีตัวยึดตำแหน่งในแฟรกเมนต์ของคุณได้ในวันนี้ด้วยการใช้งานที่มีอยู่ViewPagerซึ่งคุณจะแทนที่ด้วยเนื้อหาจริงเมื่อพร้อมใช้งาน .
CommonsWare

2
onPageChangeListener ดูเหมือนจะเป็นวิธีแก้ปัญหา
alicanbatur

123

วิธีที่ดีที่สุดที่ฉันพบคือsetUserVisibleHint
เพิ่มสิ่งนี้ในส่วนของคุณ

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) {
        // load data here
    }else{
       // fragment is no longer visible
    }
}

1
+++++ สำหรับวิธีนี้ !!! ขอบคุณหลังจากเพิ่มรหัสนี้ฉันได้รับ NPL เป็นครั้งแรกฉันเพิ่งเพิ่ม try catch block และมันใช้งานได้ดีสำหรับฉัน !!!
Bhavin Chauhan

1
ในการใช้สิ่งนี้จะไม่โหลดวันที่บน secondViewPager Fragment ในส่วนที่สองจะโหลดข้อมูลของส่วนที่สาม ใครสามารถช่วยฉันออก?
Arshad

ฉันขาดอะไรไปหรือเปล่า setUserVisibleHint เรียกก่อน onCreateView
Anton Makov

1
ทางออกที่ดีที่สุดตอนนี้เลิกใช้แล้ว
M. Sameer

@ M.Sameer ทางเลือกอื่นสำหรับ setUserVisibleHint มีการแก้ไขหรือไม่
ยุดีกรรม

8

คุณสามารถลองทำดังนี้

public abstract class LazyFragment extends Fragment {
    protected boolean isVisible;
    /**
     * 在这里实现Fragment数据的缓加载.
     * @param isVisibleToUser
     */
    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if(getUserVisibleHint()) {
            isVisible = true;
            onVisible();
        } else {
            isVisible = false;
            onInvisible();
        }
    }
    protected void onVisible(){
        lazyLoad();
    }
    protected abstract void lazyLoad();
    protected void onInvisible(){}

protected abstract void lazyLoad();
protected void onInvisible(){}

คำตอบที่ดีที่สุด!
Radesh

7

เพิ่มครั้งแรก

   boolean isFragmentLoaded = false;

กว่า

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser && !isFragmentLoaded) {
        //Load Your Data Here like.... new GetContacts().execute();

        isFragmentLoaded = true;
    }
else{
     }
}

7
แต่วิธีนี้เรียกก่อน onCreateView
AndroidGeek

2

ViewPager เป็นค่าเริ่มต้นในการโหลดหน้าถัดไป (Fragment) ซึ่งคุณไม่สามารถเปลี่ยนแปลงได้โดย setOffscreenPageLimit (0) แต่คุณสามารถทำบางอย่างเพื่อแฮ็กได้ คุณสามารถใช้ฟังก์ชัน onPageSelected ในกิจกรรมที่มี ViewPager ใน Fragment ถัดไป (ซึ่งคุณไม่ต้องการโหลด) คุณเขียนฟังก์ชันสมมติว่า showViewContent () ที่คุณใส่โค้ดเริ่มต้นที่ใช้ทรัพยากรทั้งหมดและไม่ต้องทำอะไรก่อนเมธอด onResume () จากนั้นเรียกใช้ฟังก์ชัน showViewContent () ภายใน onPageSelected หวังว่านี่จะช่วยได้


1

นี่อาจเป็นกระทู้เก่า แต่ดูเหมือนว่าจะใช้ได้สำหรับฉัน แทนที่ฟังก์ชันนี้:

@Override
public void setMenuVisibility(boolean menuVisible) { 
    super.setMenuVisibility(menuVisible);

    if ( menuVisible ) {
        /**
         * Load your stuffs here.
         */
    } else  { 
        /**
         * Fragment not currently Visible.
         */
    } 
}

โครตมีความสุข ...


เว็บวิวคืออะไร ฉันขอแนะนำให้คุณสร้างเธรดใหม่สำหรับสิ่งนั้น
ralphgabb

1

ในกรณีของฉันฉันต้องการเริ่มภาพเคลื่อนไหวในมุมมอง แต่ด้วย setUserVisibleHint มีปัญหาบางอย่าง ...
วิธีแก้ปัญหาของฉันคือ:

1 / addOnPageChangeListener สำหรับอะแดปเตอร์ของคุณ:

mViewPager.addOnPageChangeListener(this);

2 / ใช้ OnPageChangeListener:

public class PagesFragment extends Fragment implements ViewPager.OnPageChangeListener

3 / แทนที่ 3 วิธี:

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

}

@Override
public void onPageSelected(int position)
{ 
}

@Override
public void onPageScrollStateChanged(int state)
{
}

4 / ประกาศและเริ่มต้นตัวแปรนี้ในชั้นเรียนของคุณ

private static int mTabState = 1;

ข้อสังเกต : ฉันมีสามส่วนในอะแด็ปเตอร์ของฉันและใช้ mTabState สำหรับ setCurrentItem และตำแหน่งปัจจุบันของอะแดปเตอร์ซึ่งรับรู้ว่าส่วนใดที่แสดงต่อผู้ใช้ในเวลา ... 5 / ในวิธี onPageSelected เพิ่มรหัสนี้:

if (mTabState == 0 || position == 0)
    {
        Intent intent = new Intent("animation");
        intent.putExtra("current_position", position);
        LocalBroadcastManager.getInstance(mContext).sendBroadcast(intent);
    }

หากหน้าก่อนหน้าหรือหน้าปัจจุบันคือหน้า 0 (ส่วนในตำแหน่ง 0) ให้ทำสิ่งนี้

6 / ตอนนี้ในคลาสแฟรกเมนต์ของคุณ (แฟรกเมนต์ในตำแหน่ง 0 ของอะแด็ปเตอร์) คุณต้องสร้างตัวรับสัญญาณออกอากาศและลงทะเบียนในเมธอด onResume และยกเลิกการลงทะเบียนใน

BroadcastReceiver broadcastReceiver = new BroadcastReceiver()
{
    @Override
    public void onReceive(Context context, Intent intent)
    {
        if (Objects.equals(intent.getAction(), "animation"))
        {
            int currentPosition = intent.getIntExtra("current_position", 0);
            if (currentPosition == 0)
            {
                startAnimation();
                setViewsVisible();
            } else
            {
                setViewsInvisible();
            }
        }
    }
};

@Override
public void onResume()
{
    super.onResume();
    LocalBroadcastManager.getInstance(mContext).registerReceiver(broadcastReceiver, new IntentFilter("animation"));
}

@Override
public void onPause()
{
    super.onPause();
    LocalBroadcastManager.getInstance(mContext).unregisterReceiver(broadcastReceiver);
}

สรุป:ฉันมีแม่มด Fragment Pager Adapter แสดง Three Fragments อยู่ในนั้นฉันต้องการแสดงภาพเคลื่อนไหวบางส่วนบน Views ใน Fragment ในตำแหน่ง 0 ของ Adapter สำหรับสิ่งนี้ฉันใช้ BroadcastReceiver เมื่อ Fragment ถูกเลือกฉันเริ่มเมธอด Animation และแสดง Views ให้กับ User เมื่อ Fragment ไม่แสดงต่อผู้ใช้ฉันพยายามที่จะมองไม่เห็น ...


1

โปรดลองใช้รหัสนี้เพื่อแก้ไขปัญหาการรีเฟรชมุมมองใน Viewpager ....

/* DO NOT FORGET! The ViewPager requires at least “1” minimum OffscreenPageLimit */
int limit = (mAdapter.getCount() > 1 ? mAdapter.getCount() - 1 : 1);
mViewPager.setOffscreenPageLimit(limit);

0

สำหรับฟังก์ชัน "instantiateItem" ให้เตรียมแฟรกเมนต์ แต่อย่าโหลดเนื้อหาหนัก ๆ

ใช้ "onPageChangeListener" เพื่อให้ทุกครั้งที่คุณไปที่หน้าใดหน้าหนึ่งคุณจะโหลดเนื้อหาจำนวนมากและแสดง


0

ฉันมีปัญหาเดียวกัน ฉันพบโค้ดที่มีประโยชน์ในไซต์นี้และทำการแปลง

int ขั้นต่ำสำหรับ mViewPager.setOffscreenPageLimit (... ); คือ 1 ดังนั้นแม้ว่าคุณจะเปลี่ยนเป็น 0 คุณก็ยังโหลดได้ 2 หน้า

สิ่งแรกที่ต้องทำคือการสร้าง int คงที่เราจะเรียกว่า maxPageCount และแทนที่เมธอด FragmentStatePagerAdapter getCount () เพื่อส่งคืน maxPageCount:

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

สร้างวิธีการแบบคงที่ซึ่งสามารถเข้าถึงได้จากที่ใดก็ได้ในโปรแกรมที่จะช่วยให้คุณเปลี่ยน maxCount นี้:

public static void addPage(){
    maxPageCount++; //or maxPageCount = fragmentPosition+2
    mFragmentStatePagerAdapter.notifyDataSetChanged(); //notifyDataSetChanged is important here.
}

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

หวังว่ามันจะช่วยใครสักคน


0

ใช้สิ่งนี้ // สร้างบูลีนเพื่อดึงข้อมูลบูลีนส่วนตัว isViewShown = false;

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (getView() != null) {
        isViewShown = true;
        // fetchdata() contains logic to show data when page is selected mostly asynctask to fill the data
        fetchData();
    } else {
        isViewShown = false;
    }
} 
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.