跳到主要内容

轮播滚动视图:ViewFlipper

轮播视图 ViewFlipper 是 Android 从第一个版本就开始提供的 UI 控件,它能够承载多个 View,但一个时机只会有一个 View 展示在屏幕上。通过 ViewFlipper 我们可以实现很多常见的带有展示类型的功能,类似 Gallery、轮播图、导航栏、广告banner等功能,我们可以通过左右滑动、也可以设置定时自动滚动来切换 View。

1. ViewFlipper 的特性

ViewFlipper 是 FrameLayout 的子类,我们可以向 ViewFlipper 中插入一个或多个 View,而由于它是直接继承自 ViewAnimator,从名字上我们可以猜到,它可以对我们插入的多个 View 做各种动画效果,最常见的效果就是对图片做定时轮播。

2. ViewFlipper 的基本用法

在第 1 小节我们提到过,ViewFlipper 拥有两大特性:可以插入 View 并且支持对插入的 View 添加动画,这两个特性都可以采用布局和代码的形式进行设置,下面分别做介绍。

2.1 ViewFlipper 的常用属性

  • android:inAnimation:

设置 View 进入屏幕的动画效果,默认采用系统动画。

  • android:outAnimation:

设置 View 离开屏幕时的动画效果,默认采用系统动画。

  • android:flipInterval:

设置各个 View 切换的时间间隔。

2.2 ViewFlipper 的常用API

对于 ViewFlipper 如果只是简单的滚动,用属性静态设置是非常方便的。但实际开发中很多场景需要配合一些业务逻辑控制,所以大多数时候我们会用 API 来控制翻滚。

  • setInAnimation:

设置入场动画,类似属性android:inAnimation

  • setOutAnimation:

设置出场动画,类似属性android:outAnimation

  • showNext:

展示 ViewFlipper 里的下一个 View。

  • showPrevious:

展示 ViewFlipper 里的上一个 View。

  • setFilpInterval:

设置各个 View 切换的时间间隔。

  • startFlipping:

为 ViewFlipping 的所有子 View 启动一个定时器控制轮播。

  • stopFlipping:

停止 ViewFlipping 切换。

  • setAutoStart:

是否自动启动,设置成 true 则会在 ViewFlipping attach 到 Window 的时候自动调用startFlipping启动轮播。

3. ViewFlipper 使用示例

本节我们会实现一个 3 屏轮流滚动的功能,支持 View 之间自动滚动并通过监听 MotionEvent 来支持左右滑动切换。

3.1 布局文件

首先我们来通过 xml 编写布局文件,根据上面的需求要在 ViewFlipper 中放入 3 个布局。我们用一个 RelativeLayout 作为一个 ViewFlipper 的子 View 占满一屏内容,只需要包含三个这样的 RelativeLayout 即可,代码如下:

<?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">

<ViewFlipper
android:id="@+id/view\_flipper"
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent"
android:inAnimation="@anim/in\_from\_right"
android:outAnimation="@anim/out\_from\_left">

<RelativeLayout
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent">

<ImageView
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent"
android:layout\_gravity="center"
android:adjustViewBounds="true"
android:background="@android:color/black"
android:scaleType="centerCrop" />

<TextView
android:layout\_width="wrap\_content"
android:layout\_height="wrap\_content"
android:layout\_centerHorizontal="true"
android:layout\_centerVertical="true"
android:gravity="center"
android:text="第一屏:好好学Android"
android:textColor="@android:color/white"
android:textSize="18dp"
android:textStyle="bold" />
</RelativeLayout>

<RelativeLayout
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent">

<ImageView
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent"
android:layout\_gravity="center"
android:adjustViewBounds="true"
android:background="@android:color/darker\_gray"
android:scaleType="centerCrop" />

<TextView
android:layout\_width="wrap\_content"
android:layout\_height="wrap\_content"
android:layout\_centerHorizontal="true"
android:layout\_centerVertical="true"
android:gravity="center"
android:text="第二屏:在慕课网好好学Android"
android:textSize="18dp"
android:textStyle="bold" />
</RelativeLayout>

<RelativeLayout
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent">

<ImageView
android:layout\_width="fill\_parent"
android:layout\_height="fill\_parent"
android:layout\_gravity="center"
android:adjustViewBounds="true"
android:background="@android:color/holo\_green\_light"
android:scaleType="centerCrop" />

<TextView
android:layout\_width="wrap\_content"
android:layout\_height="wrap\_content"
android:layout\_centerHorizontal="true"
android:layout\_centerVertical="true"
android:gravity="center"
android:text="第三屏:在慕课网跟着超哥好好学Android"
android:textSize="18dp"
android:textStyle="bold" />
</RelativeLayout>
</ViewFlipper>
</RelativeLayout>

可以看到其实 ViewFlipper 的子 View 和我们平时开发时用到的布局没有什么差别,然后将每一屏的内容作为 ViewFlipper 的子 View 添加进去即可。

3.2 编写入场和出场动画

细心的朋友们应该会注意到,在布局文件的<ViewFlipper/>标签中有这么两个属性:

android:inAnimation="@anim/in_from_right"
android:outAnimation="@anim/out_from_left"

根据 2.1 小节对属性的介绍,我们知道这是用来设置出场和入场动画的,所以接下来我们就来完善这两个动画效果,首先按照以下步骤创建一个动画资源:

  1. 右键点击“res”目录,一次选择“New” -> “Android Resource Directory”

创建资源目录

  1. 在“Resource type”中选择“anim”,点“OK”,创建一个动画资源目录

创建动画目录

  1. 此时“res”目录下就有了“anim”文件夹,在里面创建四个动画文件:
  • in_from_left.xml:

表示从左侧进入的动画,代码如下:

<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="1400"
android:fromXDelta="-100%"
android:toXDelta="0%"/>
</set>

其中<translate/>标签表示一个 TranslateAnimation,专门用来实现移动补间动画,具体关于动画的用法在后面的动画专题章节有详细的讲解,这里我们关注 ViewFlipper,对动画只需要做一点了解即可。

其余三个动画都类似:

  • **in_from_right.xml:**表示从右侧进入,代码如下:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="1400"
android:fromXDelta="100%"
android:fromYDelta="0%" />
</set>

  • **out_from_left:**从左侧移出:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="1400"
android:fromXDelta="0%"
android:toXDelta="-100%"/>
</set>

  • **out_from_right:**从右侧移出:
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="1400"
android:fromXDelta="0%"
android:fromYDelta="0%"
android:toXDelta="100%"
android:toYDelta="0%" />
</set>

3.3 编写 MainActivity

我们要实现 ViewFlipper 的滑动切换,也就是在用户左滑的时候切换到下一页,而右滑切换到上一页。所以在 MainActivity 里面主要做的是获取 ViewFlipper 对象,然后监听用户的滑动手势从而设置相应的出场 / 入场动画,最后调用showNext()showPrevious()来最终实现上下页切换的效果,整体代码如下:

package com.emercy.myapplication;

import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.animation.AnimationUtils;
import android.widget.ViewFlipper;


public class MainActivity extends Activity {

private ViewFlipper mViewFlipper;
private Context mContext;
private float initialX;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mContext = this;
mViewFlipper = findViewById(R.id.view_flipper);
}

@Override
public boolean onTouchEvent(MotionEvent touchEvent) {
switch (touchEvent.getAction()) {
case MotionEvent.ACTION_DOWN:
// 记录滑动初始坐标
initialX = touchEvent.getX();
break;
case MotionEvent.ACTION_UP:
// 记录滑动结束坐标
float finalX = touchEvent.getX();
if (initialX > finalX) {
// 初始坐标大于结束坐标,说明为左滑,则播放下一页
if (mViewFlipper.getDisplayedChild() != 2) {
mViewFlipper.setInAnimation(mContext, R.anim.in_from_right);
mViewFlipper.setOutAnimation(mContext, R.anim.out_from_left);
mViewFlipper.showNext();
}
} else {
// 初始坐标不大于结束坐标,说明为右滑,则播放上一页
if (mViewFlipper.getDisplayedChild() != 0) {
mViewFlipper.setInAnimation(mContext, R.anim.in_from_left);
mViewFlipper.setOutAnimation(mContext, R.anim.out_from_right);
mViewFlipper.showPrevious();
}
}
break;
}
return false;
}
}

在代码中我们覆写了onTouchEvent,在 Activity 中的 View 被触摸的时候会回调此函数,我们在MotionEvent.ACTION_DOWN的时候记录触摸时的坐标,然后在MotionEvent.ACTION_UP的时候记录抬起时的坐标,根据触摸起始和结束坐标的差值我们能够推断出用户是左滑还是右滑,从而播放下一页或者上一页。

ViewFlipper手势滑动

3.4 自动轮播

第 2 小节介绍了一个API:setAutoStart(),它是用来实现自动播放的,所以我们可以给 ViewFlipper 加上自动轮播的功能。

为了控制自动播放和停止,在布局代码中我们加入两个 Button,样式可以直接借用系统播放器的两个资源文件:@android:drawable/ic_media_play@android:drawable/ic_media_pause,从名字可以看出这是“播放”和“停止”两个按钮,直接在activity_main.xml中根布局<RelativeLayout/>标签的最后加入以下布局代码:

   <LinearLayout
android:layout\_width="match\_parent"
android:layout\_height="50dp"
android:layout\_alignParentTop="true"
android:gravity="center"
android:orientation="horizontal">

<Button
android:id="@+id/play"
android:layout\_width="50dp"
android:layout\_height="50dp"
android:layout\_marginRight="10dp"
android:background="@android:drawable/ic\_media\_play" />

<Button
android:id="@+id/stop"
android:layout\_width="50dp"
android:layout\_height="50dp"
android:background="@android:drawable/ic\_media\_pause" />
</LinearLayout>

在 Java 代码中监听这两个 Button 的点击事件,在点击播放的时候自动翻下一页,对应的动画就是右边进入和左边退出,即in_from_rightout_from_left。我们可以在布局文件中的<ViewFlipper/>标签中加入

android:inAnimation="@anim/in_from_right"
android:outAnimation="@anim/out_from_left"

或者在 Java 代码中通过

mViewFlipper.setInAnimation();
mViewFlipper.setOutAnimation();

设置入场和出场动画,最终在 MainActivity 的 onCreate()函数的末尾添加如下 Java 代码:

findViewById(R.id.play).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mViewFlipper.setAutoStart(true);
mViewFlipper.setInAnimation(mContext, R.anim.in_from_right);
mViewFlipper.setOutAnimation(mContext, R.anim.out_from_left);
mViewFlipper.setFlipInterval(2000);
mViewFlipper.startFlipping();
Toast.makeText(MainActivity.this,
"启动自动播放", Toast.LENGTH_SHORT).show();
}
});


findViewById(R.id.stop).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
mViewFlipper.stopFlipping();
Toast.makeText(MainActivity.this,
"停止自动播放", Toast.LENGTH_SHORT).show();
}
});

运行之后点击播放即可实现自动翻页,效果如下:

ViewFlipper自动播放

4. 小结

本节学习了一个很方便实现幻灯片、轮播图的控件:ViewFlipper,在运营活动、广告 Banner 等场景非常常见,可以通过 xml 静态或者 API 动态设置动画及翻转时间间隔,也可以主动触发上翻和下翻动作。大家可以自己思考一下,如果不用 ViewFlipper,只用前面讲的基础布局如何实现这个效果。