輪播滾動視圖:ViewFlipper
輪播視圖 ViewFlipper 是 Android 從第一個版本就開始提供的 UI 控件,它能夠承載多個 View,但一個時機只會有一個 View 展示在屏幕上。通過 ViewFlipper 我們可以實現(xiàn)很多常見的帶有展示類型的功能,類似 Gallery、輪播圖、導(dǎo)航欄、廣告banner等功能,我們可以通過左右滑動、也可以設(shè)置定時自動滾動來切換 View。
1. ViewFlipper 的特性
ViewFlipper 是 FrameLayout 的子類,我們可以向 ViewFlipper 中插入一個或多個 View,而由于它是直接繼承自 ViewAnimator,從名字上我們可以猜到,它可以對我們插入的多個 View 做各種動畫效果,最常見的效果就是對圖片做定時輪播。
2. ViewFlipper 的基本用法
在第 1 小節(jié)我們提到過,ViewFlipper 擁有兩大特性:可以插入 View 并且支持對插入的 View 添加動畫,這兩個特性都可以采用布局和代碼的形式進行設(shè)置,下面分別做介紹。
2.1 ViewFlipper 的常用屬性
- android:inAnimation:
設(shè)置 View 進入屏幕的動畫效果,默認(rèn)采用系統(tǒng)動畫。 - android:outAnimation:
設(shè)置 View 離開屏幕時的動畫效果,默認(rèn)采用系統(tǒng)動畫。 - android:flipInterval:
設(shè)置各個 View 切換的時間間隔。
2.2 ViewFlipper 的常用API
對于 ViewFlipper 如果只是簡單的滾動,用屬性靜態(tài)設(shè)置是非常方便的。但實際開發(fā)中很多場景需要配合一些業(yè)務(wù)邏輯控制,所以大多數(shù)時候我們會用 API 來控制翻滾。
- setInAnimation:
設(shè)置入場動畫,類似屬性android:inAnimation
。 - setOutAnimation:
設(shè)置出場動畫,類似屬性android:outAnimation
。 - showNext:
展示 ViewFlipper 里的下一個 View。 - showPrevious:
展示 ViewFlipper 里的上一個 View。 - setFilpInterval:
設(shè)置各個 View 切換的時間間隔。 - startFlipping:
為 ViewFlipping 的所有子 View 啟動一個定時器控制輪播。 - stopFlipping:
停止 ViewFlipping 切換。 - setAutoStart:
是否自動啟動,設(shè)置成 true 則會在 ViewFlipping attach 到 Window 的時候自動調(diào)用startFlipping
啟動輪播。
3. ViewFlipper 使用示例
本節(jié)我們會實現(xiàn)一個 3 屏輪流滾動的功能,支持 View 之間自動滾動并通過監(jiān)聽 MotionEvent 來支持左右滑動切換。
3.1 布局文件
首先我們來通過 xml 編寫布局文件,根據(jù)上面的需求要在 ViewFlipper 中放入 3 個布局。我們用一個 RelativeLayout 作為一個 ViewFlipper 的子 View 占滿一屏內(nèi)容,只需要包含三個這樣的 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="第一屏:好好學(xué)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="第二屏:在慕課網(wǎng)好好學(xué)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="第三屏:在慕課網(wǎng)跟著超哥好好學(xué)Android"
android:textSize="18dp"
android:textStyle="bold" />
</RelativeLayout>
</ViewFlipper>
</RelativeLayout>
可以看到其實 ViewFlipper 的子 View 和我們平時開發(fā)時用到的布局沒有什么差別,然后將每一屏的內(nèi)容作為 ViewFlipper 的子 View 添加進去即可。
3.2 編寫入場和出場動畫
細(xì)心的朋友們應(yīng)該會注意到,在布局文件的<ViewFlipper/>
標(biāo)簽中有這么兩個屬性:
android:inAnimation="@anim/in_from_right"
android:outAnimation="@anim/out_from_left"
根據(jù) 2.1 小節(jié)對屬性的介紹,我們知道這是用來設(shè)置出場和入場動畫的,所以接下來我們就來完善這兩個動畫效果,首先按照以下步驟創(chuàng)建一個動畫資源:
- 右鍵點擊“res”目錄,一次選擇“New” -> “Android Resource Directory”
- 在“Resource type”中選擇“anim”,點“OK”,創(chuàng)建一個動畫資源目錄
- 此時“res”目錄下就有了“anim”文件夾,在里面創(chuàng)建四個動畫文件:
- in_from_left.xml:
表示從左側(cè)進入的動畫,代碼如下:
<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/>
標(biāo)簽表示一個 TranslateAnimation,專門用來實現(xiàn)移動補間動畫,具體關(guān)于動畫的用法在后面的動畫專題章節(jié)有詳細(xì)的講解,這里我們關(guān)注 ViewFlipper,對動畫只需要做一點了解即可。
其余三個動畫都類似:
- **in_from_right.xml:**表示從右側(cè)進入,代碼如下:
<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:**從左側(cè)移出:
<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:**從右側(cè)移出:
<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
我們要實現(xiàn) ViewFlipper 的滑動切換,也就是在用戶左滑的時候切換到下一頁,而右滑切換到上一頁。所以在 MainActivity 里面主要做的是獲取 ViewFlipper 對象,然后監(jiān)聽用戶的滑動手勢從而設(shè)置相應(yīng)的出場 / 入場動畫,最后調(diào)用showNext()
或showPrevious()
來最終實現(xiàn)上下頁切換的效果,整體代碼如下:
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:
// 記錄滑動初始坐標(biāo)
initialX = touchEvent.getX();
break;
case MotionEvent.ACTION_UP:
// 記錄滑動結(jié)束坐標(biāo)
float finalX = touchEvent.getX();
if (initialX > finalX) {
// 初始坐標(biāo)大于結(jié)束坐標(biāo),說明為左滑,則播放下一頁
if (mViewFlipper.getDisplayedChild() != 2) {
mViewFlipper.setInAnimation(mContext, R.anim.in_from_right);
mViewFlipper.setOutAnimation(mContext, R.anim.out_from_left);
mViewFlipper.showNext();
}
} else {
// 初始坐標(biāo)不大于結(jié)束坐標(biāo),說明為右滑,則播放上一頁
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 被觸摸的時候會回調(diào)此函數(shù),我們在MotionEvent.ACTION_DOWN
的時候記錄觸摸時的坐標(biāo),然后在MotionEvent.ACTION_UP
的時候記錄抬起時的坐標(biāo),根據(jù)觸摸起始和結(jié)束坐標(biāo)的差值我們能夠推斷出用戶是左滑還是右滑,從而播放下一頁或者上一頁。
3.4 自動輪播
第 2 小節(jié)介紹了一個API:setAutoStart()
,它是用來實現(xiàn)自動播放的,所以我們可以給 ViewFlipper 加上自動輪播的功能。
為了控制自動播放和停止,在布局代碼中我們加入兩個 Button,樣式可以直接借用系統(tǒng)播放器的兩個資源文件:@android:drawable/ic_media_play
和@android:drawable/ic_media_pause
,從名字可以看出這是“播放”和“停止”兩個按鈕,直接在activity_main.xml
中根布局<RelativeLayout/>
標(biāo)簽的最后加入以下布局代碼:
<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 代碼中監(jiān)聽這兩個 Button 的點擊事件,在點擊播放的時候自動翻下一頁,對應(yīng)的動畫就是右邊進入和左邊退出,即in_from_right
和out_from_left
。我們可以在布局文件中的<ViewFlipper/>
標(biāo)簽中加入
android:inAnimation="@anim/in_from_right"
android:outAnimation="@anim/out_from_left"
或者在 Java 代碼中通過
mViewFlipper.setInAnimation();
mViewFlipper.setOutAnimation();
設(shè)置入場和出場動畫,最終在 MainActivity 的 onCreate()
函數(shù)的末尾添加如下 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();
}
});
運行之后點擊播放即可實現(xiàn)自動翻頁,效果如下:
4. 小結(jié)
本節(jié)學(xué)習(xí)了一個很方便實現(xiàn)幻燈片、輪播圖的控件:ViewFlipper,在運營活動、廣告 Banner 等場景非常常見,可以通過 xml 靜態(tài)或者 API 動態(tài)設(shè)置動畫及翻轉(zhuǎn)時間間隔,也可以主動觸發(fā)上翻和下翻動作。大家可以自己思考一下,如果不用 ViewFlipper,只用前面講的基礎(chǔ)布局如何實現(xiàn)這個效果。