如何降低 android ScaleGestureDetector.SimpleOnScaleGestureListener 的敏感度
How to reduce sensitivity of android ScaleGestureDetector.SimpleOnScaleGestureListener
我有一个自定义的 SwipeRefreshLayout,里面有一个自定义的 GridView。我想根据 pinch/zoom 手势更改该 GridView 的列号。我已经成功地实施了它。问题是天平太敏感了
例如,我有第 3-5 列。缩放到3或5很容易,但是缩放到4很难,因为缩放本身太敏感了。
这是我自定义的 SwipeRefreshLayout class
/**
* This class contains fix from
*/
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
private int mTouchSlop;
private float mPrevX;
private ScaleGestureDetector mScaleGestureDetector;
private ScaleListener mScaleListener;
public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public void setScaleListener(Context context, ScaleListener scaleListener) {
this.mScaleListener = scaleListener;
mScaleGestureDetector = new ScaleGestureDetector(context, new MyOnScaleGestureListener());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mScaleGestureDetector != null) {
mScaleGestureDetector.onTouchEvent(event);
}
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
setEnabled(false);
if (mScaleListener != null) {
mScaleListener.onTwoFingerStart();
}
break;
case MotionEvent.ACTION_POINTER_UP:
setEnabled(true);
mPrevX = MotionEvent.obtain(event).getX();
if (mScaleListener != null) {
mScaleListener.onTwoFingerEnd();
}
break;
case MotionEvent.ACTION_DOWN:
mPrevX = MotionEvent.obtain(event).getX();
break;
case MotionEvent.ACTION_MOVE:
final float eventX = event.getX();
float xDiff = Math.abs(eventX - mPrevX);
if (xDiff > mTouchSlop) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
class MyOnScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private static final String TAG = "MyOnScaleGestureListene";
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (mScaleListener != null) {
// Too sensitive, must change to other approach
float scaleFactor = detector.getScaleFactor();
Log.d(TAG, "onScale: " + scaleFactor);
if (scaleFactor > 1F) {
mScaleListener.onScaleUp(scaleFactor);
} else if (scaleFactor < 1F) {
mScaleListener.onScaleDown(scaleFactor);
} else {
// no scale
}
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
public interface ScaleListener {
void onTwoFingersStart();
void onTwoFingersEnd();
void onScaleUp(float scaleFactor);
void onScaleDown(float scaleFactor);
}
}
有什么方法可以降低ScaleGestureDetector.SimpleOnScaleGestureListener的灵敏度吗?如果没有办法,有没有办法解决?
这是一个显示问题的短视频
https://www.youtube.com/watch?v=0MItDNZ_o4c
你应该有一个容忍距离,在Android世界里叫"slop"。如果手势小于该公差,您应该忽略它。
private static final int SPAN_SLOP = 7;
...
@Override
public boolean onScale(@NonNull ScaleGestureDetector detector) {
if (gestureTolerance(detector)) {
// performing scaling
}
return true;
}
private boolean gestureTolerance(@NonNull ScaleGestureDetector detector) {
final float spanDelta = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
return spanDelta > SPAN_SLOP;
}
例如,您可以查看 @rallat 制作的开源应用程序展示。
在这里你可以找到source code and presentation at GOTO Copenhagen 2016。
我能够解决问题以满足我的要求。 @azizbekian 的回答并没有真正满足我的要求,因为 spanDelta
总是在我的手指太慢或太快时重置,它可以缩放,但仍然很难获得 4 列。但是从他的代码示例中,我能够创建一个解决方法。
我只需要使用 detector.getCurrentSpan()
保存初始比例距离(跨度)
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mInitialDistance = detector.getCurrentSpan();
return true;
}
然后每次onScale()
调用的时候直接和detector.getCurrentSpan()
比较。如果发生缩放,则使用 mInitialDistance = detector.getCurrentSpan();
重置初始距离。
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (gestureTolerance(detector)) {
if (mScaleListener != null) {
float scaleFactor = detector.getScaleFactor();
if (scaleFactor > 1F) {
mScaleListener.onScaleUp(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
} else if (scaleFactor < 1F) {
mScaleListener.onScaleDown(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
}
}
}
return true;
}
private boolean gestureTolerance(@NonNull ScaleGestureDetector detector) {
final float currentDistance = detector.getCurrentSpan();
final float distanceDelta = Math.abs(mInitialDistance - currentDistance);
return distanceDelta > mScaleTriggerDistance;
}
这是我自定义的 SwipeRefreshLayout 的完整代码
public class MyCustomSwipeRefreshLayout extends SwipeRefreshLayout {
private static final String TAG = "OneTouchRefreshFreeSwip";
private static final float DEFAULT_SCALE_TRIGGER_DISTANCE = 48;// in dp
private int mTouchSlop;
private float mPrevX;
private ScaleGestureDetector mScaleGestureDetector;
private ScaleListener mScaleListener;
private float mScaleTriggerDistance;
private float mInitialDistance;
public MyCustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public void setScaleListener(Context context, ScaleListener scaleListener) {
this.mScaleListener = scaleListener;
mScaleGestureDetector = new ScaleGestureDetector(context, new MyOnScaleGestureListener());
mScaleTriggerDistance = Util.dp2px(DEFAULT_SCALE_TRIGGER_DISTANCE, context);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mScaleGestureDetector != null) {
mScaleGestureDetector.onTouchEvent(event);
}
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
setEnabled(false);
if (mScaleListener != null) {
mScaleListener.onTwoFingersStart();
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (mScaleListener != null) {
mScaleListener.onTwoFingersEnd();
}
mPrevX = MotionEvent.obtain(event).getX();
setEnabled(true);
return true;
case MotionEvent.ACTION_DOWN:
mPrevX = MotionEvent.obtain(event).getX();
break;
case MotionEvent.ACTION_MOVE:
final float eventX = event.getX();
float xDiff = Math.abs(eventX - mPrevX);
if (xDiff > mTouchSlop) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
private boolean gestureTolerance(@NonNull ScaleGestureDetector detector) {
final float currentDistance = detector.getCurrentSpan();
final float distanceDelta = Math.abs(mInitialDistance - currentDistance);
return distanceDelta > mScaleTriggerDistance;
}
class MyOnScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (gestureTolerance(detector)) {
if (mScaleListener != null) {
float scaleFactor = detector.getScaleFactor();
if (scaleFactor > 1F) {
mScaleListener.onScaleUp(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
} else if (scaleFactor < 1F) {
mScaleListener.onScaleDown(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
}
}
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mInitialDistance = detector.getCurrentSpan();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
public interface ScaleListener {
void onTwoFingersStart();
void onTwoFingersEnd();
void onScaleUp(float scaleFactor);
void onScaleDown(float scaleFactor);
}
}
我有一个自定义的 SwipeRefreshLayout,里面有一个自定义的 GridView。我想根据 pinch/zoom 手势更改该 GridView 的列号。我已经成功地实施了它。问题是天平太敏感了
例如,我有第 3-5 列。缩放到3或5很容易,但是缩放到4很难,因为缩放本身太敏感了。
这是我自定义的 SwipeRefreshLayout class
/**
* This class contains fix from
*/
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout {
private int mTouchSlop;
private float mPrevX;
private ScaleGestureDetector mScaleGestureDetector;
private ScaleListener mScaleListener;
public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public void setScaleListener(Context context, ScaleListener scaleListener) {
this.mScaleListener = scaleListener;
mScaleGestureDetector = new ScaleGestureDetector(context, new MyOnScaleGestureListener());
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mScaleGestureDetector != null) {
mScaleGestureDetector.onTouchEvent(event);
}
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
setEnabled(false);
if (mScaleListener != null) {
mScaleListener.onTwoFingerStart();
}
break;
case MotionEvent.ACTION_POINTER_UP:
setEnabled(true);
mPrevX = MotionEvent.obtain(event).getX();
if (mScaleListener != null) {
mScaleListener.onTwoFingerEnd();
}
break;
case MotionEvent.ACTION_DOWN:
mPrevX = MotionEvent.obtain(event).getX();
break;
case MotionEvent.ACTION_MOVE:
final float eventX = event.getX();
float xDiff = Math.abs(eventX - mPrevX);
if (xDiff > mTouchSlop) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
class MyOnScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
private static final String TAG = "MyOnScaleGestureListene";
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (mScaleListener != null) {
// Too sensitive, must change to other approach
float scaleFactor = detector.getScaleFactor();
Log.d(TAG, "onScale: " + scaleFactor);
if (scaleFactor > 1F) {
mScaleListener.onScaleUp(scaleFactor);
} else if (scaleFactor < 1F) {
mScaleListener.onScaleDown(scaleFactor);
} else {
// no scale
}
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
public interface ScaleListener {
void onTwoFingersStart();
void onTwoFingersEnd();
void onScaleUp(float scaleFactor);
void onScaleDown(float scaleFactor);
}
}
有什么方法可以降低ScaleGestureDetector.SimpleOnScaleGestureListener的灵敏度吗?如果没有办法,有没有办法解决?
这是一个显示问题的短视频 https://www.youtube.com/watch?v=0MItDNZ_o4c
你应该有一个容忍距离,在Android世界里叫"slop"。如果手势小于该公差,您应该忽略它。
private static final int SPAN_SLOP = 7;
...
@Override
public boolean onScale(@NonNull ScaleGestureDetector detector) {
if (gestureTolerance(detector)) {
// performing scaling
}
return true;
}
private boolean gestureTolerance(@NonNull ScaleGestureDetector detector) {
final float spanDelta = Math.abs(detector.getCurrentSpan() - detector.getPreviousSpan());
return spanDelta > SPAN_SLOP;
}
例如,您可以查看 @rallat 制作的开源应用程序展示。
在这里你可以找到source code and presentation at GOTO Copenhagen 2016。
我能够解决问题以满足我的要求。 @azizbekian 的回答并没有真正满足我的要求,因为 spanDelta
总是在我的手指太慢或太快时重置,它可以缩放,但仍然很难获得 4 列。但是从他的代码示例中,我能够创建一个解决方法。
我只需要使用 detector.getCurrentSpan()
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mInitialDistance = detector.getCurrentSpan();
return true;
}
然后每次onScale()
调用的时候直接和detector.getCurrentSpan()
比较。如果发生缩放,则使用 mInitialDistance = detector.getCurrentSpan();
重置初始距离。
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (gestureTolerance(detector)) {
if (mScaleListener != null) {
float scaleFactor = detector.getScaleFactor();
if (scaleFactor > 1F) {
mScaleListener.onScaleUp(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
} else if (scaleFactor < 1F) {
mScaleListener.onScaleDown(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
}
}
}
return true;
}
private boolean gestureTolerance(@NonNull ScaleGestureDetector detector) {
final float currentDistance = detector.getCurrentSpan();
final float distanceDelta = Math.abs(mInitialDistance - currentDistance);
return distanceDelta > mScaleTriggerDistance;
}
这是我自定义的 SwipeRefreshLayout 的完整代码
public class MyCustomSwipeRefreshLayout extends SwipeRefreshLayout {
private static final String TAG = "OneTouchRefreshFreeSwip";
private static final float DEFAULT_SCALE_TRIGGER_DISTANCE = 48;// in dp
private int mTouchSlop;
private float mPrevX;
private ScaleGestureDetector mScaleGestureDetector;
private ScaleListener mScaleListener;
private float mScaleTriggerDistance;
private float mInitialDistance;
public MyCustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
super(context, attrs);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
public void setScaleListener(Context context, ScaleListener scaleListener) {
this.mScaleListener = scaleListener;
mScaleGestureDetector = new ScaleGestureDetector(context, new MyOnScaleGestureListener());
mScaleTriggerDistance = Util.dp2px(DEFAULT_SCALE_TRIGGER_DISTANCE, context);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
if (mScaleGestureDetector != null) {
mScaleGestureDetector.onTouchEvent(event);
}
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
setEnabled(false);
if (mScaleListener != null) {
mScaleListener.onTwoFingersStart();
}
break;
case MotionEvent.ACTION_POINTER_UP:
if (mScaleListener != null) {
mScaleListener.onTwoFingersEnd();
}
mPrevX = MotionEvent.obtain(event).getX();
setEnabled(true);
return true;
case MotionEvent.ACTION_DOWN:
mPrevX = MotionEvent.obtain(event).getX();
break;
case MotionEvent.ACTION_MOVE:
final float eventX = event.getX();
float xDiff = Math.abs(eventX - mPrevX);
if (xDiff > mTouchSlop) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
private boolean gestureTolerance(@NonNull ScaleGestureDetector detector) {
final float currentDistance = detector.getCurrentSpan();
final float distanceDelta = Math.abs(mInitialDistance - currentDistance);
return distanceDelta > mScaleTriggerDistance;
}
class MyOnScaleGestureListener extends ScaleGestureDetector.SimpleOnScaleGestureListener {
@Override
public boolean onScale(ScaleGestureDetector detector) {
if (gestureTolerance(detector)) {
if (mScaleListener != null) {
float scaleFactor = detector.getScaleFactor();
if (scaleFactor > 1F) {
mScaleListener.onScaleUp(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
} else if (scaleFactor < 1F) {
mScaleListener.onScaleDown(scaleFactor);
mInitialDistance = detector.getCurrentSpan();
}
}
}
return true;
}
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mInitialDistance = detector.getCurrentSpan();
return true;
}
@Override
public void onScaleEnd(ScaleGestureDetector detector) {
}
}
public interface ScaleListener {
void onTwoFingersStart();
void onTwoFingersEnd();
void onScaleUp(float scaleFactor);
void onScaleDown(float scaleFactor);
}
}