章节索引 :

视频页面:ViewPager

ViewPager 是一种可以让用户通过左右滑动来切换页面的控件,通过它我们可以展示超过屏幕尺寸大小的内容,在某种程度上它可以说是实现多页面的最佳方式,同时 ViewPager 还支持任意动态的添加/删除页面。比如我们可以将不同的类别的内容分别放在不同页面当中,然后通过滑动切换不同的类别从而给用户展示不同的页面,这个在类似百度App等新闻类App中非常适用。在 ViewPager 中插入“娱乐”、“国际”、“体育”、“星座”等等新闻类别,然后在不同的 View 中展示不同的新闻内容,还可以根据用户的喜好动态增加/删除某些页面,接下来就一起来看看如何完成多页视图。

1. ViewPager 的特性

大家在使用 Android 手机的时候一定都见过下图的效果:

图片描述

没错,这个就是今天的主角——ViewPager 了。在实际开发过程中,我们大多数时候会采用 Fragment 来展示一个页面而不会直接采用 View,在后面的章节学完 Fragment 之后就会知道,Fragment 可以封装 UI 和逻辑,并且会维护自己的生命周期,所以通过 Fragment 我们可以实现更丰富生动的效果,当然对于 ViewPager 的使用而言其实二者几乎没什么差别,我们现在还是把重点放在 ViewPager 上,在后面学完 Fragment 之后只需要做一些简单的改动即可将 View 替换成 Fragment。ViewPager 和前面所学的 ListView/GridView 类似,也需要一个适配器来完成数据的适配,不同的是 ViewPager 有一个专门的适配器——PagerAdapter,所以我们很多的工作也是围绕着 PagerAdapter 展开。

2. PagerAdapter 的使用方法

类似前面所讲的 BaseAdapter 的四个回调接口(不记得的同学可以返回前面章节回顾一下),PagerAdapter 同样也有类似的回调接口,如下:

  • public Object instantiateItem(ViewGroup container, int position):
    根据传入的 position 创建一个 Page,适配器需要在这个回调里网 container 里面添加 View,如下:
public Object instantiateItem(ViewGroup container, int position) {
    View itemView = mLayoutInflater.inflate(R.layout.view_pager, container, false);

    TextView textView = itemView.findViewById(R.id.TextView);
    textView.setText("Imooc Android");
    container.addView(itemView);

    return itemView;
 }
  • public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object):
    根据传入的 position 移除一个 Page
public void destroyItem(ViewGroup container, int position, Object object) {
     container.removeView((View)object);
}
  • gpublic int getCount():
    返回当前 ViewPager 中可用 View 的数量
public int getCount() {
    return mList.length;
}
  • public boolean isViewFromObject(@NonNull View view, @NonNull Object object):
    通过instantiateItem()返回的对象可以看做是一个 key,这个方法用来判断传入的 View 是否是之前创建的 key,如下:
public boolean isViewFromObject(View view, Object object) {
    return view == object;
}

常用的一般就是以上四种回调方法,理解起来都比较简单,其中要注意的是getCountisViewFromObject这两个是必须实现的,而instantiateItem()destroyItem()是可选的,不过大多数场景还是推荐大家实现 4 个回调方法。

3. ViewPager 完整示例

本节将通过一个简单的例子学习 ViewPager 的使用,每一个 Page 将会通过一个 View 来实现(在学习了 Fragment 之后,可以尝试将 View 替换成 Fragment)。例子中的每一个 Page 表示一种类别,在切换过程中我们会接收切换的状态回调,同步更新类别标题。

3.1 整体布局

由于每个页面都在 ViewPager 中,所以整体的布局非常简单,我们只需要放置一个 ViewPager 以及一个 TextView 用来显示当前 Page 的标题即可。

<?xml version="1.0" encoding="utf-8"?>
<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:showIn="@layout/activity_main">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

    <TextView
        android:text="Num"
        android:textSize="100sp"
        android:id="@+id/text"
        android:layout_marginTop="50dp"
        android:layout_centerHorizontal="true"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
</RelativeLayout>

3.2 Page 页面的布局编写

对于整体布局而言,主要的页面都在 ViewPager 当中,所以我们需要为不同结构的 Page 编写不同的页面,由于本例中每个 Page 的页面结构都一样,所以可以直接复用一套,直接在里面放置一个 ImageView 用于展示类别图片。

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

3.3 适配器的编写

适配这一块主要就是对四个回调接口的实现,其实在第 2 小节的描述中已经展示了各个方法的实现方式。

package com.emercy.myapplication;

import android.view.View;
import android.view.ViewGroup;

import androidx.annotation.NonNull;
import androidx.viewpager.widget.PagerAdapter;

import java.util.List;

public class MyAdapter extends PagerAdapter {

    private final List<View> mView;

    public MyAdapter(List<View> view) {
        mView = view;
    }

    @NonNull
    @Override
    public Object instantiateItem(@NonNull ViewGroup container, int position) {
        container.addView(mView.get(position));
        return mView.get(position);
    }

    @Override
    public void destroyItem(@NonNull ViewGroup container, int position, @NonNull Object object) {
        container.removeView(mView.get(position));
    }

    @Override
    public int getCount() {
        return mView.size();
    }

    @Override
    public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
        return view == object;
    }
}

3.4 MainActivity 主逻辑编写

主逻辑主要的任务就是将前面的布局都用上,并通过 Adapter 将数据和布局串联起来,所以我们需要获取到 ViewPager 中每个 View 的实例,设置类别之后传递给 Adpater,绑定的任务就交由Adpater完成。接下来再监听 ViewPager 的滑动状态从而判断当前切换的位置,从而实现同步更新类别标题,整体代码如下:


package com.emercy.myapplication;

import android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import android.widget.ViewFlipper;

import androidx.viewpager.widget.ViewPager;

import org.w3c.dom.Text;

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


public class MainActivity extends Activity {

    private ViewPager mViewPager;
    private String[] mTitle = new String[]{"苹果", "香蕉", "荔枝"};
    private List<View> mView = new ArrayList<>();

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

        final TextView tv = findViewById(R.id.text);
        mViewPager = findViewById(R.id.view_pager);
        mViewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                tv.setText(mTitle[position]);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

        ImageView view1 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
        view1.setBackgroundColor(Color.RED);
        view1.setImageResource(R.drawable.apple);
        mView.add(view1);

        ImageView view2 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
        view2.setBackgroundColor(Color.GREEN);
        view2.setImageResource(R.drawable.banana);
        mView.add(view2);

        ImageView view3 = (ImageView) LayoutInflater.from(this).inflate(R.layout.list_item, null);
        view3.setBackgroundColor(Color.BLUE);
        view3.setImageResource(R.drawable.lychee);
        mView.add(view3);

        mViewPager.setAdapter(new MyAdapter(mView));
        tv.setText(mTitle[0]);
    }
}

编译之后效果如下:

图片描述

通过左右滑动可以切换不同的页面,每个页面对应的一种水果类别,这样就通过 ViewPager 实现了一个简单的页面切换效果。

4. 小节

本节介绍了 ViewPager 的特点及使用场景,并讲解了 ViewPager 的专属适配器——PagerAdapter 的几个回调函数的使用方法,最后采用 ViewPager 实现了一个简单的例子用于切换不同的页面从而展示不同的类别。这一节中是直接采用 View 来承载每一个 Page,而在实际开发中大多数场景会采用 Fragemnt 来承载 Page,不过对于 ViewPager 的使用到同小异,针对 Fragment 系统提供了两种 Adapter:FragmentPageAdapterFragmentStatePagerAdapter,在学完 Fragement之后大家可以自行修改本节的例子,通过 Fragment 来实现本例的效果。

环境搭建,开发相关
Android 系统背景及结构概述 Android 开发环境搭建 Genymotion 的安装与使用 Android 工程解析及使用 Android 程序签名打包
常用 UI 布局
Android 的 UI 根基 View与View Android 线性布局 LinearLayout Android相对布局RelativeLayout Android 表格布局 TableLayout Android 网格布局 GridLayout Android 帧布局 FrameLayout Android绝对布局AbsoluteLayout
基础控件
Android 文本框 TextView Android 文本输入框 EditText 按钮 Button/ImageButton 选择框 RadioButton/Check 开关控件ToggleButton/Switch Android 图片控件 ImageView Android 进度条 ProgressBar Android 拖动条 SeekBar Android 评分条 RatingBar Android 滚动条 ScrollView 轮播滚动视图 ViewFlipper
Adapter 相关控件
Android 适配器 Adapter Android 列表控件 ListView Android 网格视图 GridView Android 下拉选择框 Spinner 自动补全文本框 AutoCompleteText 折叠列表 ExpandableListView
提示类控件
吐司提示:Toast 的使用方法 状态栏通知:Notification 对话框:AlertDialog 悬浮窗:PopupWindow
菜单类控件
菜单:Menu
其他控件
视频页面:ViewPager 侧滑菜单:DrawerLayout
事件处理机制
基于监听的事件处理机制 Handler 消息传递机制 触摸事件分发处理 AsyncTask:异步任务 Android 手势处理
Android 四大组件
活动:Activity 服务:Service 广播接收器:Broadcast Receiver 内容提供者 - Content Provider
数据存储
文件存储 SharedPreferences 存储 数据库:SQLite 的使用
网络编程
HTTP 使用详解 xml 数据解析 JSON 数据解析 网页视图:WebView Socket 网络接口
绘图与动画
图片资源:Drawable 位图:Bitmap
多媒体开发
媒体播放器:MediaPlayer 相机:Camera 音频录制:MediaRecorder
并发编程
多线程