米姥姥 發表於 2020-8-19 13:58:00

Android开发 ViewPager2

<h1><span style="color: rgba(0, 128, 128, 1)">前言</span></h1>
<p>  ViewPage2是ViewPage的取代者,解决了一些解决了其前辈ViewPage的大部分难题,包括从右到左的布局支持,垂直方向,可修改的Fragment集合等。从易用性上来说ViewPage2的确会比ViewPage更简单,并且它实际上是使用RecyclerView实现的。源码里可以很容易看到,另外它的适配器也是RecyclerView.Adapter。</p>
<h1><span style="color: rgba(0, 128, 128, 1)">特别注意</span></h1>
<p>在developer官网上,已经说明了 ViewPager2在导航键翻页(TV模式),根本没实现。所有,如果你的是TV应用,原则上不建议使用ViewPager2开发(TV应用用它浪费时间),而是使用ViewPager。</p>
<p>这下面说需要完善,都2022年了,居然还没完善。 本着钻研的精神(钻牛角尖),本博客最下面给了一个不太稳定的方法实现焦点的触控。&nbsp;</p>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202204/1497956-20220408112558646-421558356.png" alt="" loading="lazy"></p>
<p>&nbsp;</p>
<p>&nbsp;</p>
<h1><span style="color: rgba(0, 128, 128, 1)">依赖</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">dependencies {
      implementation </span>"androidx.viewpager2:viewpager2:1.0.0"<span style="color: rgba(0, 0, 0, 1)">
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">使用</span><span style="color: rgba(0, 128, 128, 1)">RecyclerView.Adapter</span></h1>
<p>使用场景一般是首页图片轮播,或者引导页面</p>
<p>xml</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">androidx.constraintlayout.widget.ConstraintLayout
    </span><span style="color: rgba(255, 0, 0, 1)">xmlns:android</span><span style="color: rgba(0, 0, 255, 1)">="http://schemas.android.com/apk/res/android"</span><span style="color: rgba(255, 0, 0, 1)">
    xmlns:app</span><span style="color: rgba(0, 0, 255, 1)">="http://schemas.android.com/apk/res-auto"</span><span style="color: rgba(255, 0, 0, 1)">
    xmlns:tools</span><span style="color: rgba(0, 0, 255, 1)">="http://schemas.android.com/tools"</span><span style="color: rgba(255, 0, 0, 1)">
    android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
    android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
    tools:context</span><span style="color: rgba(0, 0, 255, 1)">=".viewpage2.ViewPager2Activity"</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">androidx.viewpager2.widget.ViewPager2
      </span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/imagePager"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:orientation</span><span style="color: rgba(0, 0, 255, 1)">="horizontal"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

<span style="color: rgba(0, 0, 255, 1)">&lt;/</span><span style="color: rgba(128, 0, 0, 1)">androidx.constraintlayout.widget.ConstraintLayout</span><span style="color: rgba(0, 0, 255, 1)">&gt;</span></pre>
</div>
<p>Adapter</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span> ViewPager2Adapter1() : RecyclerView.Adapter&lt;ViewPager2Adapter1.ViewHolder&gt;<span style="color: rgba(0, 0, 0, 1)">() {
    val mImageList </span>= mutableListOf&lt;Int&gt;<span style="color: rgba(0, 0, 0, 1)">()

    fun refreshData(list :MutableList</span>&lt;Int&gt;?<span style="color: rgba(0, 0, 0, 1)">){
      list</span>?<span style="color: rgba(0, 0, 0, 1)">.let {
            mImageList.addAll(list)
      }
      notifyDataSetChanged()
    }

    </span><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
      val imageView </span>= itemView.findViewById&lt;ImageView&gt;<span style="color: rgba(0, 0, 0, 1)">(R.id.image)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewPager2Adapter1.ViewHolder {
      </span><span style="color: rgba(0, 0, 255, 1)">return</span> ViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.item_view_pager_demo_1, parent, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">))
    }

    override fun onBindViewHolder(holder: ViewPager2Adapter1.ViewHolder, position: Int) {
      holder.imageView.setImageResource(mImageList)
    }

    override fun getItemCount(): Int </span>=<span style="color: rgba(0, 0, 0, 1)"> mImageList.size
}</span></pre>
</div>
<p>activity</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span><span style="color: rgba(0, 0, 0, 1)"> ViewPager2Activity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle</span>?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
      setContentView(R.layout.activity_view_pager2)
      val adapter </span>=<span style="color: rgba(0, 0, 0, 1)"> ViewPager2Adapter1()
      val imagePager </span>= findViewById&lt;ViewPager2&gt;<span style="color: rgba(0, 0, 0, 1)">(R.id.imagePager)
      imagePager.adapter </span>=<span style="color: rgba(0, 0, 0, 1)"> adapter
      adapter.refreshData(mutableListOf(R.drawable.ic_landscape_1, R.drawable.ic_landscape_2, R.drawable.ic_landscape_3, R.drawable.ic_landscape_4))

    }
}</span></pre>
</div>
<p>效果图</p>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220303164438063-1262273333.gif" alt=""></p>
<p>&nbsp;</p>
<h1>&nbsp;</h1>
<h1><span style="color: rgba(0, 128, 128, 1)">使用FragmentStateAdapter</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">使用场景一般是主页tab分页,每一页都是Fragment</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">class</span> MessageAdapter(fragments: List&lt;Fragment&gt;<span style="color: rgba(0, 0, 0, 1)">, fragment: Fragment) : FragmentStateAdapter(fragment) {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> var mFragments :List&lt;Fragment&gt; =<span style="color: rgba(0, 0, 0, 1)"> fragments

    override fun getItemCount() </span>=<span style="color: rgba(0, 0, 0, 1)"> mFragments.size

    override fun createFragment(position: Int) </span>=<span style="color: rgba(0, 0, 0, 1)"> mFragments
}</span></pre>
</div>
<p><span>上面的代码是在Fragment中使用,如果需要在activity中使用就使用另一个构造方法</span></p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> FragmentStateAdapter(@NonNull FragmentActivity fragmentActivity)</pre>
</div>
<p><span>然后是添加到ViewPager2里</span></p>
<div class="cnblogs_code">
<pre>      val fragmentList =<span style="color: rgba(0, 0, 0, 1)"> listOf(AlertFragment.newInstance(), FamilyFragment.newInstance(), NoticeFragment.newInstance())
      val adapter </span>= MessageAdapter(fragmentList, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
      mBinding.viewPager.adapter </span>= adapter</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">与TabLayout配合使用</span><code dir="ltr"><span class="tag"><br></span></code></h1>
<div class="cnblogs_code">
<pre>TabLayout tabLayout =<span style="color: rgba(0, 0, 0, 1)"> view.findViewById(R.id.tab_layout);
      </span><span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)"> TabLayoutMediator(tabLayout, viewPager,
                (tab, position) </span>-&gt; tab.setText("OBJECT " + (position + 1<span style="color: rgba(0, 0, 0, 1)">))
      ).attach();</span></pre>
</div>
<p>&nbsp;但是请注意!在添加数据的时候不需要在newTab了,参考如下代码</p>
<div class="cnblogs_code">
<pre>    override fun onViewCreated(view: View, savedInstanceState: Bundle?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onViewCreated(view, savedInstanceState)
      val fragmentList </span>=<span style="color: rgba(0, 0, 0, 1)"> listOf(AlertFragment.newInstance(), FamilyFragment.newInstance(), NoticeFragment.newInstance())
      val adapter </span>= MessageAdapter(fragmentList, <span style="color: rgba(0, 0, 255, 1)">this</span><span style="color: rgba(0, 0, 0, 1)">)
      mBinding.viewPager.adapter </span>=<span style="color: rgba(0, 0, 0, 1)"> adapter
      val tabIconArray </span>=<span style="color: rgba(0, 0, 0, 1)"> intArrayOf(R.drawable.message_ic_alert_3, R.drawable.message_ic_family_3, R.drawable.message_ic_notice_3)
      TabLayoutMediator(mBinding.tabLayout, mBinding.viewPager) { tab, position </span>-&gt;<span style="color: rgba(0, 0, 0, 1)">

      }.attach()

      </span><span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> ((index, itemData)in tabIconArray.withIndex()) {
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">这里不需要newTab(),因为上面的TabLayoutMediator已经创建好了tab</span>
            val tab: TabLayout.Tab? =<span style="color: rgba(0, 0, 0, 1)"> mBinding.tabLayout.getTabAt(index)
            val binding </span>= MessageItemMessageBinding.inflate(layoutInflater, mBinding.tabLayout, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">)
            binding.icon.setImageResource(itemData)
            tab</span>?.customView =<span style="color: rgba(0, 0, 0, 1)"> binding.root
            mItemRedDotArray.add(binding.redDot)
      }
      initListener()
    }</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">禁用左右滑动切换</span></h1>
<div class="cnblogs_code">
<pre>viewPager.setUserInputEnabled(false)</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">设置方向</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">横</span></p>
<div class="cnblogs_code">
<pre>android:orientation="horizontal"</pre>
</div>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">竖</span></p>
<div class="cnblogs_code">
<pre>android:orientation="vertical"</pre>
</div>
<h1><span style="background-color: rgba(255, 255, 255, 1)"><span style="color: rgba(0, 128, 128, 1)">设置滑动方向</span></span></h1>
<p><span style="background-color: rgba(255, 255, 255, 1); color: rgba(0, 0, 0, 1)">从左到右</span></p>
<div class="cnblogs_code">
<pre>android:layoutDirection="ltr"</pre>
</div>
<p>从右到左</p>
<div class="cnblogs_code">
<pre>android:layoutDirection="rtl"</pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">翻页结果监听</span></h1>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">      imagePager.registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {

            override fun onPageScrollStateChanged(state: Int) {
                </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onPageScrollStateChanged(state)
                </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
                表示ViewPager2处于空闲、稳定状态。当前页面完全可见,并且没有动画正在进行中。
               SCROLL_STATE_IDLE = 0
                表示ViewPager2当前正在被用户拖动,或者通过虚假拖动功能以编程方式进行拖动。
               SCROLL_STATE_DRAGGING = 1
                表示ViewPager2正在稳定到最终位置。
               SCROLL_STATE_SETTLING = 2
               </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                Log.e(</span>"zh", "滚动状态变化 : state = $state"<span style="color: rgba(0, 0, 0, 1)">)
            }

            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onPageScrolled(position, positionOffset, positionOffsetPixels)
                </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
                position – 当前显示的第一页的位置索引。如果 positionOffset 不为零,则页面位置+1 将可见。
                positionOffset – 表示在位置处与页面的偏移量, 取值范围
                positionOffsetPixels – 以像素为单位的值,指示与位置的偏移量。
               </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                Log.e(</span>"zh", "正在翻页滚动 : position = $position positionOffset = $positionOffset positionOffsetPixels = $positionOffsetPixels"<span style="color: rgba(0, 0, 0, 1)">)

            }

            override fun onPageSelected(position: Int) {
                </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onPageSelected(position)
                Log.e(</span>"zh", "当前页面 : position = $position"<span style="color: rgba(0, 0, 0, 1)">)
            }
      })</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">翻页动画</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">ViewPager2的翻页动画稍微一点点复杂,需要一些耐心理解.</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">首先动画的实现是依靠</span>&nbsp;ViewPager2.PageTransformer 接口回调实现的, 在下面的代码中有注释说明,</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">      imagePager.setPageTransformer(object : ViewPager2.PageTransformer {
            override fun transformPage(page: View, position: Float) {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; -1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 范围 -无穷数 到 -1</span><span style="color: rgba(0, 128, 0, 1)">
                     // 此页面在左侧屏幕外,在这个位置的view已经看不到了,一般不需要处理,只要给初始默认值就行</span><span style="color: rgba(0, 0, 0, 1)">
                  Log.e(</span>"zh", "此页面在左侧屏幕外: $position"<span style="color: rgba(0, 0, 0, 1)">)
                } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt;= 1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">范围-1 到 1</span>
                  <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
                     * 在这个位置的view正在在屏幕中显示,是需要实现动画效果的view
                     * 这里其实有2个view会交替回调, 分别是position小于0的左边的view 与 position大于0的右边的view
                     </span><span style="color: rgba(0, 128, 0, 1)">*/</span>
                  <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; 0<span style="color: rgba(0, 0, 0, 1)">) {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">viewpager左边item的显示</span>
                        Log.e("zh", "viewpager左边item的显示 位置 = $position view的内存地址 = ${page}"<span style="color: rgba(0, 0, 0, 1)">)
                     
                  } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">viewpager右边item的显示</span>
                        Log.e("zh", "viewpager右边item的显示 位置 = $position view的内存地址 = ${page}"<span style="color: rgba(0, 0, 0, 1)">)
                  }

                } </span><span style="color: rgba(0, 0, 255, 1)">else</span> { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">范围 1 到 +无穷数
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 此页面在右侧屏幕外,在这个位置的view已经看不到了,一般不需要处理,只要给初始默认值就行</span>
                  Log.e("zh", "此页面在右侧屏幕外: $position"<span style="color: rgba(0, 0, 0, 1)">)
                }
            }
      })</span></pre>
</div>
<p>如果你看了上面的注释依然不太明白,我们可以用图片解释一下</p>
<h3>图片1</h3>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220303201153728-1934604448.png" alt="" width="714" height="598" loading="lazy"></p>
<h3>图片2</h3>
<p><span style="color: rgba(255, 0, 0, 1)"><strong>理解这张图片很重要!&nbsp;</strong></span>这张图片中的效果出现的时候,就是position 正在返回 -1 到 1的值.&nbsp;</p>
<p>这正是我们需要实现动画效果(透明度,放大缩小,旋转抛出等等效果)的时候</p>
<p>&nbsp;<img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220303201908806-1457385566.png" alt="" loading="lazy"></p>
<h2>举例一个淡入淡出动画效果</h2>
<p>在上面说明了动画的取值范围后,这里举例一个最容易实现的动画,淡入淡出动画.</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">      imagePager.setPageTransformer(object : ViewPager2.PageTransformer {
            override fun transformPage(page: View, position: Float) {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; -1) {<span style="color: rgba(0, 0, 0, 1)">
                  page.alpha </span>=<span style="color: rgba(0, 0, 0, 1)"> 0f

                } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt;= 1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">范围-1 到 1</span>
                  <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
                  * 求绝对值,作为动画的变量值
                  </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                  val animationValue: Float </span>=<span style="color: rgba(0, 0, 0, 1)"> Math.abs(position)
                  </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; 0<span style="color: rgba(0, 0, 0, 1)">) {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">viewpager左边item的显示</span>
                        page.alpha = 1 -<span style="color: rgba(0, 0, 0, 1)"> animationValue
                  } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">viewpager右边item的显示</span>
                        page.alpha = 1 -<span style="color: rgba(0, 0, 0, 1)"> animationValue
                  }

                } </span><span style="color: rgba(0, 0, 255, 1)">else</span> {                     <br>            page.alpha =<span style="color: rgba(0, 0, 0, 1)"> 0f
                }
            }
      })</span></pre>
</div>
<p>效果图</p>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220303210052429-2020853800.gif" alt=""></p>
<h1>举一反三实现旋转与缩小放大动画效果</h1>
<p>代码</p>
<div class="cnblogs_code">
<pre>   override fun onCreate(savedInstanceState: Bundle?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
      setContentView(R.layout.activity_view_pager2)
      val adapter </span>=<span style="color: rgba(0, 0, 0, 1)"> ViewPager2Adapter1()
      val imagePager </span>= findViewById&lt;ViewPager2&gt;<span style="color: rgba(0, 0, 0, 1)">(R.id.imagePager)
      imagePager.adapter </span>=<span style="color: rgba(0, 0, 0, 1)"> adapter
      adapter.refreshData(mutableListOf(R.drawable.ic_landscape_1, R.drawable.ic_landscape_2, R.drawable.ic_landscape_3, R.drawable.ic_landscape_4))
      imagePager.setPageTransformer(object : ViewPager2.PageTransformer {
            override fun transformPage(page: View, position: Float) {
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; -1<span style="color: rgba(0, 0, 0, 1)">) {
                  page.alpha </span>=<span style="color: rgba(0, 0, 0, 1)"> 0f

                } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt;= 1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">范围-1 到 1</span>
                  <span style="color: rgba(0, 128, 0, 1)">/*</span><span style="color: rgba(0, 128, 0, 1)">
                  * 求绝对值,作为动画的变量值
                  </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                  val animationValue: Float </span>=<span style="color: rgba(0, 0, 0, 1)"> Math.abs(position)
                  </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; 0<span style="color: rgba(0, 0, 0, 1)">) {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">viewpager左边item的显示</span>
                        page.alpha = 1 -<span style="color: rgba(0, 0, 0, 1)"> animationValue
                        setRotate(page, animationValue)
                        setScale(page, animationValue)
                  } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                        </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">viewpager右边item的显示</span>
                        page.alpha = 1 -<span style="color: rgba(0, 0, 0, 1)"> animationValue
                        setRotate(page, animationValue)
                        setScale(page, animationValue)
                  }

                } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                  page.alpha </span>=<span style="color: rgba(0, 0, 0, 1)"> 0f
                }
            }
      })
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * 旋转效果
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    fun setRotate(view: View, value: Float) {
      view.rotationX </span>= 0.5f<span style="color: rgba(0, 0, 0, 1)">
      view.rotationY </span>= 0.5f<span style="color: rgba(0, 0, 0, 1)">
      view.rotation </span>= 360 *<span style="color: rgba(0, 0, 0, 1)"> value
    }

    </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
   * 缩放效果
   </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
    fun setScale(view: View, value: Float) {
      view.scaleX </span>= 1 -<span style="color: rgba(0, 0, 0, 1)"> value
      view.scaleY </span>= 1 -<span style="color: rgba(0, 0, 0, 1)"> value
    }</span></pre>
</div>
<p>效果图</p>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220303210459985-323161782.gif" alt=""></p>
<h2>看看google给的例子</h2>
<p>例子1</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> DepthPageTransformer <span style="color: rgba(0, 0, 255, 1)">implements</span><span style="color: rgba(0, 0, 0, 1)"> PageTransformer {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">float</span> MIN_SCALE = 0.75f<span style="color: rgba(0, 0, 0, 1)">;

    @SuppressLint(</span>"NewApi"<span style="color: rgba(0, 0, 0, 1)">)
    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> transformPage(View view, <span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)"> position) {
      </span><span style="color: rgba(0, 0, 255, 1)">int</span> pageWidth =<span style="color: rgba(0, 0, 0, 1)"> view.getWidth();
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; -1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> [-Infinity,-1)</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">This page is way off-screen to the left.</span>
            view.setAlpha(0<span style="color: rgba(0, 0, 0, 1)">);
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt;= 0) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> [-1,0]Use </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">the default slide transition when moving to the left page</span>
            view.setAlpha(1<span style="color: rgba(0, 0, 0, 1)">);
            view.setTranslationX(</span>0<span style="color: rgba(0, 0, 0, 1)">);
            view.setScaleX(</span>1<span style="color: rgba(0, 0, 0, 1)">);
            view.setScaleY(</span>1<span style="color: rgba(0, 0, 0, 1)">);
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt;= 1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (0,1]</span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Fade the page out.</span>
            view.setAlpha(1 -<span style="color: rgba(0, 0, 0, 1)"> position);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Counteract the default slide transition</span>
            view.setTranslationX(pageWidth * -<span style="color: rgba(0, 0, 0, 1)">position);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Scale the page down (between MIN_SCALE and 1)</span>
            <span style="color: rgba(0, 0, 255, 1)">float</span> scaleFactor = MIN_SCALE + (1 -<span style="color: rgba(0, 0, 0, 1)"> MIN_SCALE)
                  </span>* (1 -<span style="color: rgba(0, 0, 0, 1)"> Math.abs(position));
            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (1,+Infinity]
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This page is way off-screen to the right.</span>
            view.setAlpha(0<span style="color: rgba(0, 0, 0, 1)">);

      }
    }

}</span></pre>
</div>
<p>&nbsp;效果图</p>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220304165728286-788027352.gif" alt=""></p>
<p>&nbsp;</p>
<p>例子2</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">class</span> ZoomOutPageTransformer <span style="color: rgba(0, 0, 255, 1)">implements</span><span style="color: rgba(0, 0, 0, 1)"> PageTransformer {
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">float</span> MIN_SCALE = 0.85f<span style="color: rgba(0, 0, 0, 1)">;
   
    </span><span style="color: rgba(0, 0, 255, 1)">private</span> <span style="color: rgba(0, 0, 255, 1)">static</span> <span style="color: rgba(0, 0, 255, 1)">float</span> MIN_ALPHA = 0.5f<span style="color: rgba(0, 0, 0, 1)">;
   
    @Override
    </span><span style="color: rgba(0, 0, 255, 1)">public</span> <span style="color: rgba(0, 0, 255, 1)">void</span> transformPage(View view, <span style="color: rgba(0, 0, 255, 1)">float</span><span style="color: rgba(0, 0, 0, 1)"> position) {
      </span><span style="color: rgba(0, 0, 255, 1)">int</span> pageWidth =<span style="color: rgba(0, 0, 0, 1)"> view.getWidth();
      </span><span style="color: rgba(0, 0, 255, 1)">int</span> pageHeight =<span style="color: rgba(0, 0, 0, 1)"> view.getHeight();
   
      </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; -1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> [-Infinity,-1)
                              </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This page is way off-screen to the left. </span>
            view.setAlpha(0<span style="color: rgba(0, 0, 0, 1)">);
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt;= 1) { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> [-1,1]
                                    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Modify the default slide transition to
                                    </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> shrink the page as well </span>
            <span style="color: rgba(0, 0, 255, 1)">float</span> scaleFactor = Math.max(MIN_SCALE, 1 -<span style="color: rgba(0, 0, 0, 1)"> Math.abs(position));
            </span><span style="color: rgba(0, 0, 255, 1)">float</span> vertMargin = pageHeight * (1 - scaleFactor) / 2<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">float</span> horzMargin = pageWidth * (1 - scaleFactor) / 2<span style="color: rgba(0, 0, 0, 1)">;
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (position &lt; 0<span style="color: rgba(0, 0, 0, 1)">) {
                view.setTranslationX(horzMargin </span>- vertMargin / 2<span style="color: rgba(0, 0, 0, 1)">);
            } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
                view.setTranslationX(</span>-horzMargin + vertMargin / 2<span style="color: rgba(0, 0, 0, 1)">);
            }
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Scale the page down (between MIN_SCALE and 1) </span>
<span style="color: rgba(0, 0, 0, 1)">            view.setScaleX(scaleFactor);
            view.setScaleY(scaleFactor);
            </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Fade the page relative to its size. </span>
            view.setAlpha(MIN_ALPHA + (scaleFactor -<span style="color: rgba(0, 0, 0, 1)"> MIN_SCALE)
                  </span>/ (1 - MIN_SCALE) * (1 -<span style="color: rgba(0, 0, 0, 1)"> MIN_ALPHA));
      } </span><span style="color: rgba(0, 0, 255, 1)">else</span> { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (1,+Infinity]
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This page is way off-screen to the right. </span>
            view.setAlpha(0<span style="color: rgba(0, 0, 0, 1)">);
      }
    }
} </span></pre>
</div>
<p>效果图</p>
<p><img src="https://img2022.cnblogs.com/blog/1497956/202203/1497956-20220304165913682-68116882.gif" alt=""></p>
<h1><span style="color: rgba(0, 128, 128, 1)">背景图片与ViewPager2 左右滑动的联动动画实现</span></h1>
<p>xml</p>
<div class="cnblogs_code">
<pre>    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">ImageView
      </span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/bgImage"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:scaleX</span><span style="color: rgba(0, 0, 255, 1)">="1.1"</span><span style="color: rgba(255, 0, 0, 1)">
      android:scaleY</span><span style="color: rgba(0, 0, 255, 1)">="1.1"</span><span style="color: rgba(255, 0, 0, 1)">
      android:background</span><span style="color: rgba(0, 0, 255, 1)">="@mipmap/launcher_ic_bg"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span>

    <span style="color: rgba(0, 0, 255, 1)">&lt;</span><span style="color: rgba(128, 0, 0, 1)">androidx.viewpager.widget.ViewPager
      </span><span style="color: rgba(255, 0, 0, 1)">android:id</span><span style="color: rgba(0, 0, 255, 1)">="@+id/viewPage"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_width</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      android:layout_height</span><span style="color: rgba(0, 0, 255, 1)">="match_parent"</span><span style="color: rgba(255, 0, 0, 1)">
      app:layout_constraintBottom_toBottomOf</span><span style="color: rgba(0, 0, 255, 1)">="parent"</span><span style="color: rgba(255, 0, 0, 1)">
      app:layout_constraintTop_toBottomOf</span><span style="color: rgba(0, 0, 255, 1)">="parent"</span><span style="color: rgba(255, 0, 0, 1)">
      app:layout_constraintVertical_bias</span><span style="color: rgba(0, 0, 255, 1)">="1.0"</span><span style="color: rgba(255, 0, 0, 1)">
      tools:layout_editor_absoluteX</span><span style="color: rgba(0, 0, 255, 1)">="0dp"</span> <span style="color: rgba(0, 0, 255, 1)">/&gt;</span></pre>
</div>
<p>java</p>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 0, 0, 1)">      mBinding.viewPage.addOnPageChangeListener(object : ViewPager.OnPageChangeListener {
            var lastPosition </span>= 0<span style="color: rgba(0, 0, 0, 1)">
            var animationValue </span>=<span style="color: rgba(0, 0, 0, 1)"> 50f
            lateinit var objectAnimator : ObjectAnimator
            override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
                mDotsAdapter.setSelectItem(position)
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (lastPosition ==<span style="color: rgba(0, 0, 0, 1)"> position){
                  </span><span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)">
                }
                </span><span style="color: rgba(0, 0, 255, 1)">if</span> (lastPosition &lt;<span style="color: rgba(0, 0, 0, 1)"> position){
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">右</span>
                  animationValue = animationValue -<span style="color: rgba(0, 0, 0, 1)">50f
                  objectAnimator</span>= ObjectAnimator.ofFloat(mBinding.bgImage,"translationX"<span style="color: rgba(0, 0, 0, 1)">, animationValue)
                } </span><span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)">{
                  </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">左</span>
                  animationValue = animationValue +<span style="color: rgba(0, 0, 0, 1)"> 50f
                  objectAnimator</span>= ObjectAnimator.ofFloat(mBinding.bgImage,"translationX"<span style="color: rgba(0, 0, 0, 1)">, animationValue)
                }
                objectAnimator.duration </span>= 500<span style="color: rgba(0, 0, 0, 1)">
                objectAnimator.start()
                lastPosition </span>=<span style="color: rgba(0, 0, 0, 1)"> position
            }

            override fun onPageSelected(position: Int) {
            }

            override fun onPageScrollStateChanged(state: Int) {
            }
      })</span></pre>
</div>
<h1><span style="color: rgba(0, 128, 128, 1)">TV应用焦点与ViewPager2的兼容处理</span></h1>
<p><span style="color: rgba(0, 0, 0, 1)">在developer官网上,已经说明了 ViewPager2在导航键翻页(TV模式),根本没实现。所有,如果你的是TV应用,原则上不建议使用ViewPager2开发,而是使用ViewPager。 如果你非得杆上ViewPager2,下面这个代码也提供一个实现思路。但是十分不稳定</span></p>
<p><span style="color: rgba(0, 0, 0, 1)">TV应用特别是支持触控又支持遥控器的TV设备,</span><span>如何让焦点翻页? 特别是如何只使用遥控器方向键的情况下翻页呢?</span></p>
<div class="cnblogs_code">
<pre>    override fun onCreate(savedInstanceState: Bundle?<span style="color: rgba(0, 0, 0, 1)">) {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onCreate(savedInstanceState)
      setContentView(mBinding.root)
      </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">注册全局焦点监听    </span>
<span style="color: rgba(0, 0, 0, 1)">      mBinding.root.getViewTreeObserver().addOnGlobalFocusChangeListener(focusChangeListener)
    }

    override fun onDestroy() {
      </span><span style="color: rgba(0, 0, 255, 1)">super</span><span style="color: rgba(0, 0, 0, 1)">.onDestroy()
      mBinding.root.viewTreeObserver.removeOnGlobalFocusChangeListener(focusChangeListener)
    }

    </span><span style="color: rgba(0, 0, 255, 1)">private</span> val focusChangeListener: ViewTreeObserver.OnGlobalFocusChangeListener =<span style="color: rgba(0, 0, 0, 1)"> object : ViewTreeObserver.OnGlobalFocusChangeListener {
      override fun onGlobalFocusChanged(oldFocus: View</span>?, newFocus: View?<span style="color: rgba(0, 0, 0, 1)">) {
            Log.e(</span>"zh", "newFocus: ${newFocus} "<span style="color: rgba(0, 0, 0, 1)">)
            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (newFocus != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
                </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
               * viewPage里的当前焦点view的第3个父类其实是RecyclerView,这里需要准确找到它。
               * 当前你也可以使用另一种方法,那就是继承ViewPage,然后让mRecyclerView 全局变量暴露到外包
               </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                mBinding.viewPage.findFocus()?.parent</span>?.parent?.parent?.let { recyclerView -&gt;
                  <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> (recyclerView is RecyclerView) {
                        </span><span style="color: rgba(0, 128, 0, 1)">/**</span><span style="color: rgba(0, 128, 0, 1)">
                         * 重点方法:findContainingViewHolder
                         * 根据当前焦点view找到当前的ViewHolder
                         </span><span style="color: rgba(0, 128, 0, 1)">*/</span><span style="color: rgba(0, 0, 0, 1)">
                        val viewHolder </span>=<span style="color: rgba(0, 0, 0, 1)"> recyclerView.findContainingViewHolder(mBinding.viewPage.findFocus())
                        </span><span style="color: rgba(0, 0, 255, 1)">if</span> (viewHolder != <span style="color: rgba(0, 0, 255, 1)">null</span><span style="color: rgba(0, 0, 0, 1)">) {
                            </span><span style="color: rgba(0, 0, 255, 1)">if</span> (viewHolder.layoutPosition !=<span style="color: rgba(0, 0, 0, 1)"> RecyclerView.NO_POSITION) {
                              </span><span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">使其翻页</span>
                              mBinding.viewPage.currentItem =<span style="color: rgba(0, 0, 0, 1)"> viewHolder.layoutPosition
                            }
                        }
                  }
                }
            }
      }
    }</span></pre>
</div>
<p>&nbsp;</p>
<h1><span style="background-color: rgba(255, 255, 255, 1)"><span style="color: rgba(0, 128, 128, 1)">ViewPager2的已知bug</span></span></h1>
<p>&nbsp;因为ViewPager与SwipeRefreshLayout冲突导致RecyclerView或者其他列表布局的item无法点击的问题</p>
<p>&nbsp;</p>
<p>End</p>

</div>
<div id="MySignature" role="contentinfo">
    <div style="text-align: center">
    <p style="color:orange;font-size:16px;" >本文来自博客园,作者:观心静 ,转载请注明原文链接:https://www.cnblogs.com/guanxinjing/p/13528962.html </p>
    <div style="color:orange;font-size:16px;">本文版权归作者和博客园共有,欢迎转载,但必须给出原文链接,并保留此段声明,否则保留追究法律责任的权利。 </div>
</div><br><br>
来源:https://www.cnblogs.com/guanxinjing/p/13528962.html
頁: [1]
查看完整版本: Android开发 ViewPager2