GestureDetector.SimpleOnGestureListener中onScroll的参数e2.getX的问题

ZYLab 2012-02-23 12:01:02
我在做一个图片浏览软件,做那种仿launcher桌面的滑动效果时,当滑动到两张图片各占了屏幕显示的一半的时候,就这样一直维持着的话,这两张图片总会有明显的颤动的bug,而图片的移动都是在GestureDetector.SimpleOnGestureListener中onScroll中根据滑动距离实现的。于是我来来查看GestureDetector.SimpleOnGestureListener中的
onScroll(MotionEvent e1, MotionEvent e2,float distanceX, float distanceY) 。就在我手指一直在屏幕上不移动(或有极小颤动)的动情况下,我发现e2.getX的值变化很大。我把log亮出来给大家看看:
02-22 19:48:46.853: INFO/offset(19060): e1.getX:277.16916
02-22 19:48:46.853: INFO/offset(19060): e2.getX:196.0465
02-22 19:48:46.853: INFO/offset(19060): mCurrentOffset:0.0
02-22 19:48:46.853: INFO/offset(19060): currentScrollDelta:81.122665
02-22 19:48:46.853: INFO/offset(19060): scrollOffset:81
02-22 19:48:46.853: INFO/offset(19060): -----------------------------------
02-22 19:48:46.881: INFO/offset(19060): e1.getX:277.16916
02-22 19:48:46.881: INFO/offset(19060): e2.getX:120.147385
02-22 19:48:46.881: INFO/offset(19060): mCurrentOffset:0.0
02-22 19:48:46.881: INFO/offset(19060): currentScrollDelta:157.02177
02-22 19:48:46.881: INFO/offset(19060): scrollOffset:157
02-22 19:48:46.881: INFO/offset(19060): -----------------------------------
02-22 19:48:46.891: INFO/offset(19060): e1.getX:277.16916
02-22 19:48:46.891: INFO/offset(19060): e2.getX:197.41345
02-22 19:48:46.891: INFO/offset(19060): mCurrentOffset:0.0
02-22 19:48:46.891: INFO/offset(19060): currentScrollDelta:79.75571
02-22 19:48:46.891: INFO/offset(19060): scrollOffset:80
02-22 19:48:46.891: INFO/offset(19060): -----------------
.....

基本上e2.getX就是在120与197之间切换,于是我就搞不明白了,e2.getX为啥总是 这样改变呢?我的手指又没怎么动啊?
一些关键代码帖上来:


我是抄别人例子的,代码仓库中一个叫photostore的图片浏览器的例子 onScroll代码如下:
public boolean onScroll(MotionEvent e1, MotionEvent e2,
float distanceX, float distanceY) {
if (e2.getAction() == MotionEvent.ACTION_MOVE) {
if (mIsDragging == false) {
// Stop animation
mIsTouched = true;

// Reconfigure scroll
mIsDragging = true;
mFlingDirection = 0;
mScrollTimestamp = System.currentTimeMillis();
mCurrentOffset = mViews[mCurrentViewNumber]
.getCurrentOffset();
}

// mA是恒的是250
float maxVelocity = mGalleryWidth
/ (mAnimationDuration / 1000.0f);
// 连续滑动时间
long timestampDelta = System.currentTimeMillis()
- mScrollTimestamp;
float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f);
float currentScrollDelta = e1.getX() - e2.getX();
if (currentScrollDelta < maxScrollDelta * -1)
currentScrollDelta = maxScrollDelta * -1;
if (currentScrollDelta > maxScrollDelta)
currentScrollDelta = maxScrollDelta;
int scrollOffset = Math.round(mCurrentOffset
+ currentScrollDelta);

// We can't scroll more than the width of our own frame layout
if (scrollOffset >= mGalleryWidth)
scrollOffset = mGalleryWidth;
if (scrollOffset <= mGalleryWidth * -1)
scrollOffset = mGalleryWidth * -1;

mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber);
mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber);
mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber);
}
return false;
}



public void setOffset(int xOffset, int yOffset, int relativeViewNumber) {
// Scroll the target view relative to its own position relative to
// currently displayed view
mInternalLayout.scrollTo(
getViewOffset(mViewNumber, relativeViewNumber) + xOffset,
yOffset);
}


需要说的是,我的onGestureListener是这样调用的:
下边这个onTouch是一个叫ZoomImageView的自定义控件,实现双手触摸改变图片大小

public boolean onTouch(View v, MotionEvent event) {

final int action = event.getAction();
int pointCount = event.getPointerCount();
if (pointCount == 1) {
//fg.就是上边GestureDectector来监听的一个自定义控件叫myGallery吧
fg.onTouchEvent(event);
final float y = event.getY();
switch (action) {
case MotionEvent.ACTION_DOWN:
mY = y;
break;
case MotionEvent.ACTION_MOVE:
final float dy = (y - mY) / v.getHeight();
mState.setPanY(mState.getPanY() - dy);
mState.notifyObservers();
mY = y;
break;
}
}
if (pointCount == 2) {
final float x0 = event.getX(event.getPointerId(0));
final float y0 = event.getY(event.getPointerId(0));

final float x1 = event.getX(event.getPointerId(1));
final float y1 = event.getY(event.getPointerId(1));

final float gap = getGap(x0, x1, y0, y1);
switch (action) {
case MotionEvent.ACTION_POINTER_2_DOWN:
case MotionEvent.ACTION_POINTER_1_DOWN:
mGap = gap;
break;
case MotionEvent.ACTION_POINTER_1_UP:
mX = x1;
mY = y1;
break;
case MotionEvent.ACTION_POINTER_2_UP:
mX = x0;
mY = y0;
break;
case MotionEvent.ACTION_MOVE: {
final float dgap = (gap - mGap) / mGap;
// Log.d("Gap", String.valueOf(dgap));
Log.d("Gap", String.valueOf((float) Math.pow(20, dgap)));
mState.setZoom(mState.getZoom() * (float) Math.pow(5, dgap));
mState.notifyObservers();
mGap = gap;
break;
}
}
}
return true;
}


我所仿的demo 并不是在myGallery中放入zoomImageView进行滑动而是放入ImageView的,就没e2.getX 大幅改变的问题~因为ZoomImageView 是先获得touch的,只有在单点触摸时才会让其响应myGallery的onGestureListener ,我怀疑可能是这里的问题,但我还是找不到~~
谢啦~
...全文
594 2 打赏 收藏 转发到动态 举报
写回复
用AI写文章
2 条回复
切换为时间正序
请发表友善的回复…
发表回复
j_f0001 2012-06-02
  • 打赏
  • 举报
回复
你查看一下多指触摸的问题
  • 打赏
  • 举报
回复
请问lz解决这个问题了吗。。我也遇到了,求指点
重写的Gallery,Gallery滑动的时候下面的点跟着动 package com.ray.ui.view; import java.util.ArrayList; import android.app.Activity; import android.content.Context; import android.util.Log; import android.view.GestureDetector; import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.Interpolator; import android.view.animation.Transformation; import android.widget.Adapter; import android.widget.FrameLayout; import android.widget.LinearLayout; import android.widget.FrameLayout.LayoutParams; import com.jxtoon.flight.R; // TODO: // 1. In order to improve performance Cache screen bitmap and use for animation // 2. Establish superfluous memory allocations and delay or replace with reused objects // Probably need to make sure we are not allocating objects (strings, etc.) in loops public class MyGallery extends FrameLayout { // Constants private final int swipe_min_distance = 120; private final int swipe_max_off_path = 250; private final int swipe_threshold_veloicty = 400; // Properties private int mViewPaddingWidth = 0; private int mAnimationDuration = 250; private float mSnapBorderRatio = 0.5f; private boolean mIsGalleryCircular = true; // Members private int mGalleryWidth = 0; private boolean mIsTouched = false; private boolean mIsDragging = false; private float mCurrentOffset = 0.0f; private long mScrollTimestamp = 0; private int mFlingDirection = 0; private int mCurrentPosition = 0; private int mCurrentViewNumber = 0; private Activity mContext; private Adapter mAdapter; private FlingGalleryView[] mViews; private FlingGalleryAnimation mAnimation; private GestureDetector mGestureDetector; //An interpolator defines the rate of change of an animation. This allows the basic animation effects (alpha, scale, translate, rotate) to be accelerated, decelerated, repeated, etc. private Interpolator mDecelerateInterpolater; private ArrayList indicatorView; //Gallery构造方法 public MyGallery(Activity context) { super(context); // 增加选择指示灯 indicatorView = new ArrayList(); indicatorView.add(context.findViewById(R.id.switcher1)); indicatorView.add(context.findViewById(R.id.switcher2)); indicatorView.add(context.findViewById(R.id.switcher3)); indicatorView.add(context.findViewById(R.id.switcher4)); indicatorView.add(context.findViewById(R.id.switcher5)); indicatorView.get(0).setSelected(true);// 初始化选择第一个 mContext = context; // 指定只能有三个缓存View mViews = new FlingGalleryView[3]; mViews[0] = new FlingGalleryView(0, this); mViews[1] = new FlingGalleryView(1, this); mViews[2] = new FlingGalleryView(2, this); // 切换View动画 mAnimation = new FlingGalleryAnimation(); mGestureDetector = new GestureDetector(new FlingGestureDetector()); mDecelerateInterpolater = AnimationUtils.loadInterpolator(mContext, android.R.anim.decelerate_interpolator); } public void setPaddingWidth(int viewPaddingWidth) { mViewPaddingWidth = viewPaddingWidth; } public void setAnimationDuration(int animationDuration) { mAnimationDuration = animationDuration; } public void setSnapBorderRatio(float snapBorderRatio) { mSnapBorderRatio = snapBorderRatio; } /** * 是否Circular * 设置gallery的循环情况 * @param isGalleryCircular */ public void setIsGalleryCircular(boolean isGalleryCircular) { if (mIsGalleryCircular != isGalleryCircular) { mIsGalleryCircular = isGalleryCircular; if (mCurrentPosition == getFirstPosition()) { // We need to reload the view immediately to the left to change // it to circular view or blank mViews[getPrevViewNumber(mCurrentViewNumber)] .recycleView(getPrevPosition(mCurrentPosition)); } if (mCurrentPosition == getLastPosition()) { // We need to reload the view immediately to the right to change // it to circular view or blank mViews[getNextViewNumber(mCurrentViewNumber)] .recycleView(getNextPosition(mCurrentPosition)); } } } public int getGalleryCount() { return (mAdapter == null) ? 0 : mAdapter.getCount(); } public int getFirstPosition() { return 0; } public int getLastPosition() { return (getGalleryCount() == 0) ? 0 : getGalleryCount() - 1; } private int getPrevPosition(int relativePosition) { int prevPosition = relativePosition - 1; if (prevPosition < getFirstPosition()) { prevPosition = getFirstPosition() - 1; if (mIsGalleryCircular == true) { prevPosition = getLastPosition(); } } return prevPosition; } private int getNextPosition(int relativePosition) { int nextPosition = relativePosition + 1; if (nextPosition > getLastPosition()) { nextPosition = getLastPosition() + 1; if (mIsGalleryCircular == true) { nextPosition = getFirstPosition(); } } return nextPosition; } private int getPrevViewNumber(int relativeViewNumber) { return (relativeViewNumber == 0) ? 2 : relativeViewNumber - 1; } private int getNextViewNumber(int relativeViewNumber) { return (relativeViewNumber == 2) ? 0 : relativeViewNumber + 1; } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); // Calculate our view width mGalleryWidth = right - left; if (changed == true) { // Position views at correct starting offsets mViews[0].setOffset(0, 0, mCurrentViewNumber); mViews[1].setOffset(0, 0, mCurrentViewNumber); mViews[2].setOffset(0, 0, mCurrentViewNumber); } } public void setAdapter(Adapter adapter) { mAdapter = adapter; mCurrentPosition = 0; mCurrentViewNumber = 0; // Load the initial views from adapter mViews[0].recycleView(mCurrentPosition); mViews[1].recycleView(getNextPosition(mCurrentPosition)); mViews[2].recycleView(getPrevPosition(mCurrentPosition)); // Position views at correct starting offsets mViews[0].setOffset(0, 0, mCurrentViewNumber); mViews[1].setOffset(0, 0, mCurrentViewNumber); mViews[2].setOffset(0, 0, mCurrentViewNumber); } private int getViewOffset(int viewNumber, int relativeViewNumber) { // Determine width including configured padding width int offsetWidth = mGalleryWidth + mViewPaddingWidth; // Position the previous view one measured width to left if (viewNumber == getPrevViewNumber(relativeViewNumber)) { return offsetWidth; } // Position the next view one measured width to the right if (viewNumber == getNextViewNumber(relativeViewNumber)) { return offsetWidth * -1; } return 0; } void movePrevious() { // Slide to previous view mFlingDirection = 1; processGesture(); } void moveNext() { // Slide to next view mFlingDirection = -1; processGesture(); } @Override public boolean onKeyDown(int keyCode, KeyEvent event) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_LEFT: movePrevious(); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: moveNext(); return true; case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: } return super.onKeyDown(keyCode, event); } @Override public boolean onTouchEvent(MotionEvent event) { boolean consumed = mGestureDetector.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP) { if (mIsTouched || mIsDragging) { processScrollSnap(); processGesture(); } } return consumed; } public boolean onGalleryTouchEvent(MotionEvent event) { boolean consumed = mGestureDetector.onTouchEvent(event); if (event.getAction() == MotionEvent.ACTION_UP) { if (mIsTouched || mIsDragging) { processScrollSnap(); processGesture(); } } return consumed; } void processGesture() { int newViewNumber = mCurrentViewNumber; int reloadViewNumber = 0; int reloadPosition = 0; mIsTouched = false; mIsDragging = false; if (mFlingDirection > 0) { if (mCurrentPosition > getFirstPosition() || mIsGalleryCircular == true) { // Determine previous view and outgoing view to recycle newViewNumber = getPrevViewNumber(mCurrentViewNumber); mCurrentPosition = getPrevPosition(mCurrentPosition); reloadViewNumber = getNextViewNumber(mCurrentViewNumber); reloadPosition = getPrevPosition(mCurrentPosition); } } if (mFlingDirection < 0) { if (mCurrentPosition < getLastPosition() || mIsGalleryCircular == true) { // Determine the next view and outgoing view to recycle newViewNumber = getNextViewNumber(mCurrentViewNumber); mCurrentPosition = getNextPosition(mCurrentPosition); reloadViewNumber = getPrevViewNumber(mCurrentViewNumber); reloadPosition = getNextPosition(mCurrentPosition); } } if (newViewNumber != mCurrentViewNumber) { mCurrentViewNumber = newViewNumber; // Reload outgoing view from adapter in new position mViews[reloadViewNumber].recycleView(reloadPosition); } // Ensure input focus on the current view mViews[mCurrentViewNumber].requestFocus(); // Run the slide animations for view transitions mAnimation.prepareAnimation(mCurrentViewNumber); this.startAnimation(mAnimation); // Reset fling state mFlingDirection = 0; } void processScrollSnap() { // Snap to next view if scrolled passed snap position float rollEdgeWidth = mGalleryWidth * mSnapBorderRatio; int rollOffset = mGalleryWidth - (int) rollEdgeWidth; int currentOffset = mViews[mCurrentViewNumber].getCurrentOffset(); if (currentOffset <= rollOffset * -1) { // Snap to previous view mFlingDirection = 1; } if (currentOffset >= rollOffset) { // Snap to next view mFlingDirection = -1; } } //Gallery滑动类 private class FlingGalleryView { private int mViewNumber; private FrameLayout mParentLayout; private FrameLayout mInvalidLayout = null; private LinearLayout mInternalLayout = null; private View mExternalView = null; private int positionOfAdapter;// 当前View的位置 public FlingGalleryView(int viewNumber, FrameLayout parentLayout) { mViewNumber = viewNumber; mParentLayout = parentLayout; // Invalid layout is used when outside gallery mInvalidLayout = new FrameLayout(mContext); mInvalidLayout.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); // Internal layout is permanent for duration mInternalLayout = new LinearLayout(mContext); mInternalLayout.setLayoutParams(new LinearLayout.LayoutParams( LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); mParentLayout.addView(mInternalLayout); } public void recycleView(int newPosition) { if (mExternalView != null) { mInternalLayout.removeView(mExternalView); } if (mAdapter != null) { if (newPosition >= getFirstPosition() && newPosition <= getLastPosition()) { positionOfAdapter = newPosition; mExternalView = mAdapter.getView(newPosition, mExternalView, mInternalLayout); } else { mExternalView = mInvalidLayout; } } if (mExternalView != null) { mInternalLayout.addView(mExternalView, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT)); } } public void setOffset(int xOffset, int yOffset, int relativeViewNumber) { // Scroll the target view relative to its own position relative to // currently displayed view mInternalLayout.scrollTo( getViewOffset(mViewNumber, relativeViewNumber) + xOffset, yOffset); } public int getCurrentOffset() { // Return the current scroll position return mInternalLayout.getScrollX(); } public void requestFocus() { setHightlightIndicator(positionOfAdapter); mInternalLayout.requestFocus(); } } private void setHightlightIndicator(int positionOfIndicator) { for (int i = 0; i < indicatorView.size(); i++) { if (positionOfIndicator == i) { indicatorView.get(i).setSelected(true); } else { indicatorView.get(i).setSelected(false); } } } //gallery动画类 private class FlingGalleryAnimation extends Animation { private boolean mIsAnimationInProgres; private int mRelativeViewNumber; private int mInitialOffset; private int mTargetOffset; private int mTargetDistance; public FlingGalleryAnimation() { mIsAnimationInProgres = false; mRelativeViewNumber = 0; mInitialOffset = 0; mTargetOffset = 0; mTargetDistance = 0; } public void prepareAnimation(int relativeViewNumber) { // If we are animating relative to a new view if (mRelativeViewNumber != relativeViewNumber) { if (mIsAnimationInProgres == true) { // We only have three views so if requested again to animate // in same direction we must snap int newDirection = (relativeViewNumber == getPrevViewNumber(mRelativeViewNumber)) ? 1 : -1; int animDirection = (mTargetDistance < 0) ? 1 : -1; // If animation in same direction if (animDirection == newDirection) { // Ran out of time to animate so snap to the target // offset mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber); } } // Set relative view number for animation mRelativeViewNumber = relativeViewNumber; } // Note: In this implementation the targetOffset will always be zero // as we are centering the view; but we include the calculations of // targetOffset and targetDistance for use in future implementations mInitialOffset = mViews[mRelativeViewNumber].getCurrentOffset(); mTargetOffset = getViewOffset(mRelativeViewNumber, mRelativeViewNumber); mTargetDistance = mTargetOffset - mInitialOffset; // Configure base animation properties this.setDuration(mAnimationDuration); this.setInterpolator(mDecelerateInterpolater); // Start/continued animation mIsAnimationInProgres = true; } @Override protected void applyTransformation(float interpolatedTime, Transformation transformation) { // Ensure interpolatedTime does not over-shoot then calculate new // offset interpolatedTime = (interpolatedTime > 1.0f) ? 1.0f : interpolatedTime; int offset = mInitialOffset + (int) (mTargetDistance * interpolatedTime); for (int viewNumber = 0; viewNumber < 3; viewNumber++) { // Only need to animate the visible views as the other view will // always be off-screen if ((mTargetDistance > 0 && viewNumber != getNextViewNumber(mRelativeViewNumber)) || (mTargetDistance < 0 && viewNumber != getPrevViewNumber(mRelativeViewNumber))) { mViews[viewNumber] .setOffset(offset, 0, mRelativeViewNumber); } } } @Override public boolean getTransformation(long currentTime, Transformation outTransformation) { if (super.getTransformation(currentTime, outTransformation) == false) { // Perform final adjustment to offsets to cleanup animation mViews[0].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[1].setOffset(mTargetOffset, 0, mRelativeViewNumber); mViews[2].setOffset(mTargetOffset, 0, mRelativeViewNumber); // Reached the animation target mIsAnimationInProgres = false; return false; } // Cancel if the screen touched if (mIsTouched || mIsDragging) { // Note that at this point we still consider ourselves to be // animating // because we have not yet reached the target offset; its just // that the // user has temporarily interrupted the animation with a touch // gesture return false; } return true; } } private class FlingGestureDetector extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { // Stop animation mIsTouched = true; // Reset fling state mFlingDirection = 0; return true; } @Override public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { if (e2.getAction() == MotionEvent.ACTION_MOVE) { if (mIsDragging == false) { // Stop animation mIsTouched = true; // Reconfigure scroll mIsDragging = true; mFlingDirection = 0; mScrollTimestamp = System.currentTimeMillis(); mCurrentOffset = mViews[mCurrentViewNumber] .getCurrentOffset(); } float maxVelocity = mGalleryWidth / (mAnimationDuration / 1000.0f); long timestampDelta = System.currentTimeMillis() - mScrollTimestamp; float maxScrollDelta = maxVelocity * (timestampDelta / 1000.0f); float currentScrollDelta = e1.getX() - e2.getX(); if (currentScrollDelta < maxScrollDelta * -1) currentScrollDelta = maxScrollDelta * -1; if (currentScrollDelta > maxScrollDelta) currentScrollDelta = maxScrollDelta; int scrollOffset = Math.round(mCurrentOffset + currentScrollDelta); // We can't scroll more than the width of our own frame layout if (scrollOffset >= mGalleryWidth) scrollOffset = mGalleryWidth; if (scrollOffset <= mGalleryWidth * -1) scrollOffset = mGalleryWidth * -1; mViews[0].setOffset(scrollOffset, 0, mCurrentViewNumber); mViews[1].setOffset(scrollOffset, 0, mCurrentViewNumber); mViews[2].setOffset(scrollOffset, 0, mCurrentViewNumber); } return false; } @Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { if (Math.abs(e1.getY() - e2.getY()) <= swipe_max_off_path) { if (e2.getX() - e1.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty) { movePrevious(); } if (e1.getX() - e2.getX() > swipe_min_distance && Math.abs(velocityX) > swipe_threshold_veloicty) { moveNext(); } } return false; } @Override public void onLongPress(MotionEvent e) { // Finalise scrolling mFlingDirection = 0; processGesture(); } @Override public void onShowPress(MotionEvent e) { } @Override public boolean onSingleTapUp(MotionEvent e) { // Reset fling state mFlingDirection = 0; return false; } } }

80,350

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