两个人共尝一个痛苦只有半个痛苦,两个人共享一个欢乐却有两个欢乐。
本讲内容:Gallery仿图像集浏览
一、基本原理
在 Activity 中实现 OnGestureListener 的接口 onFling() 手势事件,通过自定义的 View 绘制draw() 图片
二、手势坐标介绍
下面示例中,用到了OnGestureListener接口的onScroll()和OnFling()方法,涉及到了Android系统坐标及触摸MotionEvent
e1和e2、速度velocityX、velocityY等值
(1)MotionEvent中 e1是手指第一次按上屏幕的起点,e2是抬起手指离开屏幕的终点,根据上图Android屏幕坐标系可知:
手指向右滑动,终点(e2)在起点(e1)的右侧,有e2.getX() - e1.getX() 大于0
手指向左滑动,终点(e2)在起点(e1)的左侧,有e2.getX() - e1.getX() 小于0
手指向下滑动,终点(e2)在起点(e1)的下侧,有e2.getY() - e1.getY() 大于0
手指向上滑动,终点(e2)在起点(e1)的上侧,有e2.getY() - e1.getY() 小于0
(2)onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY)
distanceX,是前后两次call的X距离,不是e2与e1的水平距离
distanceX,是前后两次call的Y距离,不是e2与e1的垂直距离
具体数值的方向,请详见上图(中)
(3)onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY)
velocityX,是X轴的每秒速度
velocityY,是Y轴的每秒速度
具体数值的方向,请详见上图(右)
仔细观察可以发现:velocityX、velocityY的方向与distanceX、distanceY方向正好相反
Gallery图像集的图片浏览效果。效果图如下:
下面是FlingView.java文件:
public class FlingView extends View { private Bitmap bitmap; private Bitmap nBitmap; private Bitmap fBitmap; public int offsetX = 0; public int offsetY = 0; public int postion = 0; private Bitmap[] bitmaps; public FlingView(Context context, AttributeSet attrs) { super(context, attrs); } public FlingView(Context context, Bitmap[] bitmaps) { super(context); this.bitmaps = bitmaps; bitmap = getBitmap(0); nBitmap = getBitmap(1); } // 在滑动过程中,通过实现View的Draw()方法绘制图片,注意:此时需要同时绘制当前图片(获取焦点)和下一张图片(即将获取焦点)共两张图片 @Override public void draw(Canvas canvas) { Paint paint = new Paint(); Rect rect = new Rect(); canvas.drawColor(Color.BLACK); // 绘制当前图片 if (bitmap != null) { int left = offsetX; int top = offsetY; int right = offsetX + MainActivity.deviceScreenWidth; int bottom = offsetY + MainActivity.deviceScreenHeight; rect.set(left, top, right, bottom); canvas.drawBitmap(bitmap, null, rect, paint); } // 绘制下一张图片 if (offsetX < 0) { // 向左滑动 if (nBitmap != null) { int left = MainActivity.deviceScreenWidth + 15 + offsetX; int top = 0; int right = left + MainActivity.deviceScreenWidth; int bottom = MainActivity.deviceScreenHeight; rect.set(left, top, right, bottom); canvas.drawBitmap(nBitmap, null, rect, paint); } } else if (offsetX > 0) { // 向右滑动 if (fBitmap != null) { int left = -MainActivity.deviceScreenWidth - 15 + offsetX; int top = 0; int right = left + MainActivity.deviceScreenWidth; int bottom = MainActivity.deviceScreenHeight; rect.set(left, top, right, bottom); canvas.drawBitmap(fBitmap, null, rect, paint); } } } public void handleScroll(int deltaX) { if (deltaX > 0) { offsetX -= -deltaX; } else { offsetX += deltaX; } invalidate(); } boolean isFling = false; // 标记是否需要切换到下一张(滑动距离超过屏幕宽度的 1/3) boolean isFlingRight = false; // 标记为需要向右滑动 boolean isFlingLeft = false; // 标记为需要向左滑动 class MyAnimation extends Animation { private int tmpOffsetX; @Override public void initialize(int width, int height, int parentWidth, int parentHeight) { tmpOffsetX = offsetX; super.initialize(width, height, parentWidth, parentHeight); setDuration(500); setFillAfter(true); setInterpolator(new LinearInterpolator()); } @Override protected void applyTransformation(float interpolatedTime, Transformation t) { // Log.i("bb", "offsetX==>"+offsetX); if (isFling) { // 需要滑动图片时根据方向来变换offsetX大小 if (tmpOffsetX > 0) { offsetX = (int) ((MainActivity.deviceScreenWidth - tmpOffsetX) * interpolatedTime + tmpOffsetX); } else { offsetX = (int) ((-MainActivity.deviceScreenWidth - tmpOffsetX) * interpolatedTime + tmpOffsetX); } } else { // 不需要变换的情况 offsetX = (int) (tmpOffsetX * (1 - interpolatedTime)); } invalidate(); } } // 动画结束后需要做一些工作 //在滑动图片结束后,需要做滑动动画后的处理,重新设置当前图片和当前图片的上一张和下一张的状态,为下次滑动做准备 @Override protected void onAnimationEnd() { if (isFlingRight) { // 向右滑动,position减1 nBitmap = bitmap; bitmap = fBitmap; fBitmap = null; postion = postion - 1; } else if (isFlingLeft) { // 向左滑动,position加1 fBitmap = bitmap; bitmap = nBitmap; nBitmap = null; postion = postion + 1; } isFlingRight = false; isFlingLeft = false; isFling = false; offsetX = 0; if (fBitmap == null && offsetX == 0) { // 如果前一张图片为空(向右滑),则重置前一张图片(position // - 1) if (postion > 0) { fBitmap = getBitmap(postion - 1); } } else if (nBitmap == null && offsetX == 0) { // 如果后一张图片为空(向左滑),则重置后一张图片(position // + 1) if (postion < bitmaps.length - 1) { nBitmap = getBitmap(postion + 1); } } clearAnimation(); } /** 获得当前位置的图片 */ public Bitmap getBitmap(int currentPos) { if (currentPos > bitmaps.length - 1) { return null; } Bitmap currBitmap = bitmaps[currentPos]; offsetX = 0; offsetY = 0; return currBitmap; } //获取来自Activity中的手势速度 public void onFling(int paramFloat1) { if (offsetX > MainActivity.deviceScreenWidth / 5) { if (fBitmap != null) { isFling = true; isFlingRight = true; } } else if (offsetX < -MainActivity.deviceScreenWidth / 5) { if (nBitmap != null) { isFling = true; isFlingLeft = true; } } // 开始动画效果 startAnimation(new MyAnimation()); } }
下面是MainActivity.java主界面文件:(没layout文件)
public class MainActivity extends Activity implements OnGestureListener { private FlingView flingView; private GestureDetector myGesture; public static int deviceScreenWidth; public static int deviceScreenHeight; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); // 获得手机的宽带和高度像素单位为px DisplayMetrics dm = new DisplayMetrics(); getWindowManager().getDefaultDisplay().getMetrics(dm); deviceScreenWidth = dm.widthPixels; deviceScreenHeight = dm.heightPixels; Log.i("mActivity", "deviceScreenWidth = " + deviceScreenWidth + "; deviceScreenHeight = " + deviceScreenHeight); Bitmap[] bitmaps = { BitmapFactory.decodeResource(getResources(), R.drawable.img1), BitmapFactory.decodeResource(getResources(), R.drawable.img2), BitmapFactory.decodeResource(getResources(), R.drawable.img3), BitmapFactory.decodeResource(getResources(), R.drawable.img4), BitmapFactory.decodeResource(getResources(), R.drawable.img5) }; myGesture = new GestureDetector(this); flingView = new FlingView(this, bitmaps); setContentView(flingView); } //Activity中,通过onTouchEvent() 注册 myGesture.onTouchEvent(event) @Override public boolean onTouchEvent(MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_UP: flingView.onFling(0); // 手指抬起后,重置滑动距离offsetX = 0 break; } return myGesture.onTouchEvent(event); } @Override public boolean onDown(MotionEvent e) { return false; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { Log.i("mActivity", "velocityX = " + velocityX + "; velocityY = " + velocityY); flingView.onFling((int) - velocityX); return true; } @Override public void onLongPress(MotionEvent e) { } //获取滑动的x轴距离 @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { Log.i("mActivity", "distanceX = " + distanceX + "; distanceY = " + distanceY); flingView.handleScroll(-1 * (int) distanceX); return true; } //获取手势的速度 @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { return false; } }
Take your time and enjoy it