Identify triple tap on custom view

2019-03-16 19:26发布

问题:

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