我有BottomDrawer
的实现,它实现GestureDetector
以确定用户何时翻转底部抽屉。我有两个问题:
1)onFling
并不总是被调用。这是一个相对较小的问题。
2)传递给onFling
的速度往往是错误的方向。这是一个巨大的问题,因为它导致用户将抽屉投掷关闭,并且打开,反之亦然。这是以前任何人都遇到过的问题吗?怎么解决这个问题?我想也许忽略了不到一定幅度的晃动,但是错误的速度有时会非常大,所以这并没有帮助。
以下是我的BottomDrawer
课程的代码,如果有帮助,我也可以将演示项目上传到GitHub:
package com.cbendeb.bottomdrawer.view;
import android.animation.Animator.AnimatorListener;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.widget.LinearLayout;
/**
* A bottom extensible drawer. Displays two views, one as the handle and one as the content.
*
* @author catherine
*/
public final class BottomDrawer extends LinearLayout implements GestureDetector.OnGestureListener {
private static final String TAG = BottomDrawer.class.getSimpleName();
private static final int ANIMATE_DURATION = 500;
private float lastTouchY;
// so we don't have to recalculate this stuff all the time
private float openY;
private float closedY;
private float currentY = -1;
// listeners for the end of the open and close animations
private AnimatorListener closeListener;
private AnimatorListener openListener;
// gestures
private GestureDetector gestureDetector;
private boolean flung = false;
public BottomDrawer(Context context) {
super(context);
init(context);
}
public BottomDrawer(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public BottomDrawer(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context) {
setOrientation(LinearLayout.VERTICAL);
gestureDetector = new GestureDetector(context, this);
}
/**
* @return true if the drawer is expanded
*/
public boolean isOpen() {
return currentY != closedY;
}
/**
* Opens the drawer.
*/
public void open() {
animate()
.translationY(0)
.setDuration(
Math.max(0, (int) (ANIMATE_DURATION * (getY() - openY) / (closedY - openY))))
.setListener(openListener).start();
}
/**
* Closes the drawer.
*/
public void close() {
animate()
.translationY(getChildAt(1).getHeight())
.setDuration(
Math.max(0,
(int) (ANIMATE_DURATION * (closedY - getY()) / (closedY - openY))))
.setListener(closeListener).start();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = ev.getActionMasked();
final float y = ev.getRawY();
gestureDetector.onTouchEvent(ev);
switch (action) {
case MotionEvent.ACTION_DOWN:
break;
case MotionEvent.ACTION_MOVE:
// Calculate the distance moved
final float dy = y - lastTouchY;
if (getY() + dy < closedY && getY() + dy > openY) {
setY(getY() + dy);
invalidate();
}
break;
case MotionEvent.ACTION_CANCEL:
flung = false;
break;
case MotionEvent.ACTION_UP:
Log.d(TAG, "flung: " + flung);
if (!flung) {
if (getY() >= (openY + closedY) / 2) {
Log.d(TAG, "closing");
close();
} else {
Log.d(TAG, "opening");
open();
}
}
flung = false;
break;
case MotionEvent.ACTION_POINTER_UP:
break;
}
lastTouchY = y;
return true;
}
@Override
public void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
openY = getBottom() - getChildAt(0).getHeight() - getChildAt(1).getHeight();
closedY = getBottom() - getChildAt(0).getHeight();
if (currentY == -1) {
currentY = closedY;
}
if (changed && isOpen()) {
setY(openY);
} else if (changed && !isOpen()) {
setY(closedY);
}
}
@Override
public boolean onDown(MotionEvent e) {
return true;
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
Log.d(TAG, "onFling velocityY: " + velocityY + " currentY " + currentY);
final float y = getY();
if (velocityY < 0 && y > openY && y < closedY) { // moving upwards
Log.d(TAG, "onFling opening");
open();
} else if (velocityY > 0 && y > openY && y < closedY) { // moving downwards
Log.d(TAG, "onFling closing");
close();
}
flung = true;
return true;
}
@Override
public void onLongPress(MotionEvent e) {
// Do nothing
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
// Do nothing
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
// Do nothing
return false;
}
}
我正在研究GrepCode上的GestureDetector实现......但如果有更简单的方法来解决这个问题会很好。