前面已經講過ImageSwitcher和TextSwitcher。ImageSwitcher用來切換ImageView的,TextSwitcher是用來切換TextView的。
但是我們現在要切換自定義View怎麼辦?
ImageSwitcher和TextSwitcher已經不能滿足我們的需求。ViewFlipper可以在任意View之間切換。下面我們就來講解它。
先看一下結構圖
可以看到ViewSwitcher和ViewFlipper都是繼承自ViewAnimator。
下面通過一個Demo瞭解一下ViewFlipper的用法
main.xml
[html] <?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<ViewFlipper
android:id="@+id/viewFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<include
android:id="@+id/layout01"
layout="@layout/layout01" />
<include
android:id="@+id/layout02"
layout="@layout/layout02" />
</ViewFlipper>
</LinearLayout>
layout01.xml
[html]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<TextView
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:text="一個TextView"
android:textSize="40dip" />
</LinearLayout>
layout02.xml
[html]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="一個TextView + 一個ImageView"
android:textSize="20dip" />
</LinearLayout>
</LinearLayout>
ViewFlipperDemoActivity.java
[java]
package com.tianjf;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.animation.AnimationUtils;
import android.widget.ViewFlipper;
public class ViewFlipperDemoActivity extends Activity implements
OnTouchListener {
private ViewFlipper viewFlipper;
// 左右滑動時手指按下的X坐標
private float touchDownX;
// 左右滑動時手指松開的X坐標
private float touchUpX;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
viewFlipper = (ViewFlipper) findViewById(R.id.viewFlipper);
viewFlipper.setOnTouchListener(this);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// 取得左右滑動時手指按下的X坐標
touchDownX = event.getX();
return true;
} else if (event.getAction() == MotionEvent.ACTION_UP) {
// 取得左右滑動時手指松開的X坐標
touchUpX = event.getX();
// 從左往右,看前一個View
if (touchUpX – touchDownX > 100) {
// 設置View切換的動畫
viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
android.R.anim.slide_in_left));
viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
android.R.anim.slide_out_right));
// 顯示下一個View
viewFlipper.showPrevious();
// 從右往左,看後一個View
} else if (touchDownX – touchUpX > 100) {
// 設置View切換的動畫
// 由於Android沒有提供slide_out_left和slide_in_right,所以仿照slide_in_left和slide_out_right編寫瞭slide_out_left和slide_in_right
viewFlipper.setInAnimation(AnimationUtils.loadAnimation(this,
R.anim.slide_in_right));
viewFlipper.setOutAnimation(AnimationUtils.loadAnimation(this,
R.anim.slide_out_left));
// 顯示前一個View
viewFlipper.showNext();
}
return true;
}
return false;
}
}
slide_in_right.xml
[html]
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="50%p" android:toXDelta="0" android:duration="300"/>
<alpha android:fromAlpha="0.0" android:toAlpha="1.0" android:duration="300" />
</set>
slide_out_left.xml
[html]
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromXDelta="0" android:toXDelta="-50%p" android:duration="300"/>
<alpha android:fromAlpha="1.0" android:toAlpha="0.0" android:duration="300" />
</set>
上面的例子是在佈局文件中為ViewFlipper固定添加瞭兩個View,如果現在有N個View怎麼辦呢?那麼我們就需要在Java代碼裡面動態的添加View
先上代碼再講解
main.xml
[html]
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<com.tianjf.MyViewFlipper
android:id="@+id/myViewFlipper"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/white"
android:gravity="center" >
</com.tianjf.MyViewFlipper>
</LinearLayout>
flipper_view.xml
[html]
<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="none" >
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical" >
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_launcher" />
<TextView
android:id="@+id/textView"
android:textSize="100dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
MyGestureListener.java
[java]
package com.tianjf;
import android.view.GestureDetector.SimpleOnGestureListener;
import android.view.MotionEvent;
public class MyGestureListener extends SimpleOnGestureListener {
private OnFlingListener mOnFlingListener;
public OnFlingListener getOnFlingListener() {
return mOnFlingListener;
}
public void setOnFlingListener(OnFlingListener mOnFlingListener) {
this.mOnFlingListener = mOnFlingListener;
}
@Override
public final boolean onFling(final MotionEvent e1, final MotionEvent e2,
final float speedX, final float speedY) {
if (mOnFlingListener == null) {
return super.onFling(e1, e2, speedX, speedY);
}
float XFrom = e1.getX();
float XTo = e2.getX();
float YFrom = e1.getY();
float YTo = e2.getY();
// 左右滑動的X軸幅度大於100,並且X軸方向的速度大於100
if (Math.abs(XFrom – XTo) > 100.0f && Math.abs(speedX) > 100.0f) {
// X軸幅度大於Y軸的幅度
if (Math.abs(XFrom – XTo) >= Math.abs(YFrom – YTo)) {
if (XFrom > XTo) {
// 下一個
mOnFlingListener.flingToNext();
} else {
// 上一個
mOnFlingListener.flingToPrevious();
}
}
} else {
return false;
}
return true;
}
public interface OnFlingListener {
void flingToNext();
void flingToPrevious();
}
}
MyViewFlipper.java
[java]
package com.tianjf;
import com.tianjf.MyGestureListener.OnFlingListener;
import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ViewFlipper;
public class MyViewFlipper extends ViewFlipper implements OnFlingListener {
private GestureDetector mGestureDetector = null;
private OnViewFlipperListener mOnViewFlipperListener = null;
public MyViewFlipper(Context context) {
super(context);
}
public MyViewFlipper(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setOnViewFlipperListener(OnViewFlipperListener mOnViewFlipperListener) {
this.mOnViewFlipperListener = mOnViewFlipperListener;
MyGestureListener myGestureListener = new MyGestureListener();
myGestureListener.setOnFlingListener(this);
mGestureDetector = new GestureDetector(myGestureListener);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (null != mGestureDetector) {
return mGestureDetector.onTouchEvent(ev);
} else {
return super.onInterceptTouchEvent(ev);
}
}
@Override
public void flingToNext() {
if (null != mOnViewFlipperListener) {
int childCnt = getChildCount();
if (childCnt == 2) {
removeViewAt(1);
}
addView(mOnViewFlipperListener.getNextView(), 0);
if (0 != childCnt) {
setInAnimation(getContext(), R.anim.left_slip_in);
setOutAnimation(getContext(), R.anim.left_slip_out);
setDisplayedChild(0);
}
}
}
@Override
public void flingToPrevious() {
if (null != mOnViewFlipperListener) {
int childCnt = getChildCount();
if (childCnt == 2) {
removeViewAt(1);
}
addView(mOnViewFlipperListener.getPreviousView(), 0);
if (0 != childCnt) {
setInAnimation(getContext(), R.anim.right_slip_in);
setOutAnimation(getContext(), R.anim.right_slip_out);
setDisplayedChild(0);
}
}
}
public interface OnViewFlipperListener {
View getNextView();
View getPreviousView();
}
}
ViewFlipperDemoActivity.java
[java]
package com.tianjf;
import com.tianjf.MyViewFlipper.OnViewFlipperListener;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ScrollView;
import android.widget.TextView;
public class ViewFlipperDemoActivity extends Activity implements OnViewFlipperListener {
private MyViewFlipper myViewFlipper;
private int currentNumber;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
currentNumber = 1;
myViewFlipper = (MyViewFlipper) findViewById(R.id.myViewFlipper);
myViewFlipper.setOnViewFlipperListener(this);
myViewFlipper.addView(creatView(currentNumber));
}
@Override
public View getNextView() {
currentNumber = currentNumber == 10 ? 1 : currentNumber + 1;
return creatView(currentNumber);
}
@Override
public View getPreviousView() {
currentNumber = currentNumber == 1 ? 10 : currentNumber – 1;
return creatView(currentNumber);
}
private View creatView(int currentNumber) {
LayoutInflater layoutInflater = LayoutInflater.from(this);
ScrollView resultView = (ScrollView) layoutInflater.inflate(R.layout.flipper_view, null);
((TextView) resultView.findViewById(R.id.textView)).setText(currentNumber + "");
return resultView;
}
}
好瞭,代碼上完瞭,開始講解!
ViewFilpper的showPrevious()方法和showNext()方法是用來顯示已經在佈局文件中定義好瞭的View,現在我們沒有在佈局文件中為ViewFlipper添加View,那麼showPrevious()方法和showNext()方法就不能用瞭。但是我們怎麼實現滑動來切換View呢?用什麼方法呢?
這時候,我們就要自定義一個MyViewFlipper來監聽滑動事件,並做切換視圖的處理。
你可以讓MyViewFlipper實現OnTouchListener接口,然後實現onTouch方法,然後根據MotionEvent.ACTION_DOWN和MotionEvent.ACTION_UP的坐標判斷是不是滑動事件,就像ImageSwitcher中講解的那樣(http://blog.csdn.net/tianjf0514/article/details/7556487)
除瞭自己判斷是不是滑動事件,那麼Android有沒有直接提供哪個方法作為滑動事件的回調函數呢?答案是:提供瞭。OnGestureListener中的onFling方法就是滑動事件的回調函數。這時候你也許會毫不猶豫的讓MyViewFlipper實現OnGestureListener接口,並復寫onFling方法。這樣做當然可以,不過實現OnGestureListener接口不僅僅要復寫onFling方法,還要復寫其他的方法(onDown()、onShowPress()、onSingleTapUp()、onScroll()、onLongPress()),但是這些回調函數我們不需要,這就造成瞭垃圾代碼。
為瞭避免垃圾代碼,Android提供瞭一個類SimpleOnGestureListener已經實現瞭OnGestureListener接口和OnDoubleTapListener接口,並復寫瞭所有方法。那麼我們隻要新建一個自己的MyGestureListener.java來繼承SimpleOnGestureListener,並有選擇性的復寫需要的方法(我們在此隻復寫onFling方法)。
這時,我們就自定義瞭一個手勢類,並且這個手勢類會監聽滑動事件來做一些處理。但是我們怎麼利用這個手勢類呢?怎麼利用到MyViewFlipper類中去呢?
關於onFling方法,有一點要註意:不是每個View都能有onFling回調函數,一開始,我的flipper_view.xml佈局文件最外層是一個LinearLayout,死活都走不到onFling方法,後來在外層又套瞭一個ScrollView,就能正常走到OnFling方法裡面瞭。
可以看到flingToNext方法和flingToPrevious方法裡面會判斷childCnt,如果為2,就removeViewAt(1);,然後再addView(mOnViewFlipperListener.getNextView(), 0);。這就要回顧一下ImageSwitcher的原理,ViewFlipper的原理和ImageSwitcher一樣,有且僅有2個子View,滑動時候就在這兩個子View上來回切換。index為0的就是當前看到的,index為1的就是看不見的。上面代碼的意思就是:當滑動時,必然要新添加一個View,那麼子View的個數有可能大於2,隨意要先判斷一下如果childCnt == 2,那麼就把index == 1的那個View(即看不見的View)給Remove調,然後把新添加的View添加到index == 0處。這樣可以減少內存消耗。
OK,這個例子的基本的註意點已經講完瞭。下面在系統的回顧一下這個例子的具體流程。
在我們滑動手機屏幕的時候(假設我們從右往左滑動),那麼應該顯示下一個View。
調用onFling方法中的mOnFlingListener.flingToNext();
flingToNext方法的是實現在MyViewFlipper類中,調用flingToNext方法的addView(mOnViewFlipperListener.getNextView(), 0);
getNextView的實現在ViewFlipperDemoActivity類中
好的,講完瞭,要想完全理解透徹,跑跑例子,理解理解。
摘自 殤雲的專欄