package hk.hku.cs.srli.supermonkey; import android.animation.Animator; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.ShapeDrawable; import android.graphics.drawable.shapes.OvalShape; import android.util.Log; import android.view.View; import android.view.animation.AccelerateInterpolator; import android.view.animation.DecelerateInterpolator; public class CalibrationView extends View implements ValueAnimator.AnimatorUpdateListener { private DotWrapper dot; private Animator.AnimatorListener listener; public CalibrationView(Context context) { super(context); // Enter low profile mode to avoid UI distraction. setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE); dot = new DotWrapper(); // Put the dot outside the top left corner. dot.setX(0 - dot.DEFAULT_R); dot.setY(0 - dot.DEFAULT_R); dot.setR(dot.DEFAULT_R); listener = null; } @Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { super.onSizeChanged(w, h, oldw, oldh); dot.setX(w / 2); dot.setY(h / 4); } public void setListener(Animator.AnimatorListener listener) { this.listener = listener; } public void movePointTo(float x, float y) { // Convert from normalized coordinates to position on screen. float destx = x * getWidth(); float desty = y * getHeight(); Log.v("CalibrationView", "movePointTo: " + destx + ", " + desty); startAnimation(animatedMoveTo(destx, desty), 1000); } public void shrinkPoint() { Log.v("CalibrationView", "shrinkPoint"); startAnimation(animatedResize(5), 1000); } public void expandPoint() { Log.v("CalibrationView", "expandPoint"); startAnimation(animatedResize(dot.DEFAULT_R), 1000); } public void hidePoint() { Log.v("CalibrationView", "hidePoint"); // Remove the point from view. startAnimation(animatedResize(0), 500); } private Animator animatedMoveTo(float x, float y) { ObjectAnimator oax = ObjectAnimator.ofFloat(dot, "x", dot.x, x); ObjectAnimator oay = ObjectAnimator.ofFloat(dot, "y", dot.y, y); oax.addUpdateListener(this); AnimatorSet aset = new AnimatorSet(); aset.playTogether(oax, oay); return aset; } private Animator animatedResize(float size) { ObjectAnimator oar = null; oar = ObjectAnimator.ofFloat(dot, "r", dot.r, size); // Accelerate when shrinks. if (dot.r > size) { oar.setInterpolator(new AccelerateInterpolator()); } else { oar.setInterpolator(new DecelerateInterpolator()); } oar.addUpdateListener(this); return oar; } private void startAnimation(Animator ani, long duration) { ani.setDuration(duration); ani.setStartDelay(500); if (listener != null) ani.addListener(listener); ani.start(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); dot.draw(canvas); } @Override public void onAnimationUpdate(ValueAnimator animation) { invalidate(); } private class DotWrapper { // Default radius is 20dp. public final float DEFAULT_R = 20 * getResources().getDisplayMetrics().density; private ShapeDrawable drawable; private Paint bgPaint; private float r, x, y; public DotWrapper() { this.r = DEFAULT_R; this.x = 0; this.y = 0; drawable = new ShapeDrawable(new OvalShape()); drawable.getPaint().setColor(0xFF74AC23); bgPaint = new Paint(); bgPaint.setColor(Color.BLACK); } public void setX(float x) { this.x = x; } public void setY(float y) { this.y = y; } public void setR(float r) { this.r = r; } public void draw(Canvas canvas) { updateDrawable(); drawable.draw(canvas); // Draw a dot at the center of the shape. canvas.drawOval(new RectF(x - 2, y - 2, x + 2, y + 2), bgPaint); } private void updateDrawable() { int left = Math.round(x - r); int top = Math.round(y - r); int right = Math.round(x + r); int bottom = Math.round(y + r); drawable.setBounds(new Rect(left, top, right, bottom)); } } }