4個fragment實現(xiàn)viewpager2的1-N次翻頁

1.引言

viewpager2是JetPack組件庫推出的組件之一夕膀,是原生viewPager的升級但是不兼容,因為ViewPager2的Adapter 繼承于RecycleView.Adpater烙荷。

ViewPager+Fragment實現(xiàn)無限滾動的方案主要有2種

  • 方案一:將viewpager上限設置成一個很大的數(shù),第一個頁面設置到中間檬寂。然后滑動的時候终抽,用當前的序號與viewpager頁面數(shù)取余得到目標頁面的序號,然后顯示出來桶至。理論上一個人不會無聊到一直左滑或者右滑昼伴。因此可以模擬無限循環(huán)。

  • 方案二:假設viewpager中有四個頁面镣屹,分別為A圃郊、B、C女蜈、D持舆。然后在A左邊添加一個頁面D,在D右邊添加一個頁面A伪窖,變成 D逸寓、A、B覆山、C席覆、D、A汹买。當滑到D時跳轉到D佩伤,滑到A時跳轉到A

本方案使用的是ViewPager2+Fragment 加 方案二 的實現(xiàn)方式。

2. 方案實現(xiàn)

1. 無限的實現(xiàn)

public class ViewActivity extends AppCompatActivity {

    private ViewPager2 vp2;
    private List<Fragment> fragments = new ArrayList<>();
    private List<String> datas = new ArrayList<>();
 


    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view);
        vp2 = (ViewPager2) findViewById(R.id.vp2);

        fragments.add(ItemFragment.newInstance("1"));
        fragments.add(ItemFragment.newInstance("2"));
        fragments.add(ItemFragment.newInstance("3"));
        fragments.add(ItemFragment.newInstance("4"));

        vp2.setOffscreenPageLimit(2);
        vp2.setAdapter(new FragmentPager2Adapter(this, fragments));
        MyOnPageChangeCallback callback = new MyOnPageChangeCallback(vp2);
        vp2.registerOnPageChangeCallback(callback);
    }

}

public class FragmentPager2Adapter extends FragmentStateAdapter {


    private final List<Fragment> fragments;

    public FragmentPager2Adapter(@NonNull FragmentActivity fragmentActivity, List<Fragment> fragments) {
        super(fragmentActivity);
        this.fragments = fragments;
    }

    @NonNull
    @Override
    public Fragment createFragment(int position) {
        return fragments.get(position);
    }

    @Override
    public int getItemCount() {
        return fragments.size();
    }
}
public class MyOnPageChangeCallback1 extends ViewPager2.OnPageChangeCallback {

    private static final String TAG = "MyOnPageChangeCallback";
    private final ViewPager2 vp2;
    private final int fragmentSize;
    private int realPosition = 0;


    public MyOnPageChangeCallback1(ViewPager2 vp2, int fragmentSize) {
        this.vp2 = vp2;
        this.fragmentSize = fragmentSize;
    }


    @Override
    public void onPageScrollStateChanged(int state) {
        super.onPageScrollStateChanged(state);
        if (state == ViewPager2.SCROLL_STATE_IDLE) {
            if (realPosition == (fragmentSize - 1)) {
                Log.i(TAG, "最后一頁:" + 0);
                vp2.setCurrentItem(1, false);
            } else if (realPosition == 0) {
                Log.i(TAG, "第一頁:" + (fragmentSize - 1));
                vp2.setCurrentItem(fragmentSize - 2, false);
            }
         }
     
    }

    @Override
    public void onPageSelected(int position) {
        super.onPageSelected(position);
        realPosition = position;
    }
    
}

2. 如何獲取真實的position和滑動的虛擬position

1. 獲取真實的position簡單
 @Override
    public void onPageSelected(int position) {
        super.onPageSelected(position);
        realPosition = position;
    }
2.難點在于如何滑動的虛擬position

實現(xiàn)思路:根據(jù)滑動方向+ 是否翻過頁 來判斷是向左還是向右翻頁了晦毙。

public class MyOnPageChangeCallback2 extends ViewPager2.OnPageChangeCallback {

    private static final String TAG = "MyOnPageChangeCallback";

