ViewFlipper是Android系统提供的用于切换图片的UI控件,而GestureDetector是Android提供的用于判断手势并执行相应操作的类,该类中手势判断的方法与传统的手势判断的方法(View类中的setOnTouchListener方法)相比,操作更加方便,提供的参数更多,可判断的手势更加多样。下面将浅析一个demo:使用GestureDetector的手势判断方法,动态实现切换ViewFlipper中的图片。
demo的实现效果
demo的实现效果如下所示——左右滑动屏幕上的图片,实现图片的轮询切换。
注:由于切图速度比较快,故截图显示不够明了,下面将在代码中打印Log并说明手势方法的调用。
XML布局
XML布局使用了include标签用于包含相似的内容:
<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/view_flipper"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<include
android:id="@+id/screen1"
layout="@layout/item1" />
<include
android:id="@+id/screen2"
layout="@layout/item2" />
<include
android:id="@+id/screen3"
layout="@layout/item3" />
<include
android:id="@+id/screen4"
layout="@layout/item4" />
<include
android:id="@+id/screen5"
layout="@layout/item5" />
</ViewFlipper>
item1的layout布局如下(item2-item5与item1相仿,此处不再列出):
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/android1"
android:orientation="vertical" >
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="view1" />
</LinearLayout>
activity逻辑实现
public class MainActivity extends Activity {
public static final String TAG = "GESTURE_DETECTOR";
private ViewFlipper mViewFlipper;
private GestureDetector mGestureDetector;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mViewFlipper = (ViewFlipper) findViewById(R.id.view_flipper);
// 初始化GestureDetector类
mGestureDetector = new GestureDetector(this,
new CustomSimpleOnGestureListener());
// 依然需要实现setOnTouchListener方法
mViewFlipper.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
// 使用GestureDetector接管手势操作
mGestureDetector.onTouchEvent(event);
return true;
}
});
}
// 重写GestureDetector类中的静态子类SimpleOnGestureListener,
// 并根据不同手势操作,有选择地重写该静态子类中的方法
public class CustomSimpleOnGestureListener extends
GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onSingleTapUp--");
return super.onSingleTapUp(e);
}
@Override
public void onLongPress(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onLongPress--");
super.onLongPress(e);
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
// TODO Auto-generated method stub
Log.i(TAG, "--onScroll--");
return super.onScroll(e1, e2, distanceX, distanceY);
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
float velocityY) {
// TODO Auto-generated method stub
Log.i(TAG, "--onFling--");
// 从左往右划 划到上一张
if (e2.getX() - e1.getX() > 100) {
mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(
MainActivity.this, R.anim.show_previous_in));
mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(
MainActivity.this, R.anim.show_previous_out));
mViewFlipper.showPrevious();
}
// 从右往左划 划到下一张
else if (e1.getX() - e2.getX() > 100) {
mViewFlipper.setInAnimation(AnimationUtils.loadAnimation(
MainActivity.this, R.anim.show_next_in));
mViewFlipper.setOutAnimation(AnimationUtils.loadAnimation(
MainActivity.this, R.anim.show_next_out));
mViewFlipper.showNext();
}
return true;
}
@Override
public void onShowPress(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onShowPress--");
super.onShowPress(e);
}
@Override
public boolean onDown(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onDown--");
return super.onDown(e);
}
@Override
public boolean onDoubleTap(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onDoubleTap--");
return super.onDoubleTap(e);
}
@Override
public boolean onDoubleTapEvent(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onDoubleTapEvent--");
return super.onDoubleTapEvent(e);
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onSingleTapConfirmed--");
return super.onSingleTapConfirmed(e);
}
@Override
public boolean onContextClick(MotionEvent e) {
// TODO Auto-generated method stub
Log.i(TAG, "--onContextClick--");
return super.onContextClick(e);
}
}
}
代码分析:
- 根据代码可知,要通过GestureDetector实现手势操作,仍然要先实现View类中的setOnTouchListener方法并传入OnTouchListener接口对象,只是需要在该接口中的onTouch方法中使用GestureDetector的onTouchEvent方法接管手势操作而已。
初始化GestureDetector类时,该类的构造方法中的第二个参数可以是以下4个对象之一:
接口OnGestureListener,该接口中有6个未实现方法:
—— onDown(MotionEvent e);
—— onShowPress(MotionEvent e);
—— onSingleTapUp(MotionEvent e);
—— onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
—— onLongPress(MotionEvent e);
—— onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)接口OnDoubleTapListener,该接口中有3个未实现方法:
—— onSingleTapConfirmed(MotionEvent e);
—— onDoubleTap(MotionEvent e);
—— onDoubleTapEvent(MotionEvent e);- 接口OnContextClickListener ,该接口中有1个未实现方法:
—— onContextClick(MotionEvent e); - 静态子类SimpleOnGestureListener
—— 该子类实现了上述3个接口的全部10个方法,只是都是空实现而已,并没有进行具体的逻辑覆盖。
- 推荐使用静态子类SimpleOnGestureListener对象作为构造GestureDetector类的第二个参数,这样可以有选择地覆盖所需要的方法,而不是需要实现所有未实现的方法。
- 可以为图片切换添加动画效果,切入时调用ViewFlipper的setInAnimation方法,切出时调用ViewFlipper的setOutAnimation方法,并在XML中自定义动画效果,代码如下:
<!-- 向左滑动时,切入的动画定义 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromXDelta="100%p"
android:toXDelta="0" >
</translate>
<alpha
android:duration="200"
android:fromAlpha="0.1"
android:toAlpha="1.0" >
</alpha>
</set>
<!-- 向左滑动时,切出的动画定义 -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromXDelta="0"
android:toXDelta="-100%p" />
<alpha
android:duration="200"
android:fromAlpha="1.0"
android:toAlpha="0.1" >
</alpha>
</set>
<!-- 向右滑动时,切入的动画定义 -->
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromXDelta="-100%p"
android:toXDelta="0"
android:zAdjustment="bottom" >
</translate>
<alpha
android:duration="200"
android:fromAlpha="0.1"
android:toAlpha="1.0" >
</alpha>
</set>
<!-- 向右滑动时,切出的动画定义 -->
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android" >
<translate
android:duration="200"
android:fromXDelta="0"
android:toXDelta="100%p" />
<alpha
android:duration="200"
android:fromAlpha="1.0"
android:toAlpha="0.1" >
</alpha>
</set>
需要注意:
- translate标签用于设置切换起点与终点的相对位移,单位为“%p”;
- alpha标签用于设置起始位置和终止位置的透明度,范围是0.0-1.0,其中0.0代表完全透明,1.0代表完全不透明。
SimpleOnGestureListener回调方法浅析
在静态子类SimpleOnGestureListener中,一共有10个空实现的回调方法,这些方法会根据不同手势操作而适时回调,开发者也可以为实现某个特定手势而重写特定的方法,而不用将所有方法重写。
在demo中,打印Log,浅析不同手势对应的回调方法:
- 在屏幕上快速滑动,首先调用onDown,接着是一系列的onScroll,最后是onFling;
- 单击屏幕,先后调用onDown,onSingleTapUp,onSingleTapConfirmed;
- 双指(及双指以上)单击屏幕,仅调用onDown;
- 在屏幕上慢速滑动,首先调用onDown,然后是一系列的onScroll,并不调用onFling方法;
- 用力单击屏幕,依次调用onDown,onShowPress,onSingleTapUp,OnSingleTapConfirmed;
- 长按屏幕,依次调用onDown,onShowPress,onLongPress;
- 双击屏幕,依次调用onDown,onSingleTapUp,onDoubleTap,onDoubleTapEvent,onDown,onDoubleTapEvent。
至此,除onContextClick未回调以外(该方法涉及到事件分发机制),其余9个方法均在不同手势的操作下被回调,开发者可以在不同手势所回调的方法中编写相应逻辑从而实现手势的捕捉继而触发事件逻辑。