Last active
July 13, 2020 13:42
-
-
Save webianks/b599be214fbbe143b2929bf5d80d5e87 to your computer and use it in GitHub Desktop.
Achieving scale down/up and the onClick callback on CTA views with Compound Views, Gesture Detector, and ObjectAnimator in Android.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<com.webianks.gameroon.custom.ScalingView | |
android:id="@+id/gameCard" | |
android:layout_width="wrap_content" | |
android:layout_height="wrap_content"> | |
<FrameLayout .... | |
</FrameLayout> | |
</com.webianks.gameroon.custom.ScalingView> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import android.animation.AnimatorSet | |
import android.animation.ObjectAnimator | |
import android.annotation.SuppressLint | |
import android.content.Context | |
import android.util.AttributeSet | |
import android.view.GestureDetector | |
import android.view.MotionEvent | |
import android.view.View | |
import android.widget.FrameLayout | |
import androidx.core.view.GestureDetectorCompat | |
import timber.log.Timber | |
class ScalingView : FrameLayout, GestureDetector.OnGestureListener, | |
GestureDetector.OnDoubleTapListener { | |
private var onClickListener: OnClickListener? = null | |
private lateinit var gestureDetectorCompat: GestureDetectorCompat | |
private val MAX_CLICK_DISTANCE = 2 | |
private var x1 = 0f | |
private var y1 = 0f | |
private var x2 = 0f | |
private var y2 = 0f | |
private var dx = 0f | |
private var dy = 0f | |
private var longPressed = false | |
constructor(context: Context) : super(context) { | |
init() | |
} | |
constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) { | |
init() | |
} | |
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { | |
init() | |
} | |
@SuppressLint("ClickableViewAccessibility") | |
fun init() { | |
gestureDetectorCompat = GestureDetectorCompat(context, this) | |
gestureDetectorCompat.setOnDoubleTapListener(this) | |
setOnTouchListener { view: View, event: MotionEvent -> | |
when (event.actionMasked) { | |
MotionEvent.ACTION_DOWN -> { | |
x1 = event.x | |
y1 = event.y | |
} | |
MotionEvent.ACTION_MOVE -> checkIfThresholdMoved(event) | |
MotionEvent.ACTION_CANCEL -> handleCancel() | |
MotionEvent.ACTION_UP -> { | |
if (longPressed) | |
onLongPressConfirmed() | |
} | |
} | |
gestureDetectorCompat.onTouchEvent(event) | |
true | |
} | |
} | |
override fun setOnClickListener(l: OnClickListener?) { | |
onClickListener = l | |
} | |
private fun onLongPressConfirmed() { | |
scaleOriginal() | |
longPressed = false | |
onClickListener?.onClick(this) | |
} | |
private fun handleCancel() { | |
longPressed = false | |
scaleOriginal() | |
} | |
private fun checkIfThresholdMoved(event: MotionEvent) { | |
x2 = event.x | |
y2 = event.y | |
dx = x2 - x1 | |
dy = y2 - y1 | |
if (dx > MAX_CLICK_DISTANCE || dy > MAX_CLICK_DISTANCE) | |
scaleOriginal() | |
} | |
override fun onDoubleTap(p0: MotionEvent?): Boolean { | |
return true | |
} | |
override fun onDoubleTapEvent(p0: MotionEvent?): Boolean { | |
return true | |
} | |
override fun onSingleTapConfirmed(p0: MotionEvent?): Boolean { | |
scaleOriginal() | |
return true | |
} | |
override fun onShowPress(p0: MotionEvent?) { | |
scaleDown() | |
} | |
override fun onDown(p0: MotionEvent?): Boolean { | |
return true | |
} | |
override fun onFling(p0: MotionEvent?, p1: MotionEvent?, p2: Float, p3: Float): Boolean { | |
return true | |
} | |
override fun onScroll(p0: MotionEvent?, p1: MotionEvent?, p2: Float, p3: Float): Boolean { | |
return true | |
} | |
override fun onLongPress(p0: MotionEvent?) { | |
longPressed = true | |
} | |
override fun onSingleTapUp(p0: MotionEvent?): Boolean { | |
scaleDown() | |
onClickListener?.onClick(this) | |
return true | |
} | |
private fun scaleOriginal() { | |
val scaleUpX = ObjectAnimator.ofFloat( | |
this, "scaleX", 1f) | |
val scaleUpY = ObjectAnimator.ofFloat( | |
this, "scaleY", 1f) | |
scaleUpX.duration = 200 | |
scaleUpY.duration = 200 | |
val scaleUp = AnimatorSet() | |
scaleUp.play(scaleUpX).with(scaleUpY) | |
scaleUp.start() | |
} | |
private fun scaleDown() { | |
val scaleDownX = ObjectAnimator.ofFloat(this, | |
"scaleX", 0.9f) | |
val scaleDownY = ObjectAnimator.ofFloat(this, | |
"scaleY", 0.9f) | |
scaleDownX.duration = 200 | |
scaleDownY.duration = 200 | |
val scaleDown = AnimatorSet() | |
scaleDown.play(scaleDownX).with(scaleDownY) | |
scaleDown.start() | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment