Bootstrap

Android:ViewPager \ViewPager2 简单介绍 & 使用方法解析

ViewPager2

1、什么是ViewPager?

布局管理器允许左右翻转带数据的页面,你想要显示的视图可以通过实现PagerAdapter来显示。这个类其实是在早期设计和开发的,它的API在后面的更新之中可能会被改变,当它们在新版本之中编译的时候可能还会改变源码。

ViewPager经常用来连接Fragment,它很方便管理每个页面的生命周期,使用ViewPager管理Fragment是标准的适配器实现。最常用的实现一般有FragmentPagerAdapter和FragmentStatePagerAdapter。

ViewPager是android扩展包v4包中的类,这个类可以让我们左右切换当前的view。我们先来聊聊ViewPager的几个相关知识点:

  • 1、ViewPager类直接继承了ViewGroup类,因此它一个容器类,可以添加其他的view类
  • 2、ViewPager类需要一个PagerAdapter适配器类给它提供数据(这点跟ListView一样需要数据适配器Adater)

3、ViewPager经常和Fragment一起使用,并且官方还提供了专门的FragmentPagerAdapterFragmentStatePagerAdapter类供Fragment中的ViewPager使用

2、什么是ViewPager2

原理: viewpager2 内部实现原理是使用recycleview加LinearLayoutManager实现竖直滚动,其实可以理解为对recyclerview的二次封装

源代码如下:

 private void initialize(Context context, AttributeSet attrs) {
        mRecyclerView = new RecyclerView(context) {
            @Override
            public CharSequence getAccessibilityClassName() {
                return "androidx.viewpager.widget.ViewPager";
            }

            @Override
            public void onInitializeAccessibilityEvent(@NonNull AccessibilityEvent event) {
                super.onInitializeAccessibilityEvent(event);
                event.setFromIndex(mCurrentItem);
                event.setToIndex(mCurrentItem);
            }
        };
        mRecyclerView.setId(ViewCompat.generateViewId());

        mLayoutManager = new LinearLayoutManager(context);
        mRecyclerView.setLayoutManager(mLayoutManager);
        setOrientation(context, attrs);

        mRecyclerView.setLayoutParams(
                new ViewGroup.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));
        .....
        attachViewToParent(mRecyclerView, 0, mRecyclerView.getLayoutParams());
    }

ViewPager2的出现是为了替代ViewPager,它有以下几个优势:

  • 支持RTL布局,
  • 支持竖向滚动
  • 支持notifyDataSetChanged

RTL布局是Right To Left布局也就是从右往左的布局,大家知道我们平常写的布局都是从左往右,但是如果你适配阿拉伯语等环境的UI布局,他们的写法是从右往左的,具体这里不做研究。

API的变动:

  • FragmentStateAdapter替换了原来的 FragmentStatePagerAdapter
  • RecyclerView.Adapter替换了原来的 PagerAdapter
  • registerOnPageChangeCallback替换了原来的 addPageChangeListener

FragmentStateAdapter和FragmentStatePagerAdapter作用相同, 可以用viewpager来管理fragment, 区别在于viewpager2的FragmentStateAdapter与recycleview的生命周期绑定

另外viewpager2的Adapter是继承自recyclerview的adapter, 支持除了notifyDataSetChanged()以外的notifyItemChanged(int position)等方式, 使用上更加的便捷

在这里插入图片描述

3、ViewPager使用案例

activity_main.xml:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
    </android.support.v4.view.ViewPager>

</RelativeLayout>

tab.xml(作为ViewPager的子布局):

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

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Tab1"
        />

</LinearLayout>

然后这里我们先创建4个要在ViewPager中显示的界面,代码如下:

ArrayList<View> viewContainter = new ArrayList<View>();
View view1 = LayoutInflater.from(this).inflate(R.layout.tab1, null);
View view2 = LayoutInflater.from(this).inflate(R.layout.tab2, null);
View view3 = LayoutInflater.from(this).inflate(R.layout.tab3, null);
View view4 = LayoutInflater.from(this).inflate(R.layout.tab4, null);
//viewContainter添加view
viewContainter.add(view1);
viewContainter.add(view2);
viewContainter.add(view3);
viewContainter.add(view4);

有了数据后我们就要创建数据适配器了,我们创建一个数据适配器MyPagerAdapter继承自PagerAdapter

MyPagerAdapters完整代码:

/**
*   ViewPager的数据适配器
*/
class MyPagerAdapters extends PagerAdapter{
   //返回可以滑动的VIew的个数
   @Override
   public int getCount() {
       return viewContainter.size();
   }
   //滑动切换的时候销毁当前的组件
   @Override
   public void destroyItem(ViewGroup container, int position,
                           Object object) {
       ((ViewPager) container).removeView(viewContainter.get(position));
   }
   //将当前视图添加到container中并返回当前View视图
   @Override
   public Object instantiateItem(ViewGroup container, int position) {
       ((ViewPager) container).addView(viewContainter.get(position));
       return viewContainter.get(position);
   }

   @Override
   public boolean isViewFromObject(View arg0, Object arg1) {
       return arg0 == arg1;
   }

PagerAdapter我们必须重写以下几个方法:

int getCount()
getCount():返回可以滑动的VIew的个数

void destroyItem(ViewGroup container, int position,Object object)
从当前container中删除指定位置的View

Object instantiateItem(ViewGroup container, int position)
将当前视图添加到container中并返回当前View视图

boolean isViewFromObject(View arg0, Object arg1)
该函数用来判断instantiateItem(ViewGroup, int)函数所返回来的Object与一个页面视图是否是代表的同一个视图,

官方建议直接返回arg0 == arg1即可。

MainActivity完整代码如下:

package com.zejian.viewpager;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.PagerTabStrip;
import android.support.v4.view.ViewPager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import java.util.ArrayList;

public class MainActivity extends Activity {

    ViewPager pager = null;
    PagerTabStrip tabStrip = null;
    ArrayList<View> viewContainter = new ArrayList<View>();

    @SuppressLint("ResourceAsColor")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        pager = (ViewPager) this.findViewById(R.id.viewpager);

        View view1 = LayoutInflater.from(this).inflate(R.layout.tab1, null);
        View view2 = LayoutInflater.from(this).inflate(R.layout.tab2, null);
        View view3 = LayoutInflater.from(this).inflate(R.layout.tab3, null);
        View view4 = LayoutInflater.from(this).inflate(R.layout.tab4, null);
        //viewpager开始添加view
        viewContainter.add(view1);
        viewContainter.add(view2);
        viewContainter.add(view3);
        viewContainter.add(view4);
        //设置Adapter
        pager.setAdapter(new MyPagerAdapters());

    }

    /**
    *   ViewPager的数据适配器
    */
    class MyPagerAdapters extends PagerAdapter{
        //返回可以滑动的VIew的个数
        @Override
        public int getCount() {
            return viewContainter.size();
        }
        //滑动切换的时候销毁当前的组件
        @Override
        public void destroyItem(ViewGroup container, int position,
                                Object object) {
            ((ViewPager) container).removeView(viewContainter.get(position));
        }
        //将当前视图添加到container中并返回当前View视图
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            ((ViewPager) container).addView(viewContainter.get(position));
            return viewContainter.get(position);
        }

        @Override
        public boolean isViewFromObject(View arg0, Object arg1) {
            return arg0 == arg1;
        }
    }
}

4、ViewPager2使用案例

实现水平滚动实例

引入 ViewPager2 组件:

dependencies {
    implementation "androidx.viewpager2:viewpager2:1.0.0-alpha02"
}

把 ViewPager2 声明在 activity_horizontal_scrolling.xml 里面:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    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"
    tools:context=".MainActivity">

    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/viewpager2"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_width="0dp"
        android:layout_height="0dp"/>

</androidx.constraintlayout.widget.ConstraintLayout>

建立一个简单的 item 布局,item_page.xml:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:id="@+id/container"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <androidx.appcompat.widget.AppCompatTextView
        android:id="@+id/tvTitle"
        tools:text="item"
        android:layout_centerInParent="true"
        android:textColor="@android:color/white"
        android:textSize="32dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

建立数据适配器 ViewPagerAdapter:

public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder> {
    private List<Integer> colors = new ArrayList<>();
    {
        colors.add(android.R.color.black);
        colors.add(android.R.color.holo_purple);
        colors.add(android.R.color.holo_blue_dark);
        colors.add(android.R.color.holo_green_light);
    }
    @NonNull
    @Override
    public ViewPagerViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new ViewPagerViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_page, parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull ViewPagerViewHolder holder, int position) {
        holder.mTvTitle.setText("item " + position);
        holder.mContainer.setBackgroundResource(colors.get(position));
    }

    @Override
    public int getItemCount() {
        return colors.size();
    }

    class ViewPagerViewHolder extends RecyclerView.ViewHolder {
        TextView mTvTitle;
        RelativeLayout mContainer;
        public ViewPagerViewHolder(@NonNull View itemView) {
            super(itemView);
            mContainer = itemView.findViewById(R.id.container);
            mTvTitle = itemView.findViewById(R.id.tvTitle);
        }
    }
}


在 HorizontalScrollingActivity 的 onCreate 方法中,设置 ViewPager2:

public class HorizontalScrollingActivity extends AppCompatActivity {

    public static void start(Context context) {
        Intent starter = new Intent(context, HorizontalScrollingActivity.class);
        context.startActivity(starter);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_horizontal_scrolling);
        ViewPager2 viewPager2 = findViewById(R.id.viewpager2);
        ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();
        viewPager2.setAdapter(viewPagerAdapter);
    }
}

拓展:VerticalScrollingActivity的使用

修改HorizontalScrollingActivity 为VerticalScrollingActivity

package com.example.viewpager2demo;

import android.content.Context;
import android.content.Intent;
import android.os.Bundle;

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager2.widget.ViewPager2;

public class VerticalScrollingActivity extends AppCompatActivity {

    public static void start(Context context) {
        Intent starter = new Intent(context, VerticalScrollingActivity.class);
        context.startActivity(starter);
    }
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_horizontal_scrolling);
        ViewPager2 viewPager2 = findViewById(R.id.viewpager2);
        ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter();
        viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
        viewPager2.setAdapter(viewPagerAdapter);
    }
}

4.1、Viewpager2+TabLayout+Fragment的简单使用

探秘FragmentStateAdapter:当Fragment碰上ViewPager2

activity_main

<?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:context=".MainActivity">
    <com.google.android.material.tabs.TabLayout
        android:id="@+id/my_tab"
        android:layout_width="match_parent"
        android:layout_height="60dp"/>
    <androidx.viewpager2.widget.ViewPager2
        android:id="@+id/my_pager2"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

MainActivity

package com.wmc.test;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import androidx.viewpager2.widget.ViewPager2;

import android.os.Bundle;
import android.widget.Button;

import com.google.android.material.tabs.TabLayout;
import com.google.android.material.tabs.TabLayoutMediator;

import java.util.ArrayList;
import java.util.List;

public class MainActivity extends AppCompatActivity {
    private TabLayout myTab;
    private ViewPager2 myPager2;

    List<String> titles=new ArrayList<>();
    List<Fragment> fragments=new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myTab = findViewById(R.id.my_tab);
        myPager2 = findViewById(R.id.my_pager2);

        //添加标题
        titles.add("1");
        titles.add("2");

        //添加Fragment进去
        fragments.add(new MyFragment());
        fragments.add(new Fragment());

        //实例化适配器
        MyAdapter myAdapter=new MyAdapter(getSupportFragmentManager(),getLifecycle(),fragments);
        //设置适配器
        myPager2.setAdapter(myAdapter);
        //TabLayout和Viewpager2进行关联
        new TabLayoutMediator(myTab, myPager2, new TabLayoutMediator.TabConfigurationStrategy() {
            @Override
            public void onConfigureTab(@NonNull TabLayout.Tab tab, int position) {
                tab.setText(titles.get(position));
            }
        }).attach();

    }
}

MyAdapter

在package com.wmc.test;

import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;

import java.util.List;

public class MyAdapter extends FragmentStateAdapter {
    List<Fragment> fragments;
    public MyAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments) {
        super(fragmentManager, lifecycle);
        this.fragments = fragments;
    }

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

    @Override
    public int getItemCount() {
        return fragments.size();
    }
}

MyFragment

public class MyFragment extends Fragment {
    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View inflate = inflater.inflate(R.layout.myfragment, null);
        return inflate;
    }
}

myfragment.xml

<?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"
    android:background="#FF9800"
    android:orientation="vertical">
</LinearLayout>

在这里插入图片描述

参考

1、https://blog.csdn.net/willway_wang/article/details/88725392
2、https://blog.csdn.net/javazejian/article/details/52138962
3、https://blog.csdn.net/wangjinyu501/article/details/8169924

;