自定义View和动画学习记录 抓娃娃机View
抓娃娃
目录
1. 首先准备几张钩子的相关图片
2.自定义View 的布局文件 my_view_catch.xml
3.自定义View的代码
4.具体使用
1. 首先准备几张钩子的相关图片
从前往后分别命名为:ic_tongs_top , ic_tongs_center , ic_tongs_open , ic_tongs_close ,ic_catch_coin
2.自定义View 的布局文件 my_view_catch.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="500dp"tools:background="@color/white"><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/bubbleView0"android:layout_width="50dp"android:layout_height="50dp"android:tag="0"android:layout_marginBottom="20dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/bubbleView1"app:layout_constraintStart_toStartOf="parent"app:srcCompat="@drawable/ic_catch_coin"/><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/bubbleView1"android:layout_width="50dp"android:layout_height="50dp"android:tag="1"android:layout_marginBottom="20dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toStartOf="@+id/bubbleView2"app:layout_constraintStart_toEndOf="@+id/bubbleView0"app:srcCompat="@drawable/ic_catch_coin"/><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/bubbleView2"android:layout_width="50dp"android:layout_height="50dp"android:tag="2"android:layout_marginBottom="20dp"app:layout_constraintBottom_toBottomOf="parent"app:layout_constraintEnd_toEndOf="parent"app:layout_constraintStart_toEndOf="@+id/bubbleView1"app:srcCompat="@drawable/ic_catch_coin"/><androidx.appcompat.widget.LinearLayoutCompatandroid:id="@+id/tongsView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:orientation="vertical"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent"><androidx.appcompat.widget.AppCompatImageViewandroid:layout_width="92dp"android:layout_height="9dp"android:layout_gravity="center_horizontal"android:adjustViewBounds="true"app:srcCompat="@drawable/ic_tongs_top" /><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/tongsCenterView"android:layout_width="8dp"android:layout_height="3dp"android:layout_gravity="center_horizontal"android:adjustViewBounds="true"android:scaleType="fitXY"app:srcCompat="@drawable/ic_tongs_center" /><androidx.appcompat.widget.AppCompatImageViewandroid:id="@+id/tongsBottomView"android:layout_width="92dp"android:layout_height="49dp"android:layout_gravity="center_horizontal"android:adjustViewBounds="true"android:scaleType="fitStart"app:srcCompat="@drawable/ic_tongs_close" /></androidx.appcompat.widget.LinearLayoutCompat></androidx.constraintlayout.widget.ConstraintLayout>
3.自定义View的代码
package com.example.test.ui.widgetimport android.animation.AnimatorSet
import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Rect
import android.util.AttributeSet
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import android.view.animation.Animation
import android.widget.FrameLayout
import android.widget.Toast
import androidx.appcompat.widget.AppCompatImageView
import androidx.core.animation.doOnEnd
import androidx.core.animation.doOnStart
import androidx.core.view.doOnNextLayout
import androidx.core.view.updateLayoutParamsclass MyCatchView @JvmOverloads constructor(context: Context,attrs: AttributeSet? = null,defStyleAttr: Int = 0
) : FrameLayout(context, attrs, defStyleAttr) {private val binding = MyViewCatchBinding.inflate(LayoutInflater.from(context), this, true)private var horizontalAnimator: ValueAnimator? = null //钩爪左右移动的动画private var horizontalMoveAnimator: ValueAnimator? = null //钩爪开始抓取时移动到指定位置的动画private var moveDownAnimator: ValueAnimator? = null //钩爪抓取时向下移动的动画private var moveUpAnimator: ValueAnimator? = null //钩爪抓取时向上返回的动画private var captureAnimatorSet: AnimatorSet? = null //抓取动画集合private var bubbleViewList: MutableList<AppCompatImageView> = mutableListOf()private lateinit var targetView: AppCompatImageView //要抓取的目标视图private var viewWidth = 0fprivate var viewHeight = 0fprivate var onCapture = falseinit {initBubbleView()}private fun startHorizontalAnimator() {binding.tongsView.post {//在tongsView绘制完成之后才能获取宽高if (horizontalAnimator == null) {horizontalAnimator =ValueAnimator.ofFloat(0f, viewWidth - binding.tongsView.width).apply {duration = 3000repeatCount = ValueAnimator.INFINITErepeatMode = ValueAnimator.REVERSEaddUpdateListener {binding.tongsView.translationX = it.animatedValue as Float}}}LogUtil.d("CatchView", "tongsX: ${viewWidth - binding.tongsView.width}")horizontalAnimator?.cancel()horizontalAnimator?.start()}}//开始抓取fun startCapture() {if (bubbleViewList.isEmpty()) {initBubbleView()Toast.makeText(context, "奖池重置!", Toast.LENGTH_SHORT).show()return}if (onCapture) returnonCapture = trueToast.makeText(context, "$onCapture", Toast.LENGTH_SHORT).show()horizontalAnimator?.cancel()captureAnimatorSet?.cancel()targetView = bubbleViewList.random()val startY = binding.tongsBottomView.yval startX = binding.tongsView.xval targetX = targetView.x + (targetView.width - binding.tongsView.width) / 2val targetY = targetView.y + targetView.height / 2f - binding.tongsBottomView.heightcaptureAnimatorSet = AnimatorSet().apply {// 设置动画序列playSequentially(getHorizontalMoveAnimator(startX, targetX),getMoveDownAnimator(startY, targetY),getMoveUpAnimator(targetY, startY),getHorizontalMoveAnimator(binding.tongsView.x, 0f, 1000))// 动画结束回调doOnEnd {onCapture = falsestartHorizontalAnimator()Toast.makeText(context, "恭喜获得tag:${targetView.tag}", Toast.LENGTH_SHORT).show()}}captureAnimatorSet?.start()}//将钩爪移动到指定水平位置private fun getHorizontalMoveAnimator(startX: Float,targetX: Float,delay: Long = 0,endCallBack: () -> Unit = {}): ValueAnimator {horizontalMoveAnimator = ValueAnimator.ofFloat(startX, targetX).apply {duration = 1000startDelay = delayinterpolator = AccelerateDecelerateInterpolator()addUpdateListener {binding.tongsView.translationX = it.animatedValue as Float}doOnEnd { endCallBack.invoke() }}return horizontalMoveAnimator!!}//钩爪向下移动private fun getMoveDownAnimator(startY: Float, endY: Float): ValueAnimator {val startHeight = binding.tongsCenterView.heightval openTongsY = startY + (endY - startY) / 4 * 3var setOpenSrc = falsemoveDownAnimator = ValueAnimator.ofFloat(startY, endY).apply {duration = 1000interpolator = AccelerateDecelerateInterpolator()addUpdateListener {//并不是钩爪向下移动,而是通过增加杆View的高度来把钩爪挤下去val animatedValue = it.animatedValue as Floatbinding.tongsCenterView.apply {val params = layoutParamsparams.height = startHeight + (animatedValue - startY).toInt()layoutParams = params}if (!setOpenSrc && animatedValue >= openTongsY) {binding.tongsBottomView.setImageResource(R.drawable.ic_tongs_open)!setOpenSrc}}}return moveDownAnimator!!}//钩爪向上移动private fun getMoveUpAnimator(startY: Float, endY: Float): ValueAnimator {var startHeight = 0val targetViewStartY = targetView.ymoveUpAnimator = ValueAnimator.ofFloat(startY, endY).apply {duration = 1000startDelay = 150interpolator = AccelerateDecelerateInterpolator()doOnStart {//必须在动画开始的时候再获取startHeight = binding.tongsCenterView.height}addUpdateListener {val animatedValue = animatedValue as Floatval s = (animatedValue - startY).toInt() //滑动距离binding.tongsCenterView.apply {val params = layoutParamsparams.height = startHeight + slayoutParams = params}targetView.y = targetViewStartY + s}doOnEnd {binding.tongsBottomView.setImageResource(R.drawable.ic_tongs_close)targetView.visibility = View.INVISIBLEbubbleViewList.remove(targetView)}}return moveUpAnimator!!}private fun initBubbleView() {bubbleViewList.clear()bubbleViewList.apply {add(binding.bubbleView0)add(binding.bubbleView1)add(binding.bubbleView2)}bubbleViewList.forEach {it.visibility = View.VISIBLEit.y = viewHeight - it.height - dp2px(20f)}}override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {super.onSizeChanged(w, h, oldw, oldh)viewWidth = w.toFloat()viewHeight = h.toFloat()startHorizontalAnimator()}override fun onDetachedFromWindow() {horizontalAnimator?.cancel()horizontalAnimator = nullhorizontalMoveAnimator?.cancel()horizontalMoveAnimator = nullmoveDownAnimator?.cancel()moveDownAnimator = nullmoveUpAnimator?.cancel()moveUpAnimator = nullcaptureAnimatorSet?.cancel()captureAnimatorSet = nullsuper.onDetachedFromWindow()}}
4.具体使用
<com.example.test.ui.widget.MyCatchViewandroid:id="@+id/catchView"android:layout_width="match_parent"android:layout_height="500dp"/>
binding.catchView.setOnClickListener {binding.catchView.startCapture()}