Android Splash实现
1、创建Activity
package com.wsy.knowledge.ui.splashimport 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: ActivityNativeSplashBindingoverride 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文件
<?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.AnimationLogoViewandroid: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"/><ImageViewandroid: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"/><TextViewapp: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
package com.wsy.knowledge.common.ui.viewimport 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.Rclass 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? = nullprivate var mGradientAnimator: ValueAnimator? = nullprivate var mPaint: Paint? = nullprivate var mTextPadding: Intprivate var mTextColor: Intprivate var mTextSize: Floatprivate var mOffsetAnimProgress = 0fprivate var mOffsetDuration: Intprivate var isOffsetAnimEnd = falseprivate var mGradientDuration: Intprivate var mLinearGradient: LinearGradient? = nullprivate var mGradientColor: Intprivate var mGradientMatrix: Matrix? = nullprivate var mMatrixTranslate = 0private val isAutoPlay: Booleanprivate var mWidth = 0private var mHeight = 0private var isShowGradient: Booleanprivate val mLogoOffset: Intprivate var mGradientListener: Animator.AnimatorListener? = nullcompanion object {private const val DEFAULT_LOGO = "Animation"private const val DEFAULT_TEXT_PADDING = 10private const val ANIM_LOGO_DURATION = 1500private const val ANIM_LOGO_GRADIENT_DURATION = 1500private const val ANIM_LOGO_TEXT_SIZE = 30fprivate const val ANIM_LOGO_TEXT_COLOR = Color.BLACKprivate 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 = truestyle = Paint.Style.FILLtextSize = mTextSizecolor = 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 Floatinvalidate()})addListener(object : AnimatorListenerAdapter() {override fun onAnimationEnd(animation: Animator) {if (mGradientAnimator != null && isShowGradient) {isOffsetAnimEnd = truemPaint?.shader = mLinearGradientmGradientAnimator!!.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 = falsemOffsetAnimator!!.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 = wmHeight = hinitLogoCoordinate()initGradientAnimation()}private fun initLogoCoordinate() {if (mWidth == 0 || mHeight == 0) {return}val fontMetrics = mPaint!!.fontMetricsval baseY = mHeight / 2 - fontMetrics.top / 2 - fontMetrics.bottom / 2val centerY = baseY + mLogoOffsetvar totalLength = 0ffor (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) / 2if (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 Intinvalidate()}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)}}}