Android Splash实现

1、创建Activity

Kotlin 复制代码
package com.wsy.knowledge.ui.splash


import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.annotation.SuppressLint
import android.os.Build
import android.os.Looper
import android.util.Log
import androidx.annotation.RequiresApi
import com.alibaba.android.arouter.facade.annotation.Route
import com.wsy.knowledge.common.arouter.ArouterUrl
import com.wsy.knowledge.common.ui.component.BaseActivity
import com.wsy.knowledge.databinding.ActivityNativeSplashBinding
import com.wsy.knowledge.ui.homepage.HomePageActivity


@SuppressLint("CustomSplashScreen")
@Route(path = ArouterUrl.native_splash)
class NativeSplashActivity : BaseActivity() {
    private lateinit var binding: ActivityNativeSplashBinding

    override fun getLayoutId() {
        binding = ActivityNativeSplashBinding.inflate(layoutInflater)
        setContentView(binding.root)
    }

    override fun initData() {
    }

    @RequiresApi(Build.VERSION_CODES.O)
    override fun init() {
        setFAFStatusBar()
        binding.animLogo.apply {
            addOffsetAnimListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    Log.d("AnimLogoView", "Offset anim end")
                }
            })
            addGradientAnimListener(object : AnimatorListenerAdapter() {
                override fun onAnimationEnd(animation: Animator) {
                    Log.d("AnimLogoView", "Gradient anim end")
                    startActivity(HomePageActivity::class.java, true)
                }
            })
        }
        Looper.myQueue().addIdleHandler {
            binding.animLogo.startAnimation()
            false
        }
    }



}

2、XML文件

Kotlin 复制代码
<?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=".ui.splash.NativeSplashActivity"
    android:background="@color/blue_2A6FAF"
    tools:ignore="MissingDefaultResource">
    <com.wsy.knowledge.common.ui.view.AnimationLogoView
        android:id="@+id/anim_logo"
        android:layout_width="match_parent"
        android:layout_height="@dimen/sp_200"
        app:layout_constraintTop_toBottomOf="@+id/splashImg"
        app:autoPlay="false"
        app:gradientAnimDuration="2000"
        app:gradientColor="@color/blue_2A6FAF"
        app:logoName="@string/app_name"
        app:offsetAnimDuration="2000"
        app:showGradient="true"
        app:textColor="@color/white"
        app:textPadding="3dp"
        app:textSize="@dimen/sp_40"
        />
    <ImageView
        android:id="@+id/splashImg"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        android:layout_marginBottom="@dimen/sp_200"
        android:src="@drawable/launch_second"
        android:layout_marginTop="@dimen/sp_200"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        android:layout_marginBottom="@dimen/sp_20"
        android:text="@string/search_service"
        android:textSize="@dimen/sp_14"
        android:textColor="@color/color_999"
        android:gravity="center"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

3、AnimationLogoView

Kotlin 复制代码
package com.wsy.knowledge.common.ui.view

import android.animation.Animator
import android.animation.AnimatorListenerAdapter
import android.animation.PointFEvaluator
import android.animation.ValueAnimator
import android.animation.ValueAnimator.AnimatorUpdateListener
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.text.TextUtils
import android.util.AttributeSet
import android.util.Log
import android.util.SparseArray
import android.view.View
import android.view.animation.AccelerateDecelerateInterpolator
import com.wsy.knowledge.R

class AnimationLogoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : View(context, attrs, defStyleAttr) {

    private val mLogoTexts = SparseArray<String>()
    private val mQuietPoints = SparseArray<PointF>()
    private val mRandomPoints = SparseArray<PointF>()
    private var mOffsetAnimator: ValueAnimator? = null
    private var mGradientAnimator: ValueAnimator? = null
    private var mPaint: Paint? = null
    private var mTextPadding: Int
    private var mTextColor: Int
    private var mTextSize: Float
    private var mOffsetAnimProgress = 0f
    private var mOffsetDuration: Int
    private var isOffsetAnimEnd = false
    private var mGradientDuration: Int
    private var mLinearGradient: LinearGradient? = null
    private var mGradientColor: Int
    private var mGradientMatrix: Matrix? = null
    private var mMatrixTranslate = 0
    private val isAutoPlay: Boolean
    private var mWidth = 0
    private var mHeight = 0
    private var isShowGradient: Boolean
    private val mLogoOffset: Int
    private var mGradientListener: Animator.AnimatorListener? = null

    companion object {
        private const val DEFAULT_LOGO = "Animation"
        private const val DEFAULT_TEXT_PADDING = 10
        private const val ANIM_LOGO_DURATION = 1500
        private const val ANIM_LOGO_GRADIENT_DURATION = 1500
        private const val ANIM_LOGO_TEXT_SIZE = 30f
        private const val ANIM_LOGO_TEXT_COLOR = Color.BLACK
        private const val ANIM_LOGO_GRADIENT_COLOR = Color.YELLOW
    }

    init {
        val ta = context.obtainStyledAttributes(attrs, R.styleable.AnimationLogoView)
        var logoName = ta.getString(R.styleable.AnimationLogoView_logoName)
        isAutoPlay = ta.getBoolean(R.styleable.AnimationLogoView_autoPlay, true)
        isShowGradient = ta.getBoolean(R.styleable.AnimationLogoView_showGradient, false)
        mOffsetDuration = ta.getInt(R.styleable.AnimationLogoView_offsetAnimDuration, ANIM_LOGO_DURATION)
        mGradientDuration = ta.getInt(R.styleable.AnimationLogoView_gradientAnimDuration, ANIM_LOGO_GRADIENT_DURATION)
        mTextColor = ta.getColor(R.styleable.AnimationLogoView_textColor, ANIM_LOGO_TEXT_COLOR)
        mGradientColor = ta.getColor(R.styleable.AnimationLogoView_gradientColor, ANIM_LOGO_GRADIENT_COLOR)
        mTextPadding = ta.getDimensionPixelSize(R.styleable.AnimationLogoView_textPadding, DEFAULT_TEXT_PADDING)
        mTextSize = ta.getDimension(R.styleable.AnimationLogoView_textSize, ANIM_LOGO_TEXT_SIZE)
        mLogoOffset = ta.getDimensionPixelOffset(R.styleable.AnimationLogoView_verticalOffset, 0)
        ta.recycle()
        if (TextUtils.isEmpty(logoName)) {
            logoName = DEFAULT_LOGO
        }
        fillLogoTextArray(logoName)
        initPaint()
        initOffsetAnimation()
    }

    private fun fillLogoTextArray(logoName: String?) {
        if (TextUtils.isEmpty(logoName)) {
            return
        }
        if (mLogoTexts.size() > 0) {
            mLogoTexts.clear()
        }
        for (i in logoName!!.indices) {
            mLogoTexts.put(i, logoName[i].toString())
        }
    }

    private fun initPaint() {
        mPaint=Paint().apply {
            isAntiAlias = true
            style = Paint.Style.FILL
            textSize = mTextSize
            color = mTextColor
        }
    }

    private fun initOffsetAnimation() {
        if (mOffsetAnimator == null) {
            mOffsetAnimator = ValueAnimator.ofFloat(0f, 1f).apply {
                interpolator = AccelerateDecelerateInterpolator()
                addUpdateListener(AnimatorUpdateListener { animation ->
                    if (mQuietPoints.size() <= 0 || mRandomPoints.size() <= 0) {
                        return@AnimatorUpdateListener
                    }
                    mOffsetAnimProgress = animation.animatedValue as Float
                    invalidate()
                })
                addListener(object : AnimatorListenerAdapter() {
                    override fun onAnimationEnd(animation: Animator) {
                        if (mGradientAnimator != null && isShowGradient) {
                            isOffsetAnimEnd = true
                            mPaint?.shader = mLinearGradient
                            mGradientAnimator!!.start()
                        }
                    }
                })
                duration = mOffsetDuration.toLong()
            }
        }
    }

    override fun onAttachedToWindow() {
        super.onAttachedToWindow()
        if (visibility == VISIBLE && isAutoPlay) {
            mOffsetAnimator?.start()
        }
    }

    override fun onDetachedFromWindow() {
        if (mOffsetAnimator != null && mOffsetAnimator!!.isRunning) {
            mOffsetAnimator!!.cancel()
        }
        if (mGradientAnimator != null && mGradientAnimator!!.isRunning) {
            mGradientAnimator!!.cancel()
        }
        super.onDetachedFromWindow()
    }


    fun addOffsetAnimListener(listener: Animator.AnimatorListener?) {
        mOffsetAnimator!!.addListener(listener)
    }

    fun addGradientAnimListener(listener: Animator.AnimatorListener?) {
        mGradientListener = listener
    }

    /**
     * 开启动画
     */
    fun startAnimation() {
        if (visibility == VISIBLE) {
            if (mOffsetAnimator!!.isRunning) {
                mOffsetAnimator!!.cancel()
            }
            isOffsetAnimEnd = false
            mOffsetAnimator!!.start()
        } else {
            Log.w("AnimLogoView", "The view is not visible, not to play the animation .")
        }
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        mWidth = w
        mHeight = h
        initLogoCoordinate()
        initGradientAnimation()
    }

    private fun initLogoCoordinate() {
        if (mWidth == 0 || mHeight == 0) {
            return
        }
        val fontMetrics = mPaint!!.fontMetrics
        val baseY = mHeight / 2 - fontMetrics.top / 2 - fontMetrics.bottom / 2
        val centerY = baseY + mLogoOffset
        var totalLength = 0f
        for (i in 0 until mLogoTexts.size()) {
            val str = mLogoTexts[i]
            val currentLength = mPaint!!.measureText(str)
            totalLength += if (i != mLogoTexts.size() - 1) {
                currentLength + mTextPadding
            } else {
                currentLength
            }
        }
        check(totalLength <= mWidth) { "The text of logoName is too large that this view can not display all text" }
        var startX = (mWidth - totalLength) / 2
        if (mQuietPoints.size() > 0) {
            mQuietPoints.clear()
        }
        for (i in 0 until mLogoTexts.size()) {
            val str = mLogoTexts[i]
            val currentLength = mPaint!!.measureText(str)
            mQuietPoints.put(i, PointF(startX, centerY))
            startX += currentLength + mTextPadding
        }
        if (mRandomPoints.size() > 0) {
            mRandomPoints.clear()
        }
        for (i in 0 until mLogoTexts.size()) {
            mRandomPoints.put(i, PointF(Math.random().toFloat() * mWidth, Math.random().toFloat() * mHeight))
        }
    }

    private fun initGradientAnimation() {
        if (mWidth == 0 || mHeight == 0) {
            return
        }
        if (mGradientAnimator == null) {
            mGradientAnimator = ValueAnimator.ofInt(0, 2 * mWidth).apply {
                if (mGradientListener != null) {
                   addListener(mGradientListener)
                }
                addUpdateListener { animation ->
                    mMatrixTranslate = animation.animatedValue as Int
                    invalidate()
                }

                mLinearGradient = LinearGradient(-mWidth.toFloat(), 0f, 0f, 0f, intArrayOf(mTextColor, mGradientColor, mTextColor), floatArrayOf(0f, 0.5f, 1f), Shader.TileMode.CLAMP)
                mGradientMatrix = Matrix()
                duration = mGradientDuration.toLong()
            }

        }
    }

    @SuppressLint("DrawAllocation")
    override fun onDraw(canvas: Canvas) {
        if (!isOffsetAnimEnd) {
            mPaint!!.alpha = 255f.coerceAtMost(255 * mOffsetAnimProgress + 100).toInt()
            for (i in 0 until mQuietPoints.size()) {
                val point=PointFEvaluator().evaluate(mOffsetAnimProgress,mRandomPoints[i],mQuietPoints[i])
                canvas.drawText(mLogoTexts[i],point.x, point.y, mPaint!!)
            }
        } else {
            for (i in 0 until mQuietPoints.size()) {
                val quietP = mQuietPoints[i]
                canvas.drawText(mLogoTexts[i], quietP.x, quietP.y, mPaint!!)
            }
            mGradientMatrix!!.setTranslate(mMatrixTranslate.toFloat(), 0f)
            mLinearGradient!!.setLocalMatrix(mGradientMatrix)
        }
    }


}
相关推荐
Estar.Lee3 分钟前
时间操作[取当前北京时间]免费API接口教程
android·网络·后端·网络协议·tcp/ip
Winston Wood15 分钟前
Perfetto学习大全
android·性能优化·perfetto
Dnelic-3 小时前
【单元测试】【Android】JUnit 4 和 JUnit 5 的差异记录
android·junit·单元测试·android studio·自学笔记
Eastsea.Chen5 小时前
MTK Android12 user版本MtkLogger
android·framework
长亭外的少年13 小时前
Kotlin 编译失败问题及解决方案:从守护进程到 Gradle 配置
android·开发语言·kotlin
建群新人小猿15 小时前
会员等级经验问题
android·开发语言·前端·javascript·php
1024小神16 小时前
tauri2.0版本开发苹果ios和安卓android应用,环境搭建和最后编译为apk
android·ios·tauri
兰琛16 小时前
20241121 android中树结构列表(使用recyclerView实现)
android·gitee
Y多了个想法17 小时前
RK3568 android11 适配敦泰触摸屏 FocalTech-ft5526
android·rk3568·触摸屏·tp·敦泰·focaltech·ft5526
NotesChapter18 小时前
Android吸顶效果,并有着ViewPager左右切换
android