    //記錄上一次滑動的positionOffsetPixels值
    private int lastValue = -1;
    // 滑動方向
    private boolean turnToLeft = false;
    private boolean isScrolling = false;
    private int mState;
    private ChangeViewCallback changeViewCallback;
    private int lastPosition = -1;
    private int virtualPosition = 0;



    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        super.onPageScrolled(position, positionOffset, positionOffsetPixels);
        if (isScrolling) {
            if (lastValue > positionOffsetPixels) {
                // 遞減生巡,向右側滑動
                turnToLeft = false;
            } else if (lastValue < positionOffsetPixels) {
                // 遞減,向右側滑動
                turnToLeft = true;
            }
        }
        lastValue = positionOffsetPixels;
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        super.onPageScrollStateChanged(state);
        isScrolling = state == ViewPager.SCROLL_STATE_DRAGGING;
        this.mState = state;
    }

    @Override
    public void onPageSelected(int position) {
        super.onPageSelected(position);
        if (mState == ViewPager2.SCROLL_STATE_SETTLING) {
            Log.i(TAG, "lastPosition:" + lastPosition + ",currentPosition:" + position);
            if (changeViewCallback != null && lastPosition != position) {
                if (turnToLeft) {
                    virtualPosition++;
                }
                if (!turnToLeft) {
                    virtualPosition--;
                }
                changeViewCallback.changeView(turnToLeft, virtualPosition);
                lastPosition = position;
            }
            turnToLeft = false;
        }
    }


    /**
     * 滑動狀態(tài)改變回調
     *
     * @author zxy
     */
    public interface ChangeViewCallback {
        /**
         * 切換視圖 见妒?決定于left和right 孤荣。
         *
         * @param turnToLeft
         * @param virtualPosition
         */
        public void changeView(boolean turnToLeft, int virtualPosition);
    }
    
    public void setChangeViewCallback(ChangeViewCallback callback) {
        changeViewCallback = callback;
    }
}

3. n 滾動 + 獲取滑動的虛擬position

如果實現(xiàn)無限滾動 那虛擬position 就沒有任何意義了,所有限定了從第1頁翻到n頁须揣。

public class MyOnPageChangeCallback extends ViewPager2.OnPageChangeCallback {

    private static final String TAG = "MyOnPageChangeCallback";
    private ViewPager2 vp2;
    // fragment的個數(shù) 4個滑動效果最佳
    private int fragmentSize;
   // 真正數(shù)據(jù)個數(shù) 也就滑動的次數(shù)
    private int dataSize;
    //記錄上一次滑動的positionOffsetPixels值
    private int lastValue = -1;
    // 滑動方向
    private boolean turnToLeft = false;
    private boolean isScrolling = false;
    private ChangeViewCallback changeViewCallback;
    private int lastPosition = -1;
    private int mState;
    private int realPosition = 0;
    private int virtualPosition = 0;


    public MyOnPageChangeCallback(ViewPager2 vp2, int dataSize, int fragmentSize) {
        this.vp2 = vp2;
        this.fragmentSize = fragmentSize;
        this.dataSize = dataSize;
    }


    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        super.onPageScrolled(position, positionOffset, positionOffsetPixels);
        if (isScrolling) {
            if (lastValue > positionOffsetPixels) {
                // 遞減盐股,向右側滑動
                turnToLeft = false;
            } else if (lastValue < positionOffsetPixels) {
                // 遞減,向右側滑動
                turnToLeft = true;
            }
        }
        lastValue = positionOffsetPixels;
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        super.onPageScrollStateChanged(state);
        isScrolling = state == ViewPager.SCROLL_STATE_DRAGGING;
        this.mState = state;
        if (state == ViewPager2.SCROLL_STATE_IDLE) {
           
          // 沒有做無限滾動哦耻卡,只能從 1頁翻到 n頁
           if (virtualPosition <= 0 || virtualPosition >= dataSize - 1) {
               return;
           }
            
            // 小于4 個沒有必要疯汁,4個fragment  翻頁效果最好
            if (fragmentSize < 4) {
                return;
            }
            Log.i(TAG, "realPosition:" + realPosition + ",fragments.size():" + (fragmentSize - 1));
            //此處為你需要的情況,再加入當前頁碼判斷可知道是第一頁還是最后一頁
            if (realPosition == (fragmentSize - 1)) {
                Log.i(TAG, "最后一頁:" + 0);
                vp2.setCurrentItem(1, false);
            } else if (realPosition == 0) {
                Log.i(TAG, "第一頁:" + (fragmentSize - 1));
                vp2.setCurrentItem(fragmentSize - 2, false);
            }
        }
    }

    @Override
    public void onPageSelected(int position) {
        super.onPageSelected(position);
        realPosition = position;
        if (mState == ViewPager2.SCROLL_STATE_SETTLING) {
            Log.i(TAG, "lastPosition:" + lastPosition + ",currentPosition:" + position);
            if (changeViewCallback != null && lastPosition != position) {
                if (turnToLeft) {
                    virtualPosition++;
                }
                if (!turnToLeft) {
                    virtualPosition--;
                }
                changeViewCallback.changeView(turnToLeft, virtualPosition);
                lastPosition = position;
            }
            turnToLeft = false;
        }
    }


    /**
     * 滑動狀態(tài)改變回調
     *
     * @author zxy
     */
    public interface ChangeViewCallback {
        /**
         * 切換視圖 卵酪?決定于left和right 幌蚊。
         *
         * @param turnToLeft
         * @param virtualPosition
         */
        public void changeView(boolean turnToLeft, int virtualPosition);
    }

    /**
     * set ...
     *
     * @param callback
     */
    public void setChangeViewCallback(ChangeViewCallback callback) {
        changeViewCallback = callback;
    }
}

public class ViewActivity extends AppCompatActivity {

    private ViewPager2 vp2;
    private List<Fragment> fragments = new ArrayList<>();
    private List<String> datas = new ArrayList<>();



    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_view);
        vp2 = (ViewPager2) findViewById(R.id.vp2);
        for (int i = 0; i < 30; i++) {
            datas.add("數(shù)據(jù):" + (i+1));
        }

        fragments.add(ItemFragment.newInstance("1"));
        fragments.add(ItemFragment.newInstance("2"));
        fragments.add(ItemFragment.newInstance("3"));
        fragments.add(ItemFragment.newInstance("4"));
        vp2.setOffscreenPageLimit(2);
        vp2.setAdapter(new FragmentPager2Adapter(this, fragments));
        MyOnPageChangeCallback callback = new MyOnPageChangeCallback(vp2, 30, 4);
        callback.setChangeViewCallback((turnToLeft, virtualPosition) -> {
          
            // datas 通過virtualPosition   拿到對于的數(shù)據(jù) 再傳給 fragment
            String bean = datas.get(virtualPosition);

            for (Fragment fragment : fragments) {
                ItemFragment fragment1 = (ItemFragment) fragment;
                fragment1.setData(bean);
            }
        });
        vp2.registerOnPageChangeCallback(callback);

    }
}

3. 其他

在實際開發(fā)過程中遇見過ViewPager2+Fragment使用FragmentStateAdapter時谤碳, Fragment數(shù)量變動,需要刷新的時候 notifyDataSetChanged()無效溢豆⊙鸭颍看了下FragmentStateAdapter源碼研究后找到解決之道。
需要重寫

@Override
public long getItemId(int position) {
     
}
@Override
public boolean containsItem(long itemId) {
   
}

其源碼中 getItemId 使用的是position漩仙,fragment復用導致數(shù)據(jù)錯亂搓茬。可以給fragment綁定一個唯一Id队他。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末卷仑,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子漱挎,更是在濱河造成了極大的恐慌系枪,老刑警劉巖,帶你破解...
    沈念sama閱讀 221,198評論 6 514
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件磕谅,死亡現(xiàn)場離奇詭異私爷,居然都是意外死亡,警方通過查閱死者的電腦和手機膊夹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,334評論 3 398
  • 文/潘曉璐 我一進店門衬浑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人放刨,你說我怎么就攤上這事工秩。” “怎么了进统?”我有些...
    開封第一講書人閱讀 167,643評論 0 360
  • 文/不壞的土叔 我叫張陵助币,是天一觀的道長。 經(jīng)常有香客問我螟碎,道長眉菱,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,495評論 1 296
  • 正文 為了忘掉前任掉分,我火速辦了婚禮俭缓,結果婚禮上,老公的妹妹穿的比我還像新娘酥郭。我一直安慰自己华坦,他們只是感情好,可當我...
    茶點故事閱讀 68,502評論 6 397
  • 文/花漫 我一把揭開白布不从。 她就那樣靜靜地躺著惜姐,像睡著了一般。 火紅的嫁衣襯著肌膚如雪消返。 梳的紋絲不亂的頭發(fā)上载弄,一...
    開封第一講書人閱讀 52,156評論 1 308
  • 那天耘拇,我揣著相機與錄音撵颊,去河邊找鬼宇攻。 笑死,一個胖子當著我的面吹牛倡勇,可吹牛的內容都是我干的逞刷。 我是一名探鬼主播,決...
    沈念sama閱讀 40,743評論 3 421
  • 文/蒼蘭香墨 我猛地睜開眼妻熊,長吁一口氣:“原來是場噩夢啊……” “哼夸浅!你這毒婦竟也來了?” 一聲冷哼從身側響起扔役,我...
    開封第一講書人閱讀 39,659評論 0 276
  • 序言:老撾萬榮一對情侶失蹤帆喇,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后亿胸,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體坯钦,經(jīng)...
    沈念sama閱讀 46,200評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 38,282評論 3 340
  • 正文 我和宋清朗相戀三年侈玄,在試婚紗的時候發(fā)現(xiàn)自己被綠了婉刀。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,424評論 1 352
  • 序言:一個原本活蹦亂跳的男人離奇死亡序仙,死狀恐怖突颊,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情潘悼,我是刑警寧澤律秃,帶...
    沈念sama閱讀 36,107評論 5 349
  • 正文 年R本政府宣布,位于F島的核電站治唤,受9級特大地震影響棒动,放射性物質發(fā)生泄漏。R本人自食惡果不足惜肝劲,卻給世界環(huán)境...
    茶點故事閱讀 41,789評論 3 333
  • 文/蒙蒙 一迁客、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辞槐,春花似錦掷漱、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,264評論 0 23
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至鹿榜,卻和暖如春海雪,著一層夾襖步出監(jiān)牢的瞬間锦爵,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,390評論 1 271
  • 我被黑心中介騙來泰國打工奥裸, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留险掀,地道東北人。 一個月前我還...
    沈念sama閱讀 48,798評論 3 376
  • 正文 我出身青樓湾宙,卻偏偏與公主長得像樟氢,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子侠鳄,可洞房花燭夜當晚...
    茶點故事閱讀 45,435評論 2 359

推薦閱讀更多精彩內容