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