From 979da94e2445ab4df8d34ae25f57684b816834d5 Mon Sep 17 00:00:00 2001 From: zbsz Date: Sat, 8 Aug 2015 21:43:24 +0200 Subject: [PATCH] Add ContinuousAnimator, add couple util methods to Point and DragGesture classes. --- .../scala/com/geteit/view/TouchReactor.scala | 49 +++++++++++--- .../scala/com/geteit/view/ValueAnimator.scala | 64 ++++++++++++++++++- 2 files changed, 103 insertions(+), 10 deletions(-) diff --git a/src/main/scala/com/geteit/view/TouchReactor.scala b/src/main/scala/com/geteit/view/TouchReactor.scala index 231b2ec..8f91ccd 100644 --- a/src/main/scala/com/geteit/view/TouchReactor.scala +++ b/src/main/scala/com/geteit/view/TouchReactor.scala @@ -85,7 +85,39 @@ class Point(var x: Float, var y: Float) { this } - def dst(x: Float, y: Float) = math.sqrt((x - this.x) * (x - this.x) + (y - this.y) * (y - this.y)).toFloat + def angle = Point.angle(x, y) + + def dot(p: Point): Float = dot(p.x, p.y) + + def dot(x: Float, y: Float): Float = x * this.x + y * this.y + + def len2 = x * x + y * y + + def len = math.sqrt(len2) + + def dst(x: Float, y: Float) = math.sqrt(dst2(x, y)).toFloat + + def dst2(x: Float, y: Float) = (x - this.x) * (x - this.x) + (y - this.y) * (y - this.y) + + def rotate(degrees: Float): Point = { + val rad = degrees * 0.017453292F + val cos = math.cos(rad).toFloat + val sin = math.sin(rad).toFloat + val newX = x * cos - y * sin + val newY = x * sin + y * cos + x = newX + y = newY + this + } + + override def toString = s"Point($x, $y)" +} + +object Point { + def angle(x: Float, y: Float) = { + val a = math.atan2(y, x).toFloat * 57.295776F + if (a < 0) a + 360 else a + } } class ClickGesture(context: Context, reactor: TouchReactor, longClickTimeout: FiniteDuration = 500.millis)(implicit eventContext: EventContext) { @@ -141,16 +173,16 @@ class ClickGesture(context: Context, reactor: TouchReactor, longClickTimeout: Fi } object DragGesture { - val NONE = 0 val HORIZONTAL = 1 val VERTICAL = 2 + val BOTH = HORIZONTAL | VERTICAL - private def isHorizontal(dir: Int) = dir == NONE || dir == HORIZONTAL + private def isHorizontal(dir: Int) = (dir & HORIZONTAL) != 0 - private def isVertical(dir: Int) = dir == NONE || dir == VERTICAL + private def isVertical(dir: Int) = (dir & VERTICAL) != 0 } -class DragGesture(context: Context, reactor: TouchReactor, direction: Int = DragGesture.NONE)(implicit eventContext: EventContext) { +class DragGesture(context: Context, reactor: TouchReactor, direction: Int = DragGesture.BOTH)(implicit eventContext: EventContext) { import DragGesture._ @@ -166,6 +198,8 @@ class DragGesture(context: Context, reactor: TouchReactor, direction: Int = Drag private val minimumVelocity = configuration.getScaledMinimumFlingVelocity private val maximumVelocity = configuration.getScaledMaximumFlingVelocity + protected def hit(x: Float, y: Float) = true + reactor.onDown { ev: MotionEvent => dragging = false @@ -174,19 +208,18 @@ class DragGesture(context: Context, reactor: TouchReactor, direction: Int = Drag onDragEnd ! dragged } - if (onDragStart.hasSubscribers || onDrag.hasSubscribers || onFling.hasSubscribers) { + if ((onDragStart.hasSubscribers || onDrag.hasSubscribers || onFling.hasSubscribers) && hit(ev.getX, ev.getY)) { startPos.set(ev.getX, ev.getY) var velocityTracker: VelocityTracker = null var dragObserver: Subscription = null def shouldStartDrag(x: Float, y: Float) = { direction match { - case NONE => startPos.dst(ev.getX, ev.getY) > touchSlop + case BOTH => startPos.dst(ev.getX, ev.getY) > touchSlop case VERTICAL => math.abs(ev.getY - startPos.y) > touchSlop && math.abs((ev.getY - startPos.y) / (ev.getX - startPos.x)) > 2 || math.abs(ev.getY - startPos.y) > 2 * touchSlop case HORIZONTAL => math.abs(ev.getX - startPos.x) > touchSlop && math.abs((ev.getX - startPos.x) / (ev.getY - startPos.y)) > 2 || math.abs(ev.getX - startPos.x) > 2 * touchSlop } } - dragObserver = reactor.onDrag { ev: MotionEvent => if (!dragging && shouldStartDrag(ev.getX, ev.getY)) { onDragStart !(ev.getX, ev.getY) diff --git a/src/main/scala/com/geteit/view/ValueAnimator.scala b/src/main/scala/com/geteit/view/ValueAnimator.scala index c05fa45..7501e7e 100644 --- a/src/main/scala/com/geteit/view/ValueAnimator.scala +++ b/src/main/scala/com/geteit/view/ValueAnimator.scala @@ -3,10 +3,12 @@ package com.geteit.view import android.content.Context import android.os.{Looper, SystemClock} import android.view.View +import android.view.animation.LinearInterpolator import com.geteit.util.{GtObjHandler, MathUtils} import com.nineoldandroids.animation.Animator.AnimatorListener import com.nineoldandroids.animation.ValueAnimator.AnimatorUpdateListener import com.nineoldandroids.animation.{ValueAnimator, AnimatorListenerAdapter, Animator} +import com.geteit.util.Log._ object GtValueAnimator { } @@ -50,14 +52,25 @@ class GtValueAnimator(duration: Long = 300) { this } - def startInt(values: Int*) = { + def startInt(start: Int, end: Int) = { // GtAssert.assertUIThread() - if (values.nonEmpty) anim.setIntValues(values: _*) + anim.setIntValues(start, end) anim.start() this } + def updateInt(start: Int, end: Int) = { + if (anim.isRunning) { + val value = anim.getAnimatedValue.asInstanceOf[Int] + val values = anim.getValues + } else { + anim.setIntValues(start, end) + anim.start() + } + this + } + def end() { // GtAssert.assertUIThread() @@ -72,6 +85,53 @@ class GtValueAnimator(duration: Long = 300) { def isRunning = anim.isRunning } +class ContinuousValueAnimator(var start: Long, onUpdate: Long => Unit) extends AnimatorUpdateListener { + private implicit val tag: LogTag = "ContinuousValueAnimator" + + lazy val anim = { + val a = new ValueAnimator + a.addUpdateListener(this) + a.setInterpolator(new LinearInterpolator()) + a.setFloatValues(0f, 1f) + a + } + + var current: Long = start + var target: Long = 0 + + def cancel() = if (anim.isRunning) anim.cancel() + + def set(value: Long) = { + cancel() + current = value + onUpdate(current) + } + + def animateTo(target: Long) = { + this.target = target + if (anim.isRunning) { + val duration = anim.getDuration + val shouldContinue = (current > start && target >= current) || (current < start && target <= current) + if (shouldContinue) { + val time = ((current - start).toFloat / (target - start) * duration).toLong + anim.setCurrentPlayTime(time) + } else { + anim.cancel() + start = current + anim.start() + } + } else { + start = current + anim.start() + } + } + + override def onAnimationUpdate(animation: ValueAnimator): Unit = { + current = start + ((target - start) * animation.getAnimatedFraction + .5).toLong + onUpdate(current) + } +} + class FadeAnimator(view: View, duration: Long = 300, hiddenVisibility: Int = View.GONE) extends GtValueAnimator(duration) { private val transform = ViewTransformFactory.createTransform(view)