1.效果图
这是原版效果:
下面是仿制的效果
2.实现分析
开屏动画里面有一个向上滑动的箭头,引导用户去广告页,主要是一个平移动画和透明度变化,基本元素是一个箭头,不断从底下弹出,到达某个位置后消失。所以我们要实现的是一个自定义的ViewGroup,这里我们选择RelativeLayout
代码实现
package com.ttyh.customview
import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.AnimatorSet
import android.animation.ObjectAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.drawable.Drawable
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.GestureDetector
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.LinearInterpolator
import android.widget.ImageView
import android.widget.RelativeLayout
import androidx.core.content.res.ResourcesCompat
import com.ttyh.notificationtest.R
import java.util.*
/**
* @author Create by QinBiao
* @description:
* @create_date :2022/9/2-11:20
* @Modified_time : 2022/9/2-11:20
*/
class ArrowLayout : RelativeLayout,GestureDetector.OnGestureListener{
private var params :ViewGroup.LayoutParams ?= null
private lateinit var gestureDetector:GestureDetector
//处理滑动事件的接口
private var flingListener:(()->Unit) ?= null
private var isInItFlag = false
private var icon = ResourcesCompat.getDrawable(resources, R.drawable.ic_arrow_up, null) //默认箭头图片
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
background = ResourcesCompat.getDrawable(resources,R.drawable.bg_background,null) //默认背景图片
initListener()
}
/**
* 初始化手势监听器
* */
private fun initListener() {
gestureDetector = GestureDetector(context,this)
}
private fun initView() {
val width = (icon?.intrinsicWidth?.div(4)) ?: 0
val height = icon?.intrinsicHeight?.div(4) ?:0
//将箭头imageView添加到布局的底部中央位置
params = LayoutParams(width, height).apply {
addRule(CENTER_HORIZONTAL, TRUE)
addRule(ALIGN_PARENT_BOTTOM, TRUE)
}
isInItFlag = true
}
//设置背景图片
fun setBackGround(mBackground:Drawable){
background = mBackground
}
//设置箭头
fun setIcon(drawable: Drawable){
icon = drawable
}
private fun addArrowView(){
//如果没有初始化过箭头图片的位置,初始化
if(!isInItFlag){
initView()
}
val iv = ImageView(context).apply {
layoutParams = params
setImageDrawable(icon)
}
//添加箭头图片的Imageview 到布局中
addView(iv)
//开启动画
val alphaOne = ObjectAnimator.ofFloat(iv,"alpha",0.3f,1f,0.7f)
alphaOne.interpolator = LinearInterpolator()
val translateOne = ObjectAnimator.ofFloat(iv,"translationY",0f,-200f)
translateOne.interpolator = LinearInterpolator()
val animatorSet = AnimatorSet().apply {
play(translateOne).with(alphaOne)
duration = 1500
}
animatorSet.start()
//动画监听
animatorSet.addListener(object : AnimatorListenerAdapter() {
override fun onAnimationEnd(animation: Animator?) {
super.onAnimationEnd(animation)
iv.visibility = View.GONE
removeView(iv) //移除箭头
}
override fun onAnimationRepeat(animation: Animator?) {
super.onAnimationRepeat(animation)
}
override fun onAnimationStart(animation: Animator?) {
super.onAnimationStart(animation)
}
})
}
/*@params:duration 多长时间间隔发射一个箭头*/
fun setArrowDuration(duration:Long){
val looper = Looper.getMainLooper()
val handler = Handler(looper)
val timer = Timer()
val task = object : TimerTask(){
override fun run() {
handler.postDelayed({
addArrowView()
},0)
}
}
timer.schedule(task,0,duration)
}
override fun onInterceptTouchEvent(ev: MotionEvent?): Boolean {
return true
}
override fun onDown(e: MotionEvent?): Boolean {
return true
}
override fun onShowPress(e: MotionEvent?) {
}
override fun onSingleTapUp(e: MotionEvent?): Boolean {
return false
}
override fun onScroll(
e1: MotionEvent?,
e2: MotionEvent?,
distanceX: Float,
distanceY: Float
): Boolean {
return false
}
override fun onLongPress(e: MotionEvent?) {
}
override fun onFling(
e1: MotionEvent?,
e2: MotionEvent?,
velocityX: Float,
velocityY: Float
): Boolean {
flingListener?.invoke()
return false
}
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
return gestureDetector.onTouchEvent(event)
}
}
4.使用
<?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="match_parent"
tools:context="com.ttyh.animator.AdActivity">
<com.ttyh.customview.ArrowLayout
android:id="@+id/ad"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
在Activity或者Fragment中开启动画
class AdActivity : AppCompatActivity() {
private lateinit var binding: ActivityAdBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityAdBinding.inflate(layoutInflater)
//设置弹射箭头的时间间隔,并且开始动画
binding.ad.setArrowDuration(500)
setContentView(binding.root)
}
}
实现拦截手势的接口
binding.ad.flingListener = {
//拦截滑动手势后,要做的事,比如说启动淘宝、京东
}
5 期间遇到的问题
自定义view时,无法拦截到手势的滑动手势,只能拦截到短按、长按等手势,
解决方法:
在实现手势拦截方法时,重写onDown(e:MotionEvent?):Boolean方法,表示消费掉该手势,然后就能拦截到滑动手势了