diff options
Diffstat (limited to 'app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java')
-rw-r--r-- | app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java | 249 |
1 files changed, 249 insertions, 0 deletions
diff --git a/app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java b/app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java new file mode 100644 index 000000000..33672f60c --- /dev/null +++ b/app/src/main/java/app/fedilab/android/helper/CirclesDrawingView.java @@ -0,0 +1,249 @@ +package app.fedilab.android.helper; + +import android.content.Context; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.util.AttributeSet; +import android.util.SparseArray; +import android.view.MotionEvent; +import android.view.View; + +import androidx.core.content.res.ResourcesCompat; + +import java.util.HashSet; +import java.util.Random; + +import app.fedilab.android.R; + +//Original work at https://stackoverflow.com/a/17830245 +public class CirclesDrawingView extends View { + + + // Radius limit in pixels + private final static int RADIUS_LIMIT = 100; + private static final int CIRCLES_LIMIT = 1; + private final Random mRadiusGenerator = new Random(); + /** + * All available circles + */ + private final HashSet<CircleArea> mCircles = new HashSet<>(CIRCLES_LIMIT); + private final SparseArray<CircleArea> mCirclePointer = new SparseArray<>(CIRCLES_LIMIT); + /** + * Paint to draw circles + */ + private Paint mCirclePaint; + private CircleArea touchedCircle; + + /** + * Default constructor + * + * @param ct {@link android.content.Context} + */ + public CirclesDrawingView(final Context ct) { + super(ct); + init(ct); + } + + public CirclesDrawingView(final Context ct, final AttributeSet attrs) { + super(ct, attrs); + init(ct); + } + + public CirclesDrawingView(final Context ct, final AttributeSet attrs, final int defStyle) { + super(ct, attrs, defStyle); + init(ct); + } + + public CircleArea getTouchedCircle() { + return this.touchedCircle; + } + + private void init(final Context ct) { + // Generate bitmap used for background + mCirclePaint = new Paint(); + + mCirclePaint.setColor(ResourcesCompat.getColor(getContext().getResources(), R.color.cyanea_accent, getContext().getTheme())); + mCirclePaint.setStrokeWidth(10); + mCirclePaint.setStyle(Paint.Style.STROKE); + } + + @Override + public void onDraw(final Canvas canv) { + // background bitmap to cover all area + for (CircleArea circle : mCircles) { + canv.drawCircle(circle.centerX, circle.centerY, circle.radius, mCirclePaint); + } + } + + @Override + public boolean onTouchEvent(final MotionEvent event) { + boolean handled = false; + + + int xTouch; + int yTouch; + int pointerId; + int actionIndex = event.getActionIndex(); + + // get touch event coordinates and make transparent circle from it + switch (event.getActionMasked()) { + case MotionEvent.ACTION_DOWN: + // it's the first pointer, so clear all existing pointers data + clearCirclePointer(); + + xTouch = (int) event.getX(0); + yTouch = (int) event.getY(0); + + // check if we've touched inside some circle + touchedCircle = obtainTouchedCircle(xTouch, yTouch); + touchedCircle.centerX = xTouch; + touchedCircle.centerY = yTouch; + mCirclePointer.put(event.getPointerId(0), touchedCircle); + invalidate(); + handled = true; + break; + + case MotionEvent.ACTION_POINTER_DOWN: + // It secondary pointers, so obtain their ids and check circles + pointerId = event.getPointerId(actionIndex); + + xTouch = (int) event.getX(actionIndex); + yTouch = (int) event.getY(actionIndex); + + // check if we've touched inside some circle + touchedCircle = obtainTouchedCircle(xTouch, yTouch); + + mCirclePointer.put(pointerId, touchedCircle); + touchedCircle.centerX = xTouch; + touchedCircle.centerY = yTouch; + invalidate(); + handled = true; + break; + + case MotionEvent.ACTION_MOVE: + final int pointerCount = event.getPointerCount(); + + for (actionIndex = 0; actionIndex < pointerCount; actionIndex++) { + // Some pointer has moved, search it by pointer id + pointerId = event.getPointerId(actionIndex); + + xTouch = (int) event.getX(actionIndex); + yTouch = (int) event.getY(actionIndex); + + touchedCircle = mCirclePointer.get(pointerId); + + if (null != touchedCircle) { + touchedCircle.centerX = xTouch; + touchedCircle.centerY = yTouch; + } + } + invalidate(); + handled = true; + break; + + case MotionEvent.ACTION_UP: + clearCirclePointer(); + invalidate(); + handled = true; + break; + + case MotionEvent.ACTION_POINTER_UP: + // not general pointer was up + pointerId = event.getPointerId(actionIndex); + + mCirclePointer.remove(pointerId); + invalidate(); + handled = true; + break; + + case MotionEvent.ACTION_CANCEL: + handled = true; + break; + + default: + // do nothing + break; + } + + return super.onTouchEvent(event) || handled; + } + + /** + * Clears all CircleArea - pointer id relations + */ + private void clearCirclePointer() { + + mCirclePointer.clear(); + } + + /** + * Search and creates new (if needed) circle based on touch area + * + * @param xTouch int x of touch + * @param yTouch int y of touch + * @return obtained {@link CircleArea} + */ + private CircleArea obtainTouchedCircle(final int xTouch, final int yTouch) { + CircleArea touchedCircle = getTouchedCircle(xTouch, yTouch); + + if (null == touchedCircle) { + touchedCircle = new CircleArea(xTouch, yTouch, mRadiusGenerator.nextInt(RADIUS_LIMIT) + RADIUS_LIMIT); + + if (mCircles.size() == CIRCLES_LIMIT) { + // remove first circle + mCircles.clear(); + } + + mCircles.add(touchedCircle); + } + + return touchedCircle; + } + + /** + * Determines touched circle + * + * @param xTouch int x touch coordinate + * @param yTouch int y touch coordinate + * @return {@link CircleArea} touched circle or null if no circle has been touched + */ + private CircleArea getTouchedCircle(final int xTouch, final int yTouch) { + CircleArea touched = null; + + for (CircleArea circle : mCircles) { + if ((circle.centerX - xTouch) * (circle.centerX - xTouch) + (circle.centerY - yTouch) * (circle.centerY - yTouch) <= circle.radius * circle.radius) { + touched = circle; + break; + } + } + + return touched; + } + + @Override + protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + new Rect(0, 0, getMeasuredWidth(), getMeasuredHeight()); + } + + /** + * Stores data about single circle + */ + public static class CircleArea { + public int centerX; + public int centerY; + int radius; + + CircleArea(int centerX, int centerY, int radius) { + this.radius = radius; + this.centerX = centerX; + this.centerY = centerY; + } + + @Override + public String toString() { + return "Circle[" + centerX + ", " + centerY + ", " + radius + "]"; + } + } +}
\ No newline at end of file |