欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Android?ViewPager你可能不知道的刷新操作分享

 更新時間:2023年05月18日 08:31:04   作者:newki  
這篇文章主要為大家詳細介紹了Android中ViewPager你可能不知道的刷新操作,文中的示例代碼講解詳細,具有一定的學(xué)習(xí)價值,需要的可以參考一下

前言

哎呀,這個我會。不就是 mViewPagerAdapter.notifyDataSetChanged(); 嘛,簡單!

這個可能真不是那么簡單,我們以常用的 ViewPager + Fragment 的使用為例。你調(diào)用 notifyDataSetChanged 刷新方法,會走到 getItemPosition 方法中查詢當前Item是否需要刷新,而它的默認實現(xiàn)是:

    public int getItemPosition(@NonNull Object object) {
        return POSITION_UNCHANGED;
    }

永遠標記不刷新,那么不管你是添加Pager,刪除Pager,改變Pager都是不生效的。

那有些同學(xué)就會說了,每次刷新還要做差分?我直接一把梭,直接重新設(shè)置一個 Adapter 不就萬事大吉了?

反正每次接口數(shù)據(jù)回來都重新設(shè)置 Adapter ,還管什么性能不性能,效果實現(xiàn)了再說!

    mViewPager.setAdapter(null);
    mViewPagerAdapter = new ViewPagerAdapter(getChildFragmentManager(),mFragmentList);
    mViewPager.setAdapter(mViewPagerAdapter);
    mViewPager.setOffscreenPageLimit(mFragmentList.size() - 1);

但是就算如此也是有問題的,當我們一個頁面中根據(jù)不同的篩選條件,服務(wù)端返回不同數(shù)量的數(shù)組,我們就要展示不同數(shù)量的 ViewPager 如果這樣刷新 ViewPager 就可能出現(xiàn)顯示問題。

怎么解決?幾種方案,接下來往下看:

一、清緩存重置Adapter的方案

如果除開性能問題,想直接每次直接替換一個 Adapter 其實也是可行的,如果替換之后顯示的還是之前的頁面,或者顯示的索引不對,大概率是 ViewPager 之前緩存的 Fragment 沒有清掉的。

所以我們需要自定義一個 Adapter , 在里面定義清除緩存的方法,每次設(shè)置 Adapter 之前就調(diào)用清除緩存之后再設(shè)置 Adapter 。

直接上代碼:

/**
 *  可以清除緩存的ViewPager
 */
public class ViewPagerClearAdapter extends FragmentPagerAdapter {
    private List<Fragment> mFragments;
    private FragmentTransaction mCurTransaction;
    private FragmentManager mFragmentManger;
    public ViewPagerClearAdapter(FragmentManager fragmentManager, List<Fragment> fragments) {
        this(fragmentManager, fragments, 0);
    }
    public ViewPagerClearAdapter(FragmentManager fragmentManager, List<Fragment> fragments, int behavor) {
        super(fragmentManager, behavor);
        mFragments = fragments;
        mFragmentManger = fragmentManager;
    }
    @Override
    public Fragment getItem(int position) {
        return mFragments.get(position);
    }
    @Override
    public int getCount() {
        return mFragments.size() == 0 ? 0 : mFragments.size();
    }
    /**
     * 清除緩存fragment
     *
     * @param container ViewPager
     */
    public void clear(ViewGroup container) {
        if (this.mCurTransaction == null) {
            this.mCurTransaction = mFragmentManger.beginTransaction();
        }
        for (int i = 0; i < mFragments.size(); i++) {
            long itemId = this.getItemId(i);
            String name = makeFragmentName(container.getId(), itemId);
            Fragment fragment = mFragmentManger.findFragmentByTag(name);
            if (fragment != null) {//根據(jù)對應(yīng)的ID,找到fragment,刪除
                mCurTransaction.remove(fragment);
            }
        }
        mCurTransaction.commitNowAllowingStateLoss();
    }
    /**
     * 等同于FragmentPagerAdapter的makeFragmentName方法,
     */
    private static String makeFragmentName(int viewId, long id) {
        return "android:switcher:" + viewId + ":" + id;
    }

使用的時候,先清除再設(shè)置即可:

       if (mViewPagerAdapter!=null){
            mViewPagerAdapter.clear(mViewPager);
        }
        mViewPager.setAdapter(null);
        mViewPagerAdapter = new ViewPagerClearAdapter(getChildFragmentManager(),mFragmentList);
        mViewPager.setAdapter(mViewPagerAdapter);
        if (mFragmentList.size() > 1) {
            mViewPager.setOffscreenPageLimit(mFragmentList.size() - 1);
        }

這樣也算是間接的實現(xiàn)了刷新功能,但是有點傻,RecyclerView 感覺到暴怒,那么有沒有類似 RecyclerView 那樣的智能刷新呢?

二、TabView+ViewPager的差分刷新

前言中我們說到 ViewPager 的 notifyDataSetChanged 刷新方法,會走到 getItemPosition 方法,而內(nèi)部的默認實現(xiàn)是不做刷新。

而重點的 getItemPosition 其實就是在 notifyDataSetChanged 執(zhí)行的時候拿到當前的 Item 集合做的遍歷操作,讓每一個 Item 都去自行判斷你有沒有變化。

那么難點就是如何判斷當前的對象或索引位置有沒有變化呢?

2.1 使用arguments的方式

在 ViewPagerAdapter 中我們可以重寫方法 instantiateItem 表示每次創(chuàng)建 Fragment 的時候執(zhí)行,創(chuàng)建一個 Fragment 對象。

由于內(nèi)部默認實現(xiàn)并沒有添加 Tag ,所以我們可以通過調(diào)用 super的方式拿到 fragment 對象,給他設(shè)置一個參數(shù),并記錄每一個 Fragment 對應(yīng)的索引位置。

然后我們再判斷 Fragment 是否需要刷新的時候,拿到對應(yīng)的參數(shù),并獲取當前 Fragment 的索引,判斷Fragment有沒有變化,索引有沒有變化。

當都沒有變化,說明此 Fragment 無需刷新,就返回 POSITION_UNCHANGED ,如果要刷新就返回 POSITION_NONE 。

如果返回 POSITION_NONE ,就會走到 destroyItem 的回調(diào),會銷毀 Framgent,如果有需要會重新創(chuàng)建新的 Fragment 。

完整的代碼實現(xiàn)如下:

class ViewPagerFragmentAdapter @JvmOverloads constructor(
    private val fragmentManager: FragmentManager,
    private val fragments: List<Fragment>,
    private val pageTitles: List<String>? = null,
    behavor: Int = 0
) : FragmentStatePagerAdapter(fragmentManager, behavor) {
    private val fragmentMap = mutableMapOf<Int, Fragment>()
    private val fragmentPositions = hashMapOf<Int, Int>()
    init {
        for ((index, fragment) in fragments.withIndex()) {
            fragmentMap[index] = fragment
        }
    }
    override fun getItem(position: Int): Fragment {
        return fragments[position]
    }
    override fun getCount(): Int {
        return if (fragments.isEmpty()) 0 else fragments.size
    }
    override fun getPageTitle(position: Int): CharSequence? {
        return if (pageTitles == null) "" else pageTitles[position]
    }
    override fun instantiateItem(container: ViewGroup, position: Int): Any {
        YYLogUtils.w("ViewPagerFragmentAdapter-instantiateItem")
        val fragment = super.instantiateItem(container, position) as Fragment
        val id = generateUniqueId()
        var args = fragment.arguments
        if (args == null) {
            args = Bundle()
        }
        args.putInt("_uuid", id)
        fragment.arguments = args
        // 存儲 Fragment 的位置信息
        fragmentPositions[id] = position
        return fragment
    }
    private fun generateUniqueId(): Int {
        // 生成唯一的 ID
        return UUID.randomUUID().hashCode()
    }
    override fun destroyItem(container: ViewGroup, position: Int, obj: Any) {
        super.destroyItem(container, position, obj)
    }
    override fun getItemPosition(obj: Any): Int {
        YYLogUtils.w("ViewPagerFragmentAdapter-getItemPosition")
        val fragment = obj as Fragment
        // 從 Fragment 中獲取唯一的 ID
        val args = fragment.arguments
        if (args != null && args.containsKey("_uuid")) {
            val id = args.getInt("_uuid")
            // 根據(jù) ID 獲取 Fragment 在 Adapter 中的位置
            val position = fragmentPositions[id]
            return if (position != null && position == fragments.indexOf(fragment)) {
                // Fragment 未發(fā)生變化,返回 POSITION_UNCHANGED
                POSITION_UNCHANGED
            } else {
                // Fragment 發(fā)生變化,返回 POSITION_NONE
                POSITION_NONE
            }
        }
        // 如果不是 Fragment,則返回默認值
        return super.getItemPosition(obj)
    }
}

使用起來很簡單,我們這里使用默認的TabView + ViewPager + 懶加載Fragment來看看效果:

    val fragments = mutableListOf(LazyLoad1Fragment.obtainFragment(), LazyLoad2Fragment.obtainFragment(), LazyLoad3Fragment.obtainFragment());
    val titles = mutableListOf("Demo1", "Demo2", "Demo3");
    val adapter = ViewPagerFragmentAdapter(supportFragmentManager, fragments, titles)
    override fun init() {
        //默認的添加數(shù)據(jù)適配器
        mBinding.viewPager.adapter = adapter
        mBinding.viewPager.offscreenPageLimit = fragments.size - 1
        mBinding.tabLayout.setupWithViewPager(mBinding.viewPager)
    }

我們這里使用的是原始懶加載的方案,關(guān)于每一種懶加載Fragment的使用可以看我之前的文章:Fragment懶加載的幾種方式與性能對比。

我們再標題欄加一個測試的按鈕,查看增刪改的功能是否能行?

        mBinding.easyTitle.addRightText("刷新") {
            //添加并刷新
//            fragments.add(LazyLoad1Fragment.obtainFragment())
//            titles.add("Demo4")
            //更新指定位置并刷新
//            fragments[2] = LazyLoad2Fragment.obtainFragment()
//            titles[2] = "Refresh1"
            //反轉(zhuǎn)換位置呢
//            fragments.reverse()
//            titles.reverse()
            //刪除并刷新
            fragments.removeAt(2)
            titles.removeAt(2)
            mBinding.viewPager.adapter?.notifyDataSetChanged()
            mBinding.viewPager.offscreenPageLimit = fragments.size - 1
        }

添加的效果:

指定位置替換Fragment效果:

反轉(zhuǎn)集合,應(yīng)該是第一個和第三個Fragment需要重載:

刪除指定的數(shù)據(jù):

2.2 使用Tag的方式

而使用 Tag 的方式替換其實是類似的道理,需要在創(chuàng)建 Fragment 的時候綁定 tag ,在查詢是否需要刷新的方法中需要拿到tag進行判斷:

        Fragment fragment = getItem(position);
        FragmentTransaction ft = ((FragmentActivity) mContext).getSupportFragmentManager().beginTransaction();
        ft.add(R.id.viewpager, fragment, "fragment" + position);
        ft.attach(fragment);
        ft.commit();
    @Override
    public int getItemPosition(@NonNull Object object) {
        if (object instanceof Fragment) {
            Fragment fragment = (Fragment) object;
            Integer position = fragmentMap.get(fragment.getTag());
            if (position != null && position == fragments.indexOf(fragment)) {
                // Fragment 未發(fā)生變化,返回 POSITION_UNCHANGED
                return POSITION_UNCHANGED;
            } else {
                // Fragment 發(fā)生變化,返回 POSITION_NONE
                return POSITION_NONE;
            }
        }
        // 如果不是 Fragment,則返回默認值
        return super.getItemPosition(object);
    }

這里就不做過多的介紹,如果是簡單的操作也是是可行的。只是需要重寫創(chuàng)建Fragment流程。

由于我自用的并不是 Tag 的方式,因為并不想修改內(nèi)部的創(chuàng)建 Fragment 方式,畢竟內(nèi)部還涉及到 SavedState 與 BEHAVIOR 的一些處理。

如果你感興趣可以自行實現(xiàn)!

三、自定義Tab或第三方Tab

如果我們用到一些自定義Tab的樣式,或者使用一些第三方的TabLayout,那么我們該如何做?

CustomTabView 還能綁定到 ViewPager 嗎?如果要做刷新又該如何操作?

例如我們使用自定義的Tab樣式:

  override fun init() {
        titles.forEach {
             addTab(it)
        }
        mBinding.viewPager.adapter = adapter
        mBinding.viewPager.offscreenPageLimit = fragments.size - 1
        //自定義Tab不能這么設(shè)置了?
        mBinding.tabLayout.setupWithViewPager(mBinding.viewPager)
  }
    private fun addTab(content: String) {
        val tab: TabLayout.Tab = mBinding.tabLayout.newTab()
        val view: View = layoutInflater.inflate(R.layout.tab_custom_layout, null)
        tab.customView = view
        val textView = view.findViewById<TextView>(R.id.tab_text)
        textView.text = content
        mBinding.tabLayout.addTab(tab)
    }

是可以運行,但是不能用 setupWithViewPager 方式,如果用這種方式會默認給設(shè)置原生默認的 TabView 。而沒有自定義 View 效果。

所以我們一般都是手動的監(jiān)聽實現(xiàn)效果:

        //自定義Tab不能這么設(shè)置了?
//        mBinding.tabLayout.setupWithViewPager(mBinding.viewPager)
       // 需要手動的寫監(jiān)聽綁定
        mBinding.viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            override fun onPageScrolled(i: Int, v: Float, i1: Int) {}
            override fun onPageSelected(i: Int) {
                mBinding.tabLayout.setScrollPosition(i, 0f, true)
            }
            override fun onPageScrollStateChanged(i: Int) {}
        })
        mBinding.tabLayout.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
            override fun onTabSelected(tab: TabLayout.Tab) {
                val position = tab.position
                mBinding.viewPager.setCurrentItem(position, true)
            }
            override fun onTabUnselected(tab: TabLayout.Tab) {}
            override fun onTabReselected(tab: TabLayout.Tab) {}
        })

效果:

那么增刪改的操作又有什么區(qū)別呢?

   mBinding.easyTitle.addRightText("Refresh") {
            //添加并刷新
            fragments.add(LazyLoad1Fragment.obtainFragment())
            titles.add("Demo4")
            addTab("Demo4")
            //刪除并刷新
            fragments.removeAt(2)
            titles.removeAt(2)
            mBinding.tabLayout.removeTabAt(2)
            mBinding.viewPager.adapter?.notifyDataSetChanged()
            mBinding.viewPager.offscreenPageLimit = fragments.size - 1
        }

由于沒有 setupWithViewPager 的方式綁定,所以當ViewPager變化之后我們需要手動的自己處理Tab相關(guān)的賦值與刪除等操作:

否則會出現(xiàn),ViewPager刷新了,但TabView不會刷新的問題:

自行處理Tab之后的效果:

不管是第三方的TabLayout,還是自定義的TabView,相比原生默認的 TabView 使用操作還是要復(fù)雜上一點。

四、ViewPager2的區(qū)別

而TabView + ViewPager2 + 懶加載Fragment 就更簡單啦,都是基于RV實現(xiàn)的,我們可以直接調(diào)用RV的刷新方法。

  override fun init() {
        mBinding.viewPager2.bindFragment(
            supportFragmentManager,
            this.lifecycle,
            fragments,
        )
        TabLayoutMediator(mBinding.tabLayout, mBinding.viewPager2) { tab, position ->
            //回調(diào)
            tab.text = titles[position]
        }.attach()
    }

內(nèi)部數(shù)據(jù)適配器的Adapter:

/**
 * 給ViewPager2綁定Fragment
 */
fun ViewPager2.bindFragment(
    fm: FragmentManager,
    lifecycle: Lifecycle,
    fragments: List<Fragment>
): ViewPager2 {
    offscreenPageLimit = fragments.size - 1
    adapter = object : FragmentStateAdapter(fm, lifecycle) {
        override fun getItemCount(): Int = fragments.size
        override fun createFragment(position: Int): Fragment = fragments[position]
    }
    return this
}

后面我們給它加上一些操作方法:

        mBinding.easyTitle.addRightText("Refresh2") {
            //添加并刷新
//            titles.add("Demo4")
//            fragments.add(Lazy2Fragment1.obtainFragment())
//            mBinding.viewPager2.adapter?.notifyItemInserted(fragments.size-1)
            //更新指定位置并刷新
//            fragments[1] = Lazy2Fragment1.obtainFragment()
//            titles[1] = "Refresh2"
//            mBinding.viewPager2.adapter?.notifyItemChanged(1)
            //刪除并刷新
            fragments.removeAt(2)
            mBinding.viewPager2.adapter?.notifyItemRemoved(2)
            mBinding.viewPager2.adapter?.notifyItemRangeChanged(2, 1)
        }

可以看到我們是直接使用RV的Apdater來操作的,也就不需要魔改一些 Adapter 之類的代碼。

可以看到一些效果如下:

真是簡單又方便!

話是這么說,但是感覺 ViewPager2 的風(fēng)評并不是很好的樣子,很多伙伴反饋有一些小問題??偸遣仍诳由?,不知道大家都是怎么選擇的呢?

總結(jié)

本文中我們可以回顧一下ViewPager的用法,F(xiàn)ragment的懶加載用法,重要的是可變 ViewPager 的情況下如何操作。

那么在實際開發(fā)的過程中,我們其實可以區(qū)分場景,如果是靜態(tài)的ViewPager,數(shù)量不可變的,可以直接用簡單的數(shù)據(jù)適配器來實現(xiàn),而如果是可變的ViewPager,大家可以區(qū)分三種情況來使用,都是可行的。

我個人來說之前都是用清除緩存的方式,后來用的是修改 Fragment 的 argument 的方式做的。

如果大家有類似的使用場景,其實按需選擇即可,也不知道大家平時都是怎么做的,如果有更好的方案可以交流一下哦。

到此這篇關(guān)于Android ViewPager你可能不知道的刷新操作分享的文章就介紹到這了,更多相關(guān)Android ViewPager刷新內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 詳解android項目由Gradle 2.2 切換到 3.0的坑

    詳解android項目由Gradle 2.2 切換到 3.0的坑

    本篇文章主要介紹了詳解android項目由Gradle 2.2 切換到 3.0的坑,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-02-02
  • 詳解Android中AIDL的使用

    詳解Android中AIDL的使用

    AIDL是Android Interface definition language的縮寫,對于小白來說,AIDL的作用是讓你可以在自己的APP里綁定一個其他APP的service,這樣你的APP可以和其他APP交互,接下來通過本文給大家分享Android AIDL使用,需要的朋友參考下吧
    2021-07-07
  • 創(chuàng)建Android庫的方法及Android .aar文件用法小結(jié)

    創(chuàng)建Android庫的方法及Android .aar文件用法小結(jié)

    本文給大家介紹了創(chuàng)建Android庫的方法及Android中 .aar文件生成方法與用法詳解,涉及到創(chuàng)建庫模塊操作步驟及開發(fā)注意事項,需要的朋友參考下吧
    2017-12-12
  • Android 簡單封裝獲取驗證碼倒計時功能

    Android 簡單封裝獲取驗證碼倒計時功能

    倒計時效果相信大家都不陌生,我們可以使用很多種方法去實現(xiàn)此效果,這里自己采用 CountDownTimer 定時器簡單封裝下此效果,方便我們隨時調(diào)用。下面小編給大家分享android驗證碼倒計時封裝方法,感興趣的朋友一起看看吧
    2018-01-01
  • Android鬧鐘機制實現(xiàn)定時任務(wù)功能

    Android鬧鐘機制實現(xiàn)定時任務(wù)功能

    這篇文章主要為大家詳細介紹了Android鬧鐘機制實現(xiàn)定時任務(wù)功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • Android實現(xiàn)兩個數(shù)相加功能

    Android實現(xiàn)兩個數(shù)相加功能

    這篇文章主要為大家詳細介紹了Android實現(xiàn)兩個數(shù)相加功能,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-03-03
  • Android布局居中的幾種做法

    Android布局居中的幾種做法

    這篇文章主要介紹了Android布局居中的幾種做法的相關(guān)資料,需要的朋友可以參考下
    2016-09-09
  • Android實現(xiàn)將View保存成Bitmap的方法

    Android實現(xiàn)將View保存成Bitmap的方法

    這篇文章主要介紹了Android實現(xiàn)將View保存成Bitmap的方法,涉及Android畫布Canvas、位圖bitmap及View的相關(guān)使用技巧,需要的朋友可以參考下
    2016-06-06
  • Mac 下 Android Studio 不打印日志的解決辦法

    Mac 下 Android Studio 不打印日志的解決辦法

    這篇文章主要介紹了Mac 下 Android Studio 不打印日志的解決辦法的相關(guān)資料,希望通過本文能幫助到大家,需要的朋友可以參考下
    2017-10-10
  • Android實現(xiàn)兩圓點之間來回移動加載進度

    Android實現(xiàn)兩圓點之間來回移動加載進度

    這篇文章主要為大家詳細介紹了Android實現(xiàn)兩圓點之間來回移動加載進度,文中示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-06-06

最新評論