我有一个自定义SwipeRefreshLayout,里面有自定义GridView。我想根据捏合/缩放手势更改GridView的列号。我已经成功实现了它。问题是,规模过于敏感。
例如,我的列号为3-5。它很容易扩展到3或5,但要使它变硬4,因为刻度本身太敏感了。
这是我的自定义SwipeRefreshLayout类
/**
* This class contains fix from http://stackoverflow.com/questions/23989910/horizontalscrollview-inside-swiperefreshlayout
*/
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
答案 0 :(得分:3)
你应该有一个容差距离,在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制作的展示开源应用。
答案 1 :(得分:0)
我能够解决问题以满足我的要求。 @ azizbekian的回答并不能满足我的要求,因为如果我的手指太慢而spanDelta
总是重置,如果我的手指太快,它可以缩放,但仍然很难得到4列。但是从他的代码示例中,我能够创建一个解决方法。
我只需要使用detector.getCurrentSpan()
@Override
public boolean onScaleBegin(ScaleGestureDetector detector) {
mInitialDistance = detector.getCurrentSpan();
return true;
}
然后每次detector.getCurrentSpan()
调用时直接与onScale()
进行比较。如果出现比例,则使用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);
}
}