• 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的代码非常简单,这里就不一一列举。

  • 相关阅读:
    python2文件转换为exe可执行文件
    pycharm下 os.system os.popen执行命令返回有中文乱码
    python 虚拟环境
    git commit之后,想撤销commit
    Android Dialog使用举例
    Builder模式在Java中的应用(转)
    AngularJS promise()
    给你一个承诺
    AngularJs 用户登录验证模块(demo)参考总结
    推荐 15 个 Angular.js 应用扩展指令(参考应用)
  • 原文地址:https://www.cnblogs.com/pengjingya/p/5509964.html
Copyright © 2020-2023  润新知