Android进阶之路 - ViewPager2 比 ViewPager 强在哪?

这篇具有很好参考价值的文章主要介绍了Android进阶之路 - ViewPager2 比 ViewPager 强在哪?。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

我记得前年(2022)面试的时候有被问到 ViewPager 和 ViewPager2 有什么区别?当时因为之前工作一直在开发售货机相关的项目,使用的技术要求并不高,所以一直没去了解过 ViewPager2~ 去年的时候正好有相关的功能需求,索性直接用 ViewPager2 进行了

Tip:很多人可能比较关注俩者区别、变更,那么我们结论先行,然后再接着验证

结论先行

关于它们的区别,我仅从我个人理解的角度来讲(不知不觉用了好几天…)

实现方面

  • ViewPager 继承自ViewGroup,内部并未使用已有的成熟控件,更多的是自定义的操作
  • ViewPager2 也继承自ViewGroup,但其内部可以明显的看到 RecyclerView 影子,所以可以说是基于 RecyclerView实现,那么这也意味着性能的提升,毕竟ViewHodelr减少了内存开销,同时RecyclerView相关方法在ViewPager2也可以看到类似封装

功能方面

TipViewPager2 新增功能是建立在 ViewPager 已有功能的基础上的扩展,例如ViewPager2支持垂直滑动,同时也是支持水平滑动的

  • ViewPager2 支持垂直方向滑动,而 ViewPager 仅支持水平方向滑动(扩展组件功能)

关于ViewPager2新支持的RTL方向布局简单概述一下:国内一般都是默认的LTR方向布局,但是针对国际用户会根据语言环境(阿拉伯语等)自动启动从右到左(RTL)页面布局,如果想要设置ViewPager2布局方向可以通过设置android:layoutDirection属性或setLayoutDirection()方式

  • ViewPager2 支持RTL方向布局,而 ViewPager 支持LTR方向布局(扩展组件功能)
  • ViewPager2 DiffUtil 支持,减少页面刷新频率,当数据未发生变更时不必重新绘制(提升用户体验)

Adapter(适配器)方面

  • ViewPager2 使用的Adapter,一般为PagerAdapter、及其子类 FragmentPagerAdapterFragmentStatePagerAdapter
  • ViewPager2 既然基于RecyclerView实现,那么它所使用的Adapter同理也应该基于RecyclerView.Adapter,所以新增了 FragmentStateAdapter

加载方面

  • ViewPager 默认执行预加载,如果需要懒加载的话,需要自行封装
  • ViewPager2 默认执行懒加载,但是依旧可以设置预加载

API方面

  • 监听:ViewPager2registerOnPageChangeCallback 取代了 ViewPageraddPageChangeListener
  • 关联TabLayout :ViewPagerTabLayout 关联用的是 TabLayout 的方法 setupWithViewPager()ViewPager2 是通过 TabLayoutMediator 类来做了个关联

基础了解

实践效果

Demo效果

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

项目效果

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

新增功能

如果想了解 ViewPager2,我觉得最好的方式可能就是跟着 ViewPager2官方文档 简单的过一下版本的更新情况

从更新记录可以看出部分 ViewPager、ViewPager2 区别

  • ViewPager2 支持 RTL布局(Right To Left?),ViewPager 仅支持LTR布局
  • ViewPager2 支持 水平方向、垂直方向(类似抖音、快手垂直切换视频的场景),ViewPager 仅支持横向滑动(水平方向)
  • DiffUtil 支持,减少页面刷新频率,当数据未发生变更时不必重新绘制 (记得好早以前就有类似工具,不过现在很多框架内部都做了Diff操作)

2019年2月7日 ViewPager2 应运而生(首个测试版本)

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

2019年11月20日 ViewPager2 出了首个稳定的正式版本
Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

源码解析

查看 ViewPager 可以看到其继承自 ViewGroup ,同时 内部貌似并未使用现有的View控件

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

查看 ViewPager2 发现它虽然同样继承自ViewGroup ,但其内部却是基于 RecyclerView 实现的,故 RecyclerView 具备的方法 ViewPager2 也可以尝试调用,常见的类似于LayoutManagerItemDecorator等类似方法

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

例如 ViewPager2 支持垂直方向滑动就提供了setOrientation 方法(默认水平方向),那么通过源码可以看出这种方式其实类似RecyclerView设置 LayoutManager 方式

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

setOrientation 方向提供了 ORIENTATION_HORIZONTALORIENTATION_VERTICAL;在动态设置中伪代码 viewPager2.orientation = ORIENTATION_VERTICAL

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别


变更场景

ViewPager、ViewPager2 中 引用对比

记得以前调用的是Suppor V4包下的 ViewPager,但现在不论是 ViewPagerViewPager2 都直接在 androidx 内了,基本创建项目后就可以直接引用了

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

ViewPager、ViewPager2 中 Adapter区别对比

ViewPager 源码注释中其实已经解释了Adapter相关内容

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

ViewPager 使用的 Adapter 主要是 PagerAdapter 及其子类FragmentPagerAdapterFragmentStatePagerAdapter,俩者的主要区别如下

  • FragmentPagerAdapter 支持缓存
  • FragmentStatePagerAdapter 不支持缓存

PagerAdapter

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

PagerAdapter 子类

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

ViewPager2 - API变更

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

ViewPager2 使用的 Adapter 主要是 RecyclerView.Adapter 的子类 FragmentStateAdapter,它也继承了RecyclerView的优点,内置了FragmentViewHolder提升了性能

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

FragmentStateAdapter

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

FragmentStateAdapter 提供了三种构造参数,支持绑定组件的生命周期

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

FragmentViewHolder

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别


ViewPager、ViewPager2 中 Adapter使用对比

以前我在别的知识结构 也曾引用过ViewPager,如有需要也可以借鉴下

对比后发现继承的 Adapter 和重写方法命名发生变更,此处主要说 API变更

  • ViewPager2 getItemCount 等于(=) ViewPager 中的 getCount
  • ViewPager2 createFragment 等于(=) ViewPager 中的 getItem

ViewPager

PagerAdapter的子类Adapter已经被标记过时了,最好还是开始用ViewPager2

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

示例

    class viewPager1Adapter(manager: FragmentManager):FragmentStatePagerAdapter(manager){
        override fun getCount(): Int {
            TODO("Not yet implemented")
        }

        override fun getItem(position: Int): Fragment {
            TODO("Not yet implemented")
        }
    }

ViewPager2

    class viewPager2Adapter(fragment: FragmentActivity):FragmentStateAdapter(fragment){
        override fun getItemCount(): Int {
            TODO("Not yet implemented")
        }

        override fun createFragment(position: Int): Fragment {
            TODO("Not yet implemented")
        }
    }

ViewPager、ViewPager2 中 监听对比

ViewPagerViewPager2 监听的内容相同,只有API简单变更了一下

ViewPager 通过 addOnPageChangeListener 添加监听

 viewPager1.addOnPageChangeListener(object :OnPageChangeListener{
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                TODO("Not yet implemented")
            }

            override fun onPageSelected(position: Int) {
                TODO("Not yet implemented")
            }

            override fun onPageScrollStateChanged(state: Int) {
                TODO("Not yet implemented")
            }
        })

ViewPager2 通过 registerOnPageChangeCallback 添加监听,注册函数一般都有unregister函数,不用的时候可以顺手注销一下,防止内存泄露

 viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                super.onPageScrollStateChanged(state)
            }
            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                Log.e("tag",position.toString())
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
            }
        })

ViewPager、ViewPager2 中 预加载、懒加载对比

首先要明确一个概念:不论是 ViewPagerViewPager2都支持预加载!

预加载的意义在于不必让用户每次都等切换页面后才执行接口请求、页面绘制等操作,而是直接显示效果,不过这样做性能开销方面会大一些,用户体验有时候需要看加载元素的多少而决定

懒加载顾名思义只有在用户需要的时候才去执行,其实现的核心意义在于

  • 是否为当前页面(是否可见)
  • 是否已经加载过了
  • 视图是否初始化完成(setUserVisibleHint()的调用在onCreateView之前)

预加载区别

  • ViewPager 默认调用了预加载方法,且默认预加载值为1(一般加载当前视图的左右相邻视图)

相关源码

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

  • ViewPager2 默认不进行预加载,相对应的预加载方法值为-1

相关源码

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别
Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别
Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

懒加载区别

  • ViewPager 并未实现懒加载,如果需要懒加载的话需要自行实现,通常在 Fragment 场景下就是重写 setUserVisibleHint方法,然后继承于该基类
  • ViewPager2 默认实现了懒加载, 主要是通过 LifecycleFragment 的生命周期进行管理

ViewPager、ViewPager2 关联TabLayout

因为我在项目中的 TabLayout 部分布局需要自定义一下,所以并未直接使用 TabLayout,有机会在详细说吧

  • ViewPagerTabLayout 的代码关联用的是 TabLayout 的方法 setupWithViewPager()
  • ViewPager2TabLayout 的代码关联是通过 TabLayoutMediator 类来做了关联

从ViewPager 迁移到 ViewPager2

具体迁移操作,可参考 从 ViewPager 迁移到 ViewPager2

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别


实战演练

关于ViewPager2的使用也可以看 官网代码示例:views-widgets-samples/ViewPager2

因为我Demo中并未结合TabLayout一起使用,如有需求也可以前往 用户指南:使用 ViewPager2 创建包含标签的滑动视图

Demo版本

同个页面使用ViewPagerViewPager2 遇到一个有趣的场景,最后有讲到原因与处理方式

我写Demo时为了更直观的对比,所以将 ViewPagerViewPager2 一起使用;但是在使用时我分别简单讲解了不同的使用方式和综合方式

前置配件

activity_main

<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager1"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:background="#f78744" />

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

</androidx.appcompat.widget.LinearLayoutCompat>

Tip:Demo内的AFragmentBFragment基本一致,直接copy修改下想要布局就好

AFragment

package com.example.viewpager2

import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
class AFragment: Fragment() {
    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
        return LayoutInflater.from(this.requireContext()).inflate(R.layout.fragment_a,null)
    }
}

fragment_a

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="A-Fragment"
        android:gravity="center"
        android:textStyle="bold"
        />
</LinearLayout>
ViewPager 使用方式
package com.example.viewpager2

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import androidx.viewpager.widget.ViewPager.OnPageChangeListener


class MainActivity : AppCompatActivity() {
    @SuppressLint("MissingInflatedId")

    private var fragmentList: MutableList<Fragment> = mutableListOf()
    private var fragmentListA: MutableList<Fragment> = mutableListOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var viewPager1 = findViewById<ViewPager>(R.id.view_pager1)
        //初始化数据
        fragmentList.add(AFragment())
        fragmentList.add(BFragment())

        //ViewPager使用
        val viewPager1Adapter = ViewPager1Adapter(supportFragmentManager,fragmentList)
        viewPager1.setOffscreenPageLimit(0);
        viewPager1.adapter = viewPager1Adapter
//
        viewPager1.addOnPageChangeListener(object : OnPageChangeListener {
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            }

            override fun onPageSelected(position: Int) {
            }

            override fun onPageScrollStateChanged(state: Int) {
            }
        })
    }

    inner class ViewPager1Adapter(manager: FragmentManager, fragmentList: MutableList<Fragment>) : FragmentStatePagerAdapter(manager) {
        var manager: FragmentManager
        var fragmentList: MutableList<Fragment> = mutableListOf()

        init {
            this.manager = manager
            this.fragmentList = fragmentList
        }

        override fun getCount(): Int {
            return fragmentList.size
        }

        override fun getItem(position: Int): Fragment {
            return fragmentList[position]
        }
    }
}
ViewPager2 使用方式
package com.example.viewpager2

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback

class MainActivity : AppCompatActivity() {
    @SuppressLint("MissingInflatedId")

    private var fragmentList: MutableList<Fragment> = mutableListOf()
    private var fragmentListA: MutableList<Fragment> = mutableListOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var viewPager2 = findViewById<ViewPager2>(R.id.view_pager2)
        //初始化数据
        fragmentList.add(AFragment())
        fragmentList.add(BFragment())

        //ViewPager2使用
        val viewPager2Adapter = ViewPager2Adapter(this)
        viewPager2.orientation = ORIENTATION_VERTICAL
        viewPager2.adapter = viewPager2Adapter
        viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                super.onPageScrollStateChanged(state)
            }

            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                Log.e("tag", position.toString())
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
            }
        })

    }

    inner class ViewPager2Adapter(fragment: FragmentActivity) : FragmentStateAdapter(fragment) {
        override fun getItemCount(): Int {
            return fragmentListA.size
        }

        override fun createFragment(position: Int): Fragment {
            return fragmentListA[position]
        }
    }
}
ViewPager、ViewPager2 一起使用

为什么我会用到俩个List?最后告诉你...

package com.example.viewpager2

import android.annotation.SuppressLint
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
import androidx.fragment.app.FragmentStatePagerAdapter
import androidx.viewpager.widget.ViewPager
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import androidx.viewpager2.widget.ViewPager2.ORIENTATION_VERTICAL
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback

class MainActivity : AppCompatActivity() {
    @SuppressLint("MissingInflatedId")

    private var fragmentList: MutableList<Fragment> = mutableListOf()
    private var fragmentListA: MutableList<Fragment> = mutableListOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        var viewPager1 = findViewById<ViewPager>(R.id.view_pager1)
        var viewPager2 = findViewById<ViewPager2>(R.id.view_pager2)
        //初始化数据
        fragmentList.add(AFragment())
        fragmentList.add(BFragment())
        fragmentListA.add(AFragment())
        fragmentListA.add(BFragment())

        //ViewPager使用
        val viewPager1Adapter = ViewPager1Adapter(supportFragmentManager,fragmentList)
        viewPager1.setOffscreenPageLimit(0);
        viewPager1.adapter = viewPager1Adapter
//
        viewPager1.addOnPageChangeListener(object : OnPageChangeListener {
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
            }

            override fun onPageSelected(position: Int) {
            }

            override fun onPageScrollStateChanged(state: Int) {
            }
        })

        //ViewPager2使用
        val viewPager2Adapter = ViewPager2Adapter(this)
        viewPager2.orientation = ORIENTATION_VERTICAL
        viewPager2.adapter = viewPager2Adapter
        viewPager2.registerOnPageChangeCallback(object : OnPageChangeCallback() {
            override fun onPageScrollStateChanged(state: Int) {
                super.onPageScrollStateChanged(state)
            }

            override fun onPageSelected(position: Int) {
                super.onPageSelected(position)
                Log.e("tag", position.toString())
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                super.onPageScrolled(position, positionOffset, positionOffsetPixels)
            }
        })

    }

    inner class ViewPager1Adapter(manager: FragmentManager, fragmentList: MutableList<Fragment>) : FragmentStatePagerAdapter(manager) {
        var manager: FragmentManager
        var fragmentList: MutableList<Fragment> = mutableListOf()

        init {
            this.manager = manager
            this.fragmentList = fragmentList
        }

        override fun getCount(): Int {
            return fragmentList.size
        }

        override fun getItem(position: Int): Fragment {
            return fragmentList[position]
        }
    }

    inner class ViewPager2Adapter(fragment: FragmentActivity) : FragmentStateAdapter(fragment) {
        override fun getItemCount(): Int {
            return fragmentListA.size
        }

        override fun createFragment(position: Int): Fragment {
            return fragmentListA[position]
        }
    }
}

项目版本

因为是在项目中使用的实践场景,所以只留伪代码做个记录;部分框架特有的方法可先行忽略、部分业务逻辑亦可忽略,只看类似 TabLayout + ViewPager2的效果即可

ActivityFragment数据共享采用了ViewModel;接口返回数据监听采用了LiveData;有兴趣的可以前往进阶

  • 组件化之路 - ViewModel一知半解
  • 组件化之路 - LiveData一知半解
  • 组件化之路 - LiveData + ViewModel一知半解

组件中可以有多个ViewModel,在这里为了区分功能一个 FundSmileViewModel 用于网络请求等逻辑操作,一个 FundCodeViewModel 用于存放共享数据(当然也可以只用一个)

package cn.com.xx

import android.graphics.Color
import android.os.Bundle
import androidx.activity.viewModels
import androidx.core.graphics.toColorInt
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import dagger.hilt.android.AndroidEntryPoint

@AndroidEntryPoint
class FundAndSmileActivity : BaseActivity() {

    private val binding by lazy { ActivityPurchaseAndSmileBinding.inflate(layoutInflater) }

    private val fundCodeViewModel by viewModels<FundCodeViewModel>()

    private val viewModel by viewModels<FundSmileViewModel>()

    private lateinit var fundCode: String

    private var fragmentList: MutableList<Fragment> = mutableListOf()

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)
        ActivityTaskManager.getManager(TaskTag.FIXED_SMILE).addActivity(this)
        statusBar()
        setData()
        loadingState(LoadingState.LoadingStart)
        viewModel.requestFundControl5010(fundCode)
        binding.ivImgTag.isVisible = SPUtils.AppSP().get("fundSmileTag", "0") == "0"
        viewModel.smileState.observe(this) {
            loadingState(LoadingState.LoadingEnd)
            binding.llTab.isVisible = it
            fragmentList.clear()
            fragmentList.add(FundFixedCreateFragment())
            if (it) fragmentList.add(SmileInvestmentCreateFragment())
            initView()
        }
    }

    fun statusBar() {
        statusBarDarkView(binding.statusBar)
        binding.titleBarBg.setBackgroundColor(Color.WHITE)
        binding.titleBar.setLeftImageAction(R.drawable.icon_base_nav_back_black) { onBackPressedDispatcher.onBackPressed() }
        binding.titleBar.setTitle(text = "定投设置", textColor = "#484848".toColorInt(), medium = true)
        binding.titleBar.setDividerState(color = "#eeeeee".toColorInt())
    }

    fun setData() {
        fundCode = intent.getStringExtra("fundCode") ?: ""
        val pickerParams1 = intent?.getParcelable("pickerParams1", PickerParams::class)
        val pickerParams2 = intent?.getParcelable("pickerParams2", PickerParams::class)
        fundCodeViewModel.putFundCode(fundCode)
        pickerParams1?.let { fundCodeViewModel.putPickerParams1(it) }
        pickerParams2?.let { fundCodeViewModel.putPickerParams2(it) }
    }

    fun initView() {
        val pagerAdapter = PagerAdapter(this)
        binding.viewPage.isUserInputEnabled = false;//禁止滑动
        binding.viewPage.adapter = pagerAdapter
        binding.viewPage.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
            override fun onPageSelected(position: Int) {
                tabState(position)
            }
        })

        binding.llTab1.setOnClickListener {
            tabState(0)
            binding.viewPage.currentItem = 0
        }
        binding.llTab2.setOnClickListener {
            tabState(1)
            binding.viewPage.currentItem = 1
        }
    }

    fun tabState(state: Int) {
        binding.tvTabTitle1.setTextColor("#333333".toColorInt())
        binding.tvTabDesc1.setTextColor("#999999".toColorInt())
        binding.viewTab1.setBackgroundColor("#F9F9FA".toColorInt())
        binding.tvTabTitle2.setTextColor("#333333".toColorInt())
        binding.tvTabDesc2.setTextColor("#999999".toColorInt())
        binding.viewTab2.setBackgroundColor("#F9F9FA".toColorInt())

        if (state == 0) {
            binding.tvTabTitle1.setTextColor("#1760EA".toColorInt())
            binding.tvTabDesc1.setTextColor("#801760EA".toColorInt())
            binding.viewTab1.setBackgroundColor("#1760EA".toColorInt())
        } else {
            binding.tvTabTitle2.setTextColor("#1760EA".toColorInt())
            binding.tvTabDesc2.setTextColor("#801760EA".toColorInt())
            binding.viewTab2.setBackgroundColor("#1760EA".toColorInt())
            binding.ivImgTag.isVisible = false
            SPUtils.AppSP().put("fundSmileTag", "1")
        }
    }

    inner class PagerAdapter(fragment: FragmentActivity) : FragmentStateAdapter(fragment) {
        override fun getItemCount(): Int {
            return fragmentList.size
        }

        override fun createFragment(position: Int): Fragment {
            return fragmentList[position]
        }
    }
}

activity_purchase_and_smile(ActivityPurchaseAndSmileBinding)

Tip:部分自定义控件,自行取舍、更换即可

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:ignore="MissingDefaultResource">

    <LinearLayout
        android:id="@+id/title_bar_bg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@drawable/drawable_title_bar_blue"
        android:orientation="vertical"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <View
            android:id="@+id/status_bar"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            tools:layout_height="28dp" />

        <cn.com.ui.widgets.TitleBar
            android:id="@+id/title_bar"
            android:layout_width="match_parent"
            android:layout_height="?actionBarSize"
            tools:layout_height="48dp" />

    </LinearLayout>

    <LinearLayout
        android:id="@+id/ll_tab"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#F9F9FA"
        android:orientation="horizontal"
        android:visibility="gone"
        tools:ignore="MissingConstraints"
        tools:visibility="visible">

        <LinearLayout
            android:layout_marginStart="12dp"
            android:id="@+id/ll_tab1"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:orientation="vertical">

            <cn.com.acts.ui.widgets.MediumTextView
                android:id="@+id/tv_tab_title1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="@dimen/mp_8"
                android:text="普通定投"
                android:textColor="#1760EA"
                android:textSize="16dp"
                app:mediumText="true" />

            <TextView
                android:id="@+id/tv_tab_desc1"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="1dp"
                android:text="定期定额"
                android:textColor="#801760EA"
                android:textSize="12dp" />

            <View
                android:id="@+id/view_tab1"
                android:layout_width="match_parent"
                android:layout_height="2dp"
                android:layout_marginTop="4dp"
                android:background="#1760EA" />
        </LinearLayout>

        <LinearLayout
            android:id="@+id/ll_tab2"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:gravity="center"
            android:layout_marginEnd="12dp"
            android:orientation="vertical">

            <LinearLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:orientation="horizontal">

                <cn.com.acts.ui.widgets.MediumTextView
                    android:id="@+id/tv_tab_title2"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/mp_8"
                    android:text="微笑智能定投"
                    android:textColor="#1760EA"
                    android:textSize="16dp"
                    app:mediumText="true" />

                <ImageView
                    android:id="@+id/iv_img_tag"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_marginTop="@dimen/mp_5"
                    android:src="@drawable/icon_fund_smile_top_right_tag"
                    android:visibility="gone"
                    tools:visibility="visible" />
            </LinearLayout>

            <TextView
                android:id="@+id/tv_tab_desc2"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginTop="1dp"
                android:text="定期不定额 全新智能模型"
                android:textColor="#801760EA"
                android:textSize="12dp" />

            <View
                android:id="@+id/view_tab2"
                android:layout_width="match_parent"
                android:layout_height="2dp"
                android:layout_marginTop="4dp"
                android:background="#1760EA" />
        </LinearLayout>
    </LinearLayout>

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

</LinearLayout>

有趣的小问题:java.lang.IllegalStateException: Fragment already added

当我在Activity中同时设置ViewPagerViewPager2的Adapter时(引用同一份数据源),报了以下错误

Tip:从错误来看标明Fragment已经被添加过了;具体原因是当我们添加一个Fragment到Activity中,FragmentManager会负责管理Fragment生命周期和状态,如果尝试多次添加同一个Fragment实例,就会报以下错误

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

知道报错原因,那么解决方案就由之而来,故最终我在 ViewPager、ViewPager2 一起使用 中分别使用了不同的数据源(曲线救国,非最优解)~

以下个人瞎捉摸,可忽略:我扭头看了一下 fragmentList 仅添加了一次数据,为什么会有重复实例? 然后我就看到了ViewPagerViewPager2所使用 Adapter构造的不同,获取FragmentManager后涉及到了组件 lifecycle

FragmentStatePagerAdapter

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别

FragmentStateAdapter

Android进阶之路 - ViewPager2 比 ViewPager 强在哪?,Android进阶之路,Android,ViewPager2,ViewPager,ViewPager1、2区别文章来源地址https://www.toymoban.com/news/detail-828047.html

到了这里,关于Android进阶之路 - ViewPager2 比 ViewPager 强在哪?的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • android 关于TabLayout联动ViewPager2 实现底部导航栏

    最近在心血来潮想写在app 不过我关于android可以说是0基础 在写底部导航栏的时候去问了大佬才知道TabLayout和ViewPager 花了两天才看懂... 这里只是简单介绍因为我不准备专门做安卓软件所以在学的途中很多地方没有认真记 本篇文章使用的代码是Java 这里官方是有将两个进行联动

    2024年01月25日
    浏览(41)
  • 【Android】使用ViewPager2实现轮播图效果,手动/自动轮播图

    这里使用Gilde进行加载图片:Glide 使用Gilde可以加载网络图片还可以提高图片加载性能。 接下来新建一个子布局item_image,加载viewPage2的子布局。 ViewPage2就是使用recyclerView实现的,所以这里使用方法其实类似。 这里直接继承RecyclerView.Adapter即可,代码很简单不必多说。 创建一个

    2024年02月03日
    浏览(48)
  • Android 使用ViewPager2+ExoPlayer+VideoCache 实现仿抖音视频翻页播放

    1. 实现效果    效果图中,视频没有铺满 是因为使用了ExoPlayer的RESIZE_MODE_FIT模式, 虽然使用RESIZE_MODE_FILL模式可以填充整个父布局,但是本Demo中使用的视频源本身就不适合全屏,会把视频拉伸,效果不好。 抖音上的视频源应该都有严格的宽高尺寸,才能做到全屏有很好的效

    2023年04月09日
    浏览(37)
  • 从七个方面聊聊linux到底强在哪

      从事计算机相关行业的同学不难发现,身边总有一些朋友在学习linux,有的开发同学甚至自己的电脑就是它。经常听他们说linux如何好用等等。那么linux到底好在那里,能让大家如此喜欢。这也是我经常问自己的一个问题。下面我将通过以下七点来为大家阐述linux的巨大优势

    2024年02月02日
    浏览(56)
  • ViewPager2+TabLayout

    ViewPager2最显著的特点是基于RecyclerView实现,RecyclerView是目前Android端最成熟的AdapterView解决方案,这带来诸多好处: 1、抛弃传统的PagerAdapter,统一了Adapter的API/ 2、通过LinearLayoutManager可以实现类似抖音的纵向滑动 3、支持DiffUitl,可以通过diff实现局部刷新 4、支持RTL(right-to-

    2023年04月19日
    浏览(37)
  • ViewPager2与TabLayout的简单使用

    ViewPager2与TabLayout的简单使用 MainActivity.java activity_main.xml ViewPagerAdapter.java ShowBigIdBean.java item_img.xml item_icon_layout.xml item_circle_shape.xml

    2024年02月12日
    浏览(36)
  • Android进阶之路 - 字体自适应

    开发中有很多场景需要进行自适应适配,但是关于这种字体自适应,我也是为数不多的几次使用,同时也简单分析了下源码,希望我们都有收获 很多时候控件的宽度是有限的,而要实现比较好看的UI效果,常见的处理方式应该有以下几种 默认执行多行显示 单行显示,不足部

    2024年02月08日
    浏览(42)
  • Android进阶之路 - 背景阴影、阴影背景

    不知道你是不是也经常听到这些话:你这个没有阴影效果;你这个阴影太浓了;你这个阴影太粗了;你这个阴影太实了;你这个阴影颜色也不对,你这个阴影… 在正式开发中,临近上线前有个环节叫UI验收(产品验收在其前后均可),主要查看开发效果与设计图是否统一,当

    2024年02月04日
    浏览(40)
  • Android进阶之路 - 存、取、读 本地 Json 文件

    最近在开发中又开始加载一些本地的json数据源,回头看之前竟然没记录,赶紧记录一波 ~ 如何准备一个合格的json文件? 准备一个txt格式的json串, 另存为(A)... 更改文件名后缀为json 、 更改编码格式为 UTF-8 - 保存后你就获取到了一个合格的json文件了 题外话 - 此处可不看 因

    2024年02月01日
    浏览(36)
  • Android进阶之路 - 通过Gradle 窗口、插件、命令 查看项目依赖

    最近在处理一个依赖冲突的问题,虽然根据错误提示已经解决了问题;但还是很有必要了解一下 如何查看项目中各模块的依赖组件 ,以便于快速解决组件依赖的问题,故特记录于此 在实战中我们一般可以通过 Gradle窗口 、 Gradle插件 、以及 Terminal 执行Gradle相关命令 获取项目

    2024年02月04日
    浏览(43)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包