zoukankan      html  css  js  c++  java
  • 22、Android--ViewPager

    ViewPager

    ViewPager是android扩展包v4包中的类,这个类可以让用户左右切换当前的view。我们首先来看看API对于这个类的表述:

    ViewPager类直接继承了ViewGroup类,所有它是一个容器类,可以在其中添加其他的view类。
    ViewPager类需要一个PagerAdapter适配器类给它提供数据。
    ViewPager经常和Fragment一起使用,并且提供了专门的FragmentPagerAdapter、FragmentStatePagerAdapter类来供Fragment中的ViewPager使用。

    基本使用

    1、在布局文件中定义ViewPager,由于早期是在V4兼容包中提供的(过时),现在推荐使用Androix包下的ViewPager。

    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
    修改为
    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center" />
    

    2、创建三个Layout文件,用于滑动切换的视图显示:

    layout01.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="#ffffff"
        android:orientation="vertical">
    </LinearLayout>
    

    layout02.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="#ffff00"
        android:orientation="vertical">
    </LinearLayout>
    

    layout03.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="#ff00ff"
        android:orientation="vertical">
    </LinearLayout>
    

    3、编写自定义的MyPageAdapter继承自PageAdapter

    class MyPageAdapter extends PagerAdapter{
        private List<View> viewList;
    
        public MyPageAdapter(List<View> viewList) {
            this.viewList = viewList;
        }
    
        // 返回要滑动的VIew的个数
        @Override
        public int getCount() {
            return viewList.size();
        }
    
        // 判断pager的一个view是否和instantiateItem方法返回的object有关联
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    
        // 将当前视图添加到container中,然后返回当前View
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(viewList.get(position));
            return viewList.get(position);
        }
    
        // 从当前container中删除指定位置(position)的View
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(viewList.get(position));
        }
    }
    

    4、在Activity中构建数据,并设置适配器即可

    public class MainActivity extends AppCompatActivity {
        private ViewPager mViewPager;
        private View mView1, mView2, mView3;
        private List<View> mViewList;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mViewPager = findViewById(R.id.viewpager);
            // 构建布局
            mView1 = View.inflate(this, R.layout.layout01, null);
            mView2 = View.inflate(this,R.layout.layout02,null);
            mView3 = View.inflate(this,R.layout.layout03, null);
            // 将要分页显示的View装入数组中
            mViewList = new ArrayList<>();
            mViewList.add(mView1);
            mViewList.add(mView2);
            mViewList.add(mView3);
            MyPageAdapter adapter = new MyPageAdapter(mViewList);
            mViewPager.setAdapter(adapter);
        }
    }
    

    PageAdapter

    在使用PagerAdapter的时候,至少需要覆写如下几个方法:

    instantiateItem(ViewGroup, int):将当前视图添加到container中,然后返回当前View
    destroyItem(ViewGroup, int, Object):从当前container中删除指定位置的View
    getCount():返回要滑动的View的个数
    isViewFromObject(View, Object):判断pager的一个view是否和instantiateItem方法返回的object有关联

    每个滑动页面都对应一个Key,而且这个Key值是用来唯一追踪这个页面的,也就是说每个滑动页面都与一个唯一的Key对应。可以将当前页面本身的View作为Key,也可以自定义的方式来实现Key。

    自定义Key

    由于Key与View要一一对应,所以我把每个视图所处的位置Position作为Key来实现自定义Key。

    class MyPageAdapter extends PagerAdapter{
        private List<View> viewList;
    
        public MyPageAdapter(List<View> viewList) {
            this.viewList = viewList;
        }
    
        // 返回要滑动的VIew的个数
        @Override
        public int getCount() {
            return viewList.size();
        }
    
        // 判断pager的一个view是否和instantiateItem方法返回的object有关联
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == viewList.get((Integer) object);
        }
    
        // 将当前视图添加到container中,然后返回当前View
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(viewList.get(position));
            return position;
        }
    
        // 从当前container中删除指定位置(position)的View
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(viewList.get(position));
        }
    }
    

    由于instantiateItem()方法返回的是position,所以在isViewFromObject()方法中的object则是int类型的position,这样就保持一一对应的关系。

    ViewPager标题

    ViewPager提供两种标题栏的方式:PagerTitleStripPagerTabStrip

    PagerTitleStrip:标题附带的是普通的文字

    PagerTabStrip:标题除了文字外,还带有下划线

    两者的区别仅仅是布局不一样而已,其他的都一样,在开发中一般不使用,这里做简单介绍。

    PagerTitleStrip

    1、创建PagerTitleStrip所在的布局文件:activity_pager_title_strip

    <?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:orientation="vertical">
    
        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center">
            <androidx.viewpager.widget.PagerTitleStrip
                android:id="@+id/pager_title"
                android:layout_width="wrap_content"
                android:layout_gravity="top"
                android:layout_height="40dp"
                android:textColor="#FF0000"/>
        </androidx.viewpager.widget.ViewPager>
    </LinearLayout>
    

    2、创建适配器,需要实现getPageTitle()方法来返回标题的数据信息

    class MyPageAdapter extends PagerAdapter{
        private List<String> titleList;
        private List<View> viewList;
    
        public MyPageAdapter(List<String> titleList, List<View> viewList) {
            this.titleList = titleList;
            this.viewList = viewList;
        }
    
        @Override
        public CharSequence getPageTitle(int position) {
            return titleList.get(position);
        }
    
        @Override
        public int getCount() {
            return viewList.size();
        }
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == viewList.get((Integer) object);
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            container.addView(viewList.get(position));
            return position;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView(viewList.get(position));
        }
    }
    

    3、在Activity中使用的代码如下所示:

    public class MainActivity extends AppCompatActivity {
        private ViewPager mViewPager;
        private View mView1, mView2, mView3;
        private List<View> mViewList;
        private List<String> mTitleList;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_pager_title_strip);
            mViewPager = findViewById(R.id.viewpager);
            // 构建标题数组
            mTitleList = new ArrayList<>();
            mTitleList.add("标题一");
            mTitleList.add("标题二");
            mTitleList.add("标题三");
            // 构建布局
            mView1 = View.inflate(this, R.layout.layout01, null);
            mView2 = View.inflate(this,R.layout.layout02,null);
            mView3 = View.inflate(this,R.layout.layout03, null);
            // 将要分页显示的View装入数组中
            mViewList = new ArrayList<>();
            mViewList.add(mView1);
            mViewList.add(mView2);
            mViewList.add(mView3);
            MyPageAdapter adapter = new MyPageAdapter(mTitleList, mViewList);
            mViewPager.setAdapter(adapter);
        }
    }
    

    注:PagerTabStrip的用法和PagerTitleStrip一样,只是显示效果不同,这里不再演示。

    OnPageChangeListener

    OnPageChangeListener是ViewPager用来实现页面切换的监听器,它包含如下方法:

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
    public void onPageSelected(int position)
    public void onPageScrollStateChanged(int state)

    对于该三个方法的详细叙述如下所示:

    mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
        /**
         * 当页面在滑动的时候会调用此方法,在滑动停止前会一直被调用
         * @param position  当前页面及滑动页面的位置编号
         * @param positionOffset    当前页面偏移的百分比
         * @param positionOffsetPixels  当前页面偏移的像素位置   
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }
    
        /**
         * 页面跳转完后调用
         * @param position 当前选中的页面的的位置编号
         */
        @Override
        public void onPageSelected(int position) {
        }
    
        /**
         * 状态改变的时候调用
         * @param state 有三种状态:0:默认什么也不做;1:表示正在滑动;2:表示滑动完毕
         */
        @Override
        public void onPageScrollStateChanged(int state) {
        }
    });
    

    注意:ViewPager早期使用的setOnPageChangeListener()方法已经过时,并不推荐再使用。

    结合Fragment

    在Android开发中,我们可以使用ViewPager+Fragment的方式来实现界面的横向切换,这样就需要用到如下两个适配器,它们都继承自PageAdapter。

    FragmentPagerAdapter:类中每一个生成的Fragment都将保存在内存中,内存开销大,适合页面较少的静态页面。

    FragmentStatePagerAdapter:每次生成的Fragment只保留在当前页面,当页面离开时,就会被消除,释放其资源。

    FragmentPagerAdapter

    1、在Activity的布局文件中创建ViewPager

    <?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:orientation="vertical">
        <androidx.viewpager.widget.ViewPager
            android:id="@+id/viewpager"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>
    </LinearLayout>
    

    2、创建三个Fragment,并实现三个布局文件

    fragment1的创建

    public class Fragment1 extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View layout = View.inflate(getActivity(), R.layout.fragment_one, null);
            return layout;
        }
    }
    

    使用到的布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="20sp"
            android:text="Fragment1"/>
    </RelativeLayout>
    

    fragment2的创建

    public class Fragment2 extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View layout = View.inflate(getActivity(), R.layout.fragment_two, null);
            return layout;
        }
    }
    

    使用到的布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="20sp"
            android:text="Fragment2"/>
    </RelativeLayout>
    

    fragment3的创建

    public class Fragment3 extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
            View layout = View.inflate(getActivity(), R.layout.fragment_three, null);
            return layout;
        }
    }
    

    使用到的布局文件如下:

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="20sp"
            android:text="Fragment3"/>
    </RelativeLayout>
    

    3、创建三个Fragment后,编写一个类MyFragmentPageAdapter继承自FragmentPageAdapter

    class MyFragmentPageAdapter extends FragmentPagerAdapter{
        private List<Fragment> mFragments;
        public MyFragmentPageAdapter(FragmentManager fm, List<Fragment> mFragments) {
            super(fm);
            this.mFragments = mFragments;
        }
    
        @Override
        public Fragment getItem(int position) {
            return mFragments.get(position);
        }
    
        @Override
        public int getCount() {
            return mFragments.size();
        }
    }
    

    这里需要注意,FragmentPageAdapter有两个构造函数,它们分别如下:

    public FragmentPagerAdapter( FragmentManager fm) :在API 27之后已经过时,其实也是调用第二个构造函数。

    public FragmentPagerAdapter(FragmentManager fm, int behavior):推荐使用,增加了behavior参数,用来实现懒加载。

    其中第二个构造函数的behavior的取值如下:

    FragmentPagerAdapter.BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT:解决ViewPager和Fragment生命周期的缺陷问题
    

    4、最后是Activity中代码的实现如下:

    public class MainActivity extends AppCompatActivity {
        private ViewPager mViewPager;
        private List<Fragment> mFragments;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
            mViewPager = findViewById(R.id.viewpager);
            mFragments = new ArrayList<>();
            mFragments.add(new Fragment1());
            mFragments.add(new Fragment2());
            mFragments.add(new Fragment3());
            MyFragmentPageAdapter adapter = new MyFragmentPageAdapter(getSupportFragmentManager(), mFragments);
            mViewPager.setAdapter(adapter);
        }
    }
    

    注:FragmentStatePagerAdapter的使用方式和FragmentPagerAdapter完全一致,这里不再叙述。

    ViewPager2

    Google在androidX组件包中新增了ViewPager2组件,它是用来替换之前的ViewPager的。它包含如下特性:

    1、能够关闭用户输入(setUserInputEnabled isUserInputEnabled
    2、支持RTL布局(视图从左到右显示),支持横向和纵向的滑动以及notifyDataSetChanged刷新视图

    其中API的变更如下所示:

    1、FragmentStateAdapter 替代 FragmentStatePagerAdapter
    2、RecyclerView.Adapter 替代 PagerAdapter
    3、registerOnPageChangeCallback 替代 addPageChangeListener

    初步了解ViewPager2后,我们要使用它需要添加相关的依赖:

    implementation "androidx.viewpager2:viewpager2:1.0.0-alpha02"

    横向和纵向滑动

    上面以及介绍了ViewPager2支持横向和纵向的滑动,那么我们先来看看横向的滑动。

    1、在Activity的布局中声明ViewPager2

    <?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">
        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/viewpager2"
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>
    

    2、建立简单的item的布局item_page.xml

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/rl_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <TextView
            android:id="@+id/tv_content"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerInParent="true"
            android:textSize="20sp"
            android:text="ViewPager2"/>
    </RelativeLayout>
    

    3、建立数据适配器ViewPagerAdapter.java

    class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewPagerViewHolder{
        private Context mContext;
        private List<Integer> mColors;
    
        public ViewPagerAdapter(Context context, List<Integer> colors) {
            this.mContext = context;
            this.mColors = colors;
        }
    
        @Override
        public ViewPagerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View layout = LayoutInflater.from(mContext).inflate(R.layout.item_page, parent, false);
            ViewPagerViewHolder viewHolder = new ViewPagerViewHolder(layout);
            return viewHolder;
        }
    
        @Override
        public void onBindViewHolder(ViewPagerViewHolder holder, int position) {
            holder.tvContent.setText("item " + position);
            holder.rlContainer.setBackgroundResource(mColors.get(position));
        }
    
        @Override
        public int getItemCount() {
            return mColors.size();
        }
    
        class ViewPagerViewHolder extends RecyclerView.ViewHolder {
            RelativeLayout rlContainer;
            TextView tvContent;
            public ViewPagerViewHolder(View itemView) {
                super(itemView);
                rlContainer = itemView.findViewById(R.id.rl_container);
                tvContent = itemView.findViewById(R.id.tv_content);
            }
        }
    }
    

    注:如果在使用过程中出现Pages must fill the whole ViewPager2 (use match_parent)错误,可以使用如下代码来填充布局:

    View layout = View.inflate(mContext, R.layout.item_page, null);
    ViewGroup.LayoutParams layoutParams = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
    		ViewGroup.LayoutParams.MATCH_PARENT);
    layout.setLayoutParams(layoutParams);
    

    4、Activity中的代码如下所示:

    public class ViewPagerActivity extends AppCompatActivity {
        private List<Integer> mColors;
        private ViewPager2 mViewPager2;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_view_pager);
            mViewPager2 = findViewById(R.id.viewpager2);
            mColors = new ArrayList<>();
            mColors.add(R.color.white);
            mColors.add(R.color.black);
            mColors.add(R.color.purple_200);
            ViewPagerAdapter adapter = new ViewPagerAdapter(this, mColors);
            mViewPager2.setAdapter(adapter);
        }
    }
    

    运行后就可以横向滑动了,由于默认是横向滑动,如果想要实现纵向滑动只需要设置ViewPager2的方向即可:

    viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
    

    registerOnPageChangeCallback

    registerOnPageChangeCallback是ViewPager2用来实现页面切换的监听器,它包含如下方法:

    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
    public void onPageSelected(int position)
    public void onPageScrollStateChanged(int state)

    对于该三个方法的详细叙述如下所示:

    mViewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
        /**
         * 当页面在滑动的时候会调用此方法,在滑动停止前会一直被调用
         * @param position  当前页面及滑动页面的位置编号
         * @param positionOffset    当前页面偏移的百分比
         * @param positionOffsetPixels  当前页面偏移的像素位置
         */
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        }
    
        /**
         * 页面跳转完后调用
         * @param position 当前选中的页面的的位置编号
         */
        @Override
        public void onPageSelected(int position) {
        }
    
        /**
         * 状态改变的时候调用
         * @param state 有三种状态:0:默认什么也不做;1:表示正在滑动;2:表示滑动完毕
         */
        @Override
        public void onPageScrollStateChanged(int state) {
        }
    });
    

    监听器除了方法名之外,基本用法和ViewPager一样。

    FragmentStateAdapter

    FragmentStateAdapter有三个构造方法,它们分别是:

    public FragmentStateAdapter(FragmentActivity fragmentActivity)
    public FragmentStateAdapter(Fragment fragment)
    public FragmentStateAdapter(FragmentManager fragmentManager, Lifecycle lifecycle)

    FragmentStateAdapter在使用上和FragmentStatePagerAdapter一样,所以它们的特性也是一样的。

    1、编写MyFragmentStateAdapter适配器,代码如下:

    class MyFragmentStateAdapter extends FragmentStateAdapter {
        private List<Fragment> mFragments;
        public MyFragmentStateAdapter(FragmentActivity fragmentActivity, List<Fragment> fragments) {
            super(fragmentActivity);
            this.mFragments = fragments;
        }
    
        @Override
        public Fragment createFragment(int position) {
            return mFragments.get(position);
        }
    
        @Override
        public int getItemCount() {
            return mFragments.size();
        }
    }
    

    2、在Activity中使用的时候,当前Activity需要继承自FragmentActivity,在构建适配器的时候需要用到:

    public class ViewPagerActivity extends FragmentActivity {
        private ViewPager2 mViewPager2;
        private List<Fragment> mFragments;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_view_pager);
            mViewPager2 = findViewById(R.id.viewpager2);
            mFragments = new ArrayList<>();
            mFragments.add(new Fragment1());
            mFragments.add(new Fragment2());
            mFragments.add(new Fragment3());
            MyFragmentStateAdapter adapter = new MyFragmentStateAdapter(this, mFragments);
            mViewPager2.setAdapter(adapter);
        }
    }
    

    其中Fragment1、Fragment2、Fragment3的代码非常简单,这里就不一一列举。

  • 相关阅读:
    简单明了的带你理解springboot原理和三大核心注解
    Spring Boot(一):入门篇
    【Mysql优化】聚簇索引与非聚簇索引概念
    Mysql索引原理与优化
    Mysql全文索引的使用
    索引的优缺点,如何创建索引
    184 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 04 例:字符串与byte(即:字节)数组间的相互转换
    183 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 03 String常用方法(下)
    182 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 02 String常用方法(上)
    181 01 Android 零基础入门 03 Java常用工具类03 Java字符串 02 String类 01 String常用方法简介
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5509964.html
Copyright © 2011-2022 走看看