问题
I want to draw circle, whenever user taps on a custom view, and based on tap count circle color changes.
Single Tap : YELLOW CIRCLE
Double Tap : GREEN CIRCLE
Triple Tap : RED COLOR
Problem is that, i made one custom view that will count tap event based on time, but some time it miss the first tap. which cause the problem in view.
Following code shows all my effort to make above custom view.
TripleTapView
package com.slk.car_rating_app;
import java.util.ArrayList;
import java.util.Date;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.CountDownTimer;
import android.view.MotionEvent;
import android.view.View;
public class TripleTapView extends View {
// Set the tap delay in milliseconds
protected static final long TAP_MAX_DELAY = 500L;
// Radius to capture tap within bound
final static int RADIUS = 30;
// Store all points with tap count
public ArrayList<CustomPoint> point = new ArrayList<CustomPoint>();
// Context to access view
Context context;
Paint paint;
private long thisTime = 0, prevTime = 0;
private boolean firstTap = true, doubleTap = false;;
float stopX, stopY, startX, startY;
RectF area_rect;
TapCounter tapCounter = new TapCounter(TAP_MAX_DELAY, TAP_MAX_DELAY);
public TripleTapView(Context context) {
super(context);
this.context = context;
paint = new Paint();
paint.setColor(Color.GREEN);
paint.setAntiAlias(true);
paint.setDither(true);
paint.setStyle(Paint.Style.FILL);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(2);
}
@SuppressLint("DrawAllocation")
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
for (CustomPoint point_temp : point) {
// For changing tap circle color based on tap count
switch (point_temp.count) {
case 1:
paint.setColor(Color.YELLOW);
break;
case 2:
paint.setColor(Color.GREEN);
break;
case 3:
paint.setColor(Color.RED);
break;
}
canvas.drawCircle(point_temp.point.x, point_temp.point.y, 10, paint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
stopX = event.getX();
stopY = event.getY();
if (firstTap) {
addFirstTap();
} else if (doubleTap) {
prevTime = thisTime;
thisTime = new Date().getTime();
if (thisTime > prevTime) {
if ((thisTime - prevTime) <= TAP_MAX_DELAY) {
if (area_rect.contains(stopX, stopY))
doubleTap = false;
else {
addPoint(1);
addFirstTap();
}
} else {
addPoint(1);
firstTap = true;
}
} else {
firstTap = true;
}
} else {
prevTime = thisTime;
thisTime = new Date().getTime();
if (thisTime > prevTime) {
if ((thisTime - prevTime) <= TAP_MAX_DELAY) {
if (area_rect.contains(stopX, stopY)) {
addPoint(3);
firstTap = true;
} else {
addPoint(2);
addFirstTap();
}
} else {
addPoint(2);
firstTap = true;
}
} else {
firstTap = true;
}
}
}
return true;
}
void addPoint(int tapCount) {
point.add(new CustomPoint(new PointF(startX, startY), tapCount));
invalidate();
}
void addFirstTap() {
thisTime = new Date().getTime();
firstTap = false;
doubleTap = true;
startX = stopX;
startY = stopY;
area_rect = new RectF(stopX - RADIUS, stopY - RADIUS, stopX + RADIUS,
stopY + RADIUS);
tapCounter.resetCounter();
}
class TapCounter extends CountDownTimer {
public TapCounter(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {
if (doubleTap) {
prevTime = thisTime;
thisTime = new Date().getTime();
if (thisTime > prevTime) {
if ((thisTime - prevTime) <= TAP_MAX_DELAY) {
doubleTap = false;
} else {
addPoint(1);
firstTap = true;
}
} else {
firstTap = true;
}
} else if (!firstTap && !doubleTap) {
prevTime = thisTime;
thisTime = new Date().getTime();
if (thisTime > prevTime) {
if ((thisTime - prevTime) <= TAP_MAX_DELAY) {
addPoint(2);
firstTap = true;
}
} else {
firstTap = true;
}
}
}
@Override
public void onTick(long millisUntilFinished) {
}
public void resetCounter() {
start();
}
}
}
Please help me to fix this issue.
回答1:
This code will do what you need. I simplified your class.
import java.util.ArrayList;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PointF;
import android.graphics.RectF;
import android.os.CountDownTimer;
import android.view.MotionEvent;
import android.view.View;
public class TripleTapView extends View {
// Set the tap delay in milliseconds
protected static final long TAP_MAX_DELAY = 500L;
// Radius to capture tap within bound
private final static int RADIUS = 30;
// Store all points with tap count
public ArrayList<CustomPoint> _points = new ArrayList<CustomPoint>();
Context _context;
Paint _paint;
TapCounter _tapCounter = new TapCounter(TAP_MAX_DELAY, TAP_MAX_DELAY);
public TripleTapView(Context context) {
super(context);
_context = context;
_paint = new Paint();
_paint.setAntiAlias(true);
_paint.setDither(true);
_paint.setStyle(Paint.Style.FILL);
_paint.setStrokeJoin(Paint.Join.ROUND);
_paint.setStrokeCap(Paint.Cap.ROUND);
_paint.setStrokeWidth(2);
}
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
for (CustomPoint point_temp : _points) {
// For changing tap circle color based on tap count
switch (point_temp.count) {
case 1:
_paint.setColor(Color.YELLOW);
break;
case 2:
_paint.setColor(Color.GREEN);
break;
case 3:
_paint.setColor(Color.RED);
break;
}
canvas.drawCircle(point_temp.point.x, point_temp.point.y, 10,
_paint);
}
}
private RectF _lastTapArea;
private int _lastTapCount = 0;
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
_tapCounter.resetCounter();
float x = event.getX();
float y = event.getY();
if (_lastTapArea != null) {
if (_lastTapArea.contains(x, y)) {
if (_lastTapCount < 3) {
_lastTapCount++;
} else {
addPoint(_lastTapArea.centerX(),
_lastTapArea.centerY(), _lastTapCount);
_lastTapCount = 1;
}
} else {
addPoint(_lastTapArea.centerX(), _lastTapArea.centerY(),
_lastTapCount);
_lastTapCount = 1;
_lastTapArea = new RectF(x - RADIUS, y - RADIUS,
x + RADIUS, y + RADIUS);
}
} else {
_lastTapCount = 1;
_lastTapArea = new RectF(x - RADIUS, y - RADIUS, x + RADIUS, y
+ RADIUS);
}
return true;
}
return false;
}
void addPoint(float x, float y, int tapCount) {
_points.add(new CustomPoint(new PointF(x, y), tapCount));
invalidate();
}
class TapCounter extends CountDownTimer {
public TapCounter(long millisInFuture, long countDownInterval) {
super(millisInFuture, countDownInterval);
}
@Override
public void onFinish() {
if (_lastTapArea != null) {
if (_lastTapCount > 0)
addPoint(_lastTapArea.centerX(), _lastTapArea.centerY(),
_lastTapCount);
_lastTapCount = 0;
_lastTapArea = null;
}
}
@Override
public void onTick(long millisUntilFinished) {
}
public void resetCounter() {
start();
}
}
}
来源:https://stackoverflow.com/questions/15861638/identify-triple-tap-on-custom-view