私のアプリケーションでは、移動イベントとクリックイベントの両方を処理する必要があります。
クリックは、1つのACTION_DOWNアクション、複数のACTION_MOVEアクション、および1つのACTION_UPアクションのシーケンスです。理論的には、ACTION_DOWNイベントを取得してからACTION_UPイベントを取得した場合、ユーザーがビューをクリックしたことを意味します。
しかし、実際には、このシーケンスは一部のデバイスでは機能しません。私のSamsung Galaxy Gioでは、[ビュー]をクリックするだけでこのようなシーケンスが表示されます:ACTION_DOWN、ACTION_MOVEを数回、そしてACTION_UP。つまりACTION_MOVEアクションコードを使用すると、予期しないOnTouchEventが発生します。シーケンスACTION_DOWN-> ACTION_UPを取得しません(またはほとんど実行しません)。
OnClickListenerはクリックの位置を提供しないため、使用することもできません。では、クリックイベントを検出して移動とはどう違うのでしょうか?
非常にシンプルで、指が動かされることを心配する必要がない別のソリューションがあります。単に距離が移動したとしてクリックを基準にしている場合、クリックとロングクリックをどのように区別できますか。
これにもっとスマートを加えて、移動した距離を含めることもできますが、ユーザーが200ミリ秒で移動できる距離がクリックではなく移動を構成するはずなのに、私はまだインスタンスに出会っていません。
setOnTouchListener(new OnTouchListener() {
private static final int MAX_CLICK_DURATION = 200;
private long startClickTime;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
startClickTime = Calendar.getInstance().getTimeInMillis();
break;
}
case MotionEvent.ACTION_UP: {
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
if(clickDuration < MAX_CLICK_DURATION) {
//click event has occurred
}
}
}
return true;
}
});
考慮に入れて最高の結果を得ました:
ACTION_DOWN
およびACTION_UP
イベント。異なる画面をよりよくサポートするために、ピクセルではなく density-indepenent pixels で最大許容距離を指定したかったのです。たとえば、15 DP。例:
/**
* Max allowed duration for a "click", in milliseconds.
*/
private static final int MAX_CLICK_DURATION = 1000;
/**
* Max allowed distance to move during a "click", in DP.
*/
private static final int MAX_CLICK_DISTANCE = 15;
private long pressStartTime;
private float pressedX;
private float pressedY;
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
pressStartTime = System.currentTimeMillis();
pressedX = e.getX();
pressedY = e.getY();
break;
}
case MotionEvent.ACTION_UP: {
long pressDuration = System.currentTimeMillis() - pressStartTime;
if (pressDuration < MAX_CLICK_DURATION && distance(pressedX, pressedY, e.getX(), e.getY()) < MAX_CLICK_DISTANCE) {
// Click event has occurred
}
}
}
}
private static float distance(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy);
return pxToDp(distanceInPx);
}
private static float pxToDp(float px) {
return px / getResources().getDisplayMetrics().density;
}
ここでの考え方は 宝石の解 と同じですが、これらの違いはあります:
更新(2015): ガブリエルの微調整版 もご覧ください。
Jonik's lead 私は少し微調整したバージョンを作成しました。指を動かしてから放す前にその場に戻るとクリックとして登録されません:
だからここに私の解決策があります:
/**
* Max allowed duration for a "click", in milliseconds.
*/
private static final int MAX_CLICK_DURATION = 1000;
/**
* Max allowed distance to move during a "click", in DP.
*/
private static final int MAX_CLICK_DISTANCE = 15;
private long pressStartTime;
private float pressedX;
private float pressedY;
private boolean stayedWithinClickDistance;
@Override
public boolean onTouchEvent(MotionEvent e) {
switch (e.getAction()) {
case MotionEvent.ACTION_DOWN: {
pressStartTime = System.currentTimeMillis();
pressedX = e.getX();
pressedY = e.getY();
stayedWithinClickDistance = true;
break;
}
case MotionEvent.ACTION_MOVE: {
if (stayedWithinClickDistance && distance(pressedX, pressedY, e.getX(), e.getY()) > MAX_CLICK_DISTANCE) {
stayedWithinClickDistance = false;
}
break;
}
case MotionEvent.ACTION_UP: {
long pressDuration = System.currentTimeMillis() - pressStartTime;
if (pressDuration < MAX_CLICK_DURATION && stayedWithinClickDistance) {
// Click event has occurred
}
}
}
}
private static float distance(float x1, float y1, float x2, float y2) {
float dx = x1 - x2;
float dy = y1 - y2;
float distanceInPx = (float) Math.sqrt(dx * dx + dy * dy);
return pxToDp(distanceInPx);
}
private static float pxToDp(float px) {
return px / getResources().getDisplayMetrics().density;
}
検出器を使用してください、それは動作し、ドラッグの場合には発生しません
フィールド:
private GestureDetector mTapDetector;
初期化:
mTapDetector = new GestureDetector(context,new GestureTap());
内部クラス:
class GestureTap extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onDoubleTap(MotionEvent e) {
return true;
}
@Override
public boolean onSingleTapConfirmed(MotionEvent e) {
// TODO: handle tap here
return true;
}
}
onTouch:
@Override
public boolean onTouch(View v, MotionEvent event) {
mTapDetector.onTouchEvent(event);
return true;
}
楽しい :)
クリックイベントの認識を最も最適化するには、2つのことを考慮する必要があります。
実際、スティムソーニとニーティラジャンによって与えられた論理を組み合わせます
だからここに私の解決策があります:
view.setOnTouchListener(new OnTouchListener() {
private final int MAX_CLICK_DURATION = 400;
private final int MAX_CLICK_DISTANCE = 5;
private long startClickTime;
private float x1;
private float y1;
private float x2;
private float y2;
private float dx;
private float dy;
@Override
public boolean onTouch(View view, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN:
{
startClickTime = Calendar.getInstance().getTimeInMillis();
x1 = event.getX();
y1 = event.getY();
break;
}
case MotionEvent.ACTION_UP:
{
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
x2 = event.getX();
y2 = event.getY();
dx = x2-x1;
dy = y2-y1;
if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE)
Log.v("","On Item Clicked:: ");
}
}
return false;
}
});
以下のコードはあなたの問題を解決します
@Override public boolean onTouchEvent(MotionEvent event){ switch(event.getAction()){ case(MotionEvent.ACTION_DOWN) : x1 = event.getX(); y1 = event.getY(); break; case(MotionEvent.ACTION_UP):{ x2 = event.getX(); y2 = event.getY(); dx = x2-x1; dy = y2-y1; if(Math.abs(dx)> Math.abs(dy)) { if(dx> 0)move(1); //right else if(dx == 0)move(5); //クリック else move(2); //left } else { if(dy> 0)move(3); // down else if(dy == 0)move(5); //クリック else move(4); //up } } } return true; }
ACTION_MOVEが発生せずにACTION_DOWNが発生することは非常に困難です。最初のタッチが発生した場所とは異なる場所にある画面上の指のわずかな痙攣によって、MOVEイベントがトリガーされます。また、指の圧力の変化もMOVEイベントをトリガーすると考えています。 Action_Moveメソッドでifステートメントを使用して、元のDOWNモーションから移動が発生した距離を判断しようとします。移動が設定された半径の外側で発生した場合、MOVEアクションが発生します。これはおそらく、あなたがしようとしていることを行うのに最適なリソース効率の良い方法ではありませんが、機能するはずです。
Gil SHの回答を使用して、onSingleTapUp()
ではなくonSingleTapConfirmed()
を実装することで改善しました。はるかに高速で、ドラッグ/移動してもビューをクリックしません。
ジェスチャータップ:
public class GestureTap extends GestureDetector.SimpleOnGestureListener {
@Override
public boolean onSingleTapUp(MotionEvent e) {
button.performClick();
return true;
}
}
次のように使用します。
final GestureDetector gestureDetector = new GestureDetector(getApplicationContext(), new GestureTap());
button.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
gestureDetector.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
return true;
case MotionEvent.ACTION_UP:
return true;
case MotionEvent.ACTION_MOVE:
return true;
}
return false;
}
});
クリックのみに反応する場合は、次を使用します。
if (event.getAction() == MotionEvent.ACTION_UP) {
}
上記の答えに追加すると、onClickアクションとDragアクションの両方を実装したい場合、以下の私のコードを使用できます。 @Stimsoniの助けを借りて:
// assumed all the variables are declared globally;
public boolean onTouch(View view, MotionEvent event) {
int MAX_CLICK_DURATION = 400;
int MAX_CLICK_DISTANCE = 5;
switch (event.getAction())
{
case MotionEvent.ACTION_DOWN: {
long clickDuration1 = Calendar.getInstance().getTimeInMillis() - startClickTime;
startClickTime = Calendar.getInstance().getTimeInMillis();
x1 = event.getX();
y1 = event.getY();
break;
}
case MotionEvent.ACTION_UP:
{
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
x2 = event.getX();
y2 = event.getY();
dx = x2-x1;
dy = y2-y1;
if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) {
Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show();
Log.d("clicked", "On Item Clicked:: ");
// imageClickAction((ImageView) view,rl);
}
}
case MotionEvent.ACTION_MOVE:
long clickDuration = Calendar.getInstance().getTimeInMillis() - startClickTime;
x2 = event.getX();
y2 = event.getY();
dx = x2-x1;
dy = y2-y1;
if(clickDuration < MAX_CLICK_DURATION && dx < MAX_CLICK_DISTANCE && dy < MAX_CLICK_DISTANCE) {
//Toast.makeText(getApplicationContext(), "item clicked", Toast.LENGTH_SHORT).show();
// Log.d("clicked", "On Item Clicked:: ");
// imageClickAction((ImageView) view,rl);
}
else {
ClipData clipData = ClipData.newPlainText("", "");
View.DragShadowBuilder shadowBuilder = new View.DragShadowBuilder(view);
//Toast.makeText(getApplicationContext(), "item dragged", Toast.LENGTH_SHORT).show();
view.startDrag(clipData, shadowBuilder, view, 0);
}
break;
}
return false;
}