Hitesh Patel Hitesh Patel - 15 days ago 5
Android Question

Identify triple tap on custom view

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.

Answer

If I think I understand your problem, this code will do what you need. I simplified quite your class. Tell me if it works for you.

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();
        }
    }
}
Comments