当前位置: 首页 > news >正文

自定义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()}

http://www.lryc.cn/news/603963.html

相关文章:

  • 高端医疗超声AFE模拟前端应用
  • 网络安全运维面试准备
  • 背包进一步(多重背包、混合背包)
  • jvm冷门知识十讲
  • Arduino声控RGB矩阵音乐节奏灯DIY全攻略
  • Nuxt3 全栈作品【通用信息管理系统】菜单管理
  • 比肩 7B 表现!Ovis-U1-3B 集多模态理解、图像生成与编辑于一体
  • 《嵌入式C语言笔记(十五):字符串操作与多维指针深度解析》
  • Go进阶:流程控制(if/for/switch)与数组切片
  • ORACLE的用户维护与权限操作
  • 火山方舟使用豆包基模 —— 基础流程
  • 什么是ios企业签名?
  • ROUGE-WE:词向量化革新的文本生成评估框架
  • H.264视频的RTP有效载荷格式(翻译自:RFC6184 第5节 RTP有效载荷格式)
  • 自然语言处理NLP(3)
  • 烟草复杂包装识别准确率↑31%!陌讯多模态SKU检测算法在零售终端的实战解析
  • CMake 完全实战指南:从入门到精通
  • MySQL的JDBC编程
  • Seq2Seq学习笔记
  • 【绘制图像轮廓】——图像预处理(OpenCV)
  • idea运行tomcat日志乱码问题
  • CentOS安装ffmpeg并转码视频为mp4
  • 编程算法在金融、医疗、教育、制造业等领域的落地案例
  • 单片机(STM32-WIFI模块)
  • windows电脑如何截屏 windows电脑截屏教程汇总
  • 【机器学习深度学习】DeepSpeed框架:高效分布式训练的开源利器
  • Python Flask: Windows 2022 server SMB账户(共享盘账户)密码修改
  • 影刀RPA_初级课程_玩转影刀自动化_EXCEL操作自动化
  • 数据结构(5)单链表算法题(中)
  • 第二十二天(数据结构,无头节点的单项链表)