您的位置:首页 > 移动开发 > Android开发

Android学习笔记_21_ViewFlipper使用详解 手势识别器

2013-12-05 16:29 387 查看
一、介绍ViewFilpper类

1.1 屏幕切换

    屏幕切换指的是在同一个Activity内屏幕见的切换,最长见的情况就是在一个FrameLayout内有多个页面,比如一个系统设置页面;一个个性化设置页面。

1.2 ViewFilpper类

    ViewFlipper extends ViewAnimator

java.lang.Object
android.view.View
android.view.ViewGroup
android.widget.FrameLayout
android.widget.ViewAnimator
android.widget.ViewFlipper
Class Overview

Simple
ViewAnimator
that will animate between two or more views that have been added to it. Only one child is shown at a time. If requested, can automatically flip between each child at a regular interval.

    意思是:简单的ViewAnimator之间,两个或两个以上的view加上动画效果。只有一个小孩会显示在一个时间。如果需要,每个孩子能自动翻转之间在固定的时间间隔。

  该类继承了Framelayout类,ViewAnimator类的作用是为FrameLayout里面的View切换提供动画效果。
  该类有如下几个和动画相关的函数:

  setInAnimation:设置View进入屏幕时候使用的动画,该函数有两个版本,一个接受单个参数,类型为android.view.animation.Animation;一个接受两个参数,类型为Context和int,分别为Context对象和定义Animation的resourceID。

  setOutAnimation: 设置View退出屏幕时候使用的动画,参数setInAnimation函数一样。

   showNext: 调用该函数来显示FrameLayout里面的下一个View。

   showPrevious: 调用该函数来显示FrameLayout里面的上一个View。

    简单ViewFlipper的应用

二、ViewFlipper复杂应用

2.1 动态添加多个View

  下面通过一个Demo了解一下ViewFlipper的用法

main.xml

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

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

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

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

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

<?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布局文件

<?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布局文件

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


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类中


1.3 手势识别器

 

public class DemoActivity extends Activity {
ViewFlipper vf;
//手势识别的帮助类
GestureDetector  mGestureDetector ;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
vf = (ViewFlipper) this.findViewById(R.id.vf);
TextView tv1 = new TextView(this);
tv1.setText("第一个界面");
ImageView iv2 = new ImageView(this);
iv2.setImageResource(R.drawable.ic_launcher);
TextView tv3 = new TextView(this);
tv3.setText("第三个界面");

vf.addView(tv1);
vf.addView(iv2);
vf.addView(tv3);
mGestureDetector = new GestureDetector(new GestureDetector.SimpleOnGestureListener(){

//e1:手指进入屏幕事件,e2:手指离开屏幕事件。
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2,
float velocityX, float velocityY) {

if( Math.abs(e1.getY()-e2.getY()) > 100){
//垂直方向变化过大 无效事件
return false;
}
if( e1.getX() - e2.getX() > 100 && Math.abs(velocityX)>100)
{

showpre();
return false;
}
if( e2.getX() - e1.getX() > 100 && Math.abs(velocityX)>100 )
{

shownext();

return false;
}

return super.onFling(e1, e2, velocityX, velocityY);
}

});

}
public void pre(View view){
showpre();
}
private void showpre() {
vf.showPrevious();
AlphaAnimation inaa = new AlphaAnimation(0.0f, 1.0f);
inaa.setDuration(1000);
vf.setInAnimation(inaa);
AlphaAnimation outaa = new AlphaAnimation(1.0f, 0.0f);
outaa.setDuration(1000);
vf.setOutAnimation(outaa);
}
public void next(View view){
shownext();
}
private void shownext() {
vf.showNext();
AlphaAnimation inaa = new AlphaAnimation(0.0f, 1.0f);
inaa.setDuration(1000);
vf.setInAnimation(inaa);
AlphaAnimation outaa = new AlphaAnimation(1.0f, 0.0f);
outaa.setDuration(1000);
vf.setOutAnimation(outaa);
}
@Override
public boolean onTouchEvent(MotionEvent event) {

// 把当前屏幕触摸的事件 传递个手势识别器
mGestureDetector.onTouchEvent(event);
//有的时候 消费掉 触摸事件 (防止事件层层传递)
//return true;
// 控制当前的事件 是否让(上层框架)系统的框架 默认处理
return super.onTouchEvent(event);
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: