美文网首页
下载 加载 loading。。。。。

下载 加载 loading。。。。。

作者: 菜鸟何时起飞 | 来源:发表于2020-05-26 14:42 被阅读0次
loadingButton.gif
enum class AnimationType{
    SUCCESSFUL,
    FAILED
}

@Suppress("unused")
class LoadingButton @JvmOverloads constructor(
        context: Context,
        attrs: AttributeSet? = null,
        defStyle: Int = 0) : View(context, attrs, defStyle){

    companion object {
        private const val STATE_BUTTON = 0
        private const val STATE_ANIMATION_STEP1 = 1
        private const val STATE_ANIMATION_STEP2 = 2
        private const val STATE_ANIMATION_LOADING = 3
        private const val STATE_STOP_LOADING = 4
        private const val STATE_ANIMATION_SUCCESS = 5
        private const val STATE_ANIMATION_FAILED = 6

        private const val DEFAULT_WIDTH = 88
        private const val DEFAULT_HEIGHT = 56

        private const val DEFAULT_COLOR = Color.BLUE
        private const val DEFAULT_TEXT_COLOR = Color.WHITE

    }

    private val mDensity = resources.displayMetrics.density

    private val defaultMinHeight = 48 * mDensity

    var animationEndAction: ((AnimationType) -> Unit)? = null

    var rippleEnable = true
        set(value) {
            invalidate()
            field = value
        }

    var rippleColor = Color.BLACK
        set(value){
            mRipplePaint.color = value
            field = value
        }

    var textColor
        get() = mTextColor
        set(value) {
            mTextColor = value
            invalidate()
        }

    var typeface: Typeface
        get() = mTextPaint.typeface
        set(value) {
            mTextPaint.typeface = value
            invalidate()
        }

    var text
        get() = mText
        set(value) {
            if (text.isEmpty()) {
                return
            }
            this.mText = value
            mTextWidth = mTextPaint.measureText(mText)
            mTextHeight = measureTextHeight(mTextPaint)
            invalidate()
        }

    /**
     * set button text, dip
     */
    var textSize
        get() = (mTextPaint.textSize / mDensity).toInt()
        set(value) {
            mTextPaint.textSize = value * mDensity
            mTextWidth = mTextPaint.measureText(mText)
            invalidate()
       }

    var cornerRadius
        get() = mButtonCorner
        set(value) {
            mButtonCorner = value
            invalidate()
        }

    /** while loading data failed, reset view to normal state */
    var resetAfterFailed = true

    /**
     * set button background as shader paint
     */
    var backgroundShader: Shader?
        get() = mStrokePaint.shader
        set(value) {
            mPaint.shader = value
            mStrokePaint.shader = value
            mPathEffectPaint.shader = value
            mPathEffectPaint2.shader = value
            invalidate()
        }


    private var mCurrentState = STATE_BUTTON
    private var mMinHeight = defaultMinHeight

    private var mColorPrimary = DEFAULT_COLOR
    private var mDisabledBgColor = Color.LTGRAY
    private var mTextColor = Color.WHITE
    private var mDisabledTextColor = Color.DKGRAY
    private var mRippleAlpha = 0.2f

    private var mPadding = 6 * mDensity

    private val mPaint = Paint()
    private val mRipplePaint = Paint()
    private val mStrokePaint = Paint()
    private val mTextPaint = Paint()
    private val mPathEffectPaint = Paint()
    private val mPathEffectPaint2 = Paint()

    private var mScaleWidth = 0
    private var mScaleHeight = 0
    private var mDegree = 0
    private var mAngle = 0
    private var mEndAngle= 0

    private var mButtonCorner = 2 * mDensity
    private var mRadius = 0
    private var mTextWidth = 0f
    private var mTextHeight = 0f

    private val mMatrix = Matrix()

    private var mPath = Path()
    private var mSuccessPath: Path? = null
    private var mSuccessPathLength = 0f
    private var mSuccessPathIntervals: FloatArray? = null

    private var mFailedPath: Path? = null
    private var mFailedPath2: Path? = null
    private var mFailedPathLength = 0f
    private var mFailedPathIntervals: FloatArray? = null

    private var mTouchX = 0f
    private var mTouchY = 0f
    private var mRippleRadius = 0f

    private val mButtonRectF = RectF()
    private val mArcRectF = RectF()

    private var mText: String = ""
    private var mLoadingAnimatorSet: AnimatorSet? = null

    init {
        if (attrs != null) {
            val ta = context.obtainStyledAttributes(attrs, R.styleable.LoadingButton, 0, 0)
            mColorPrimary = ta.getInt(R.styleable.LoadingButton_lb_btnColor, Color.BLUE)
            mDisabledBgColor = ta.getColor(R.styleable.LoadingButton_lb_btnDisabledColor, Color.LTGRAY)
            mDisabledTextColor = ta.getColor(R.styleable.LoadingButton_lb_disabledTextColor, Color.DKGRAY)
            val text = ta.getString(R.styleable.LoadingButton_lb_btnText)
            mText = text ?: ""
            mTextColor = ta.getColor(R.styleable.LoadingButton_lb_textColor, Color.WHITE)
            resetAfterFailed = ta.getBoolean(R.styleable.LoadingButton_lb_resetAfterFailed, true)
            rippleColor = ta.getColor(R.styleable.LoadingButton_lb_btnRippleColor, Color.BLACK)
            rippleEnable = ta.getBoolean(R.styleable.LoadingButton_lb_rippleEnable, true)
            mRippleAlpha = ta.getFloat(R.styleable.LoadingButton_lb_btnRippleAlpha, 0.3f)
            mButtonCorner = ta.getDimension(R.styleable.LoadingButton_lb_cornerRadius, 2 * mDensity)
            mMinHeight = ta.getDimension(R.styleable.LoadingButton_lb_min_height, defaultMinHeight)
            ta.recycle()
        }

        mPaint.apply {
            setLayerType(LAYER_TYPE_SOFTWARE, this)
            isAntiAlias = true
            color = mColorPrimary
            style = Paint.Style.FILL
            setShadowDepth(2 * mDensity)
        }

        mRipplePaint.apply {
            isAntiAlias = true
            color = rippleColor
            alpha = (mRippleAlpha * 255).toInt()
            style = Paint.Style.FILL
        }

        mStrokePaint.apply {
            isAntiAlias = true
            color = mColorPrimary
            style = Paint.Style.STROKE
            strokeWidth = 2 * mDensity
        }

        mTextPaint.apply {
            isAntiAlias = true
            color = mTextColor
            textSize = 16 * mDensity
            isFakeBoldText = true
        }

        mTextWidth = mTextPaint.measureText(mText)
        mTextHeight = measureTextHeight(mTextPaint)

        mPathEffectPaint.apply {
            isAntiAlias = true
            color = mColorPrimary
            style = Paint.Style.STROKE
            strokeWidth = 2 * mDensity
        }

        mPathEffectPaint2.apply {
            isAntiAlias = true
            color = mColorPrimary
            style = Paint.Style.STROKE
            strokeWidth = 2 * mDensity
        }

        setLayerType(LAYER_TYPE_SOFTWARE, mPaint)
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        setMeasuredDimension(
                measureDimension((DEFAULT_WIDTH * mDensity).toInt(), widthMeasureSpec),
                measureDimension((DEFAULT_HEIGHT * mDensity).toInt(), heightMeasureSpec))
    }

    override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
        super.onSizeChanged(w, h, oldw, oldh)
        val viewHeight = max(h, mMinHeight.toInt())
        mRadius = (viewHeight - mPadding * 2).toInt() / 2
        mButtonRectF.top = mPadding
        mButtonRectF.bottom = viewHeight - mPadding
        mArcRectF.left = (width / 2 - mRadius).toFloat()
        mArcRectF.top = mPadding
        mArcRectF.right = (width / 2 + mRadius).toFloat()
        mArcRectF.bottom = viewHeight - mPadding
    }

    override fun setEnabled(enabled: Boolean) {
        super.setEnabled(enabled)
        if (mCurrentState == STATE_BUTTON) {
            updateButtonColor()
        }
    }

    @SuppressLint("ClickableViewAccessibility")
    override fun onTouchEvent(event: MotionEvent): Boolean {
        if (!isEnabled) {
            return true
        }
        when (event.action) {
            MotionEvent.ACTION_DOWN -> {
                mTouchX = event.x
                mTouchY = event.y
                playTouchDownAnimation()
            }
            MotionEvent.ACTION_UP -> if (event.x > mButtonRectF.left && event.x < mButtonRectF.right && event.y > mButtonRectF.top && event.y < mButtonRectF.bottom) {
                // only register as click if finger is up inside view
                playRippleAnimation()
            } else {
                // if finger is moved outside view and lifted up, reset view
                mTouchX = 0f
                mTouchY = 0f
                mRippleRadius = 0f
                mRipplePaint.alpha = (mRippleAlpha * 255).toInt()
                mPaint.setShadowDepth(2 * mDensity)
                invalidate()
            }
        }
        return true
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        val viewHeight = max(height, mMinHeight.toInt())
        when (mCurrentState) {
            STATE_BUTTON, STATE_ANIMATION_STEP1 -> {
                val cornerRadius = (mRadius - mButtonCorner) * (mScaleWidth / (width / 2 - viewHeight / 2).toFloat()) + mButtonCorner
                mButtonRectF.left = mScaleWidth.toFloat()
                mButtonRectF.right = (width - mScaleWidth).toFloat()
                canvas.drawRoundRect(mButtonRectF, cornerRadius, cornerRadius, mPaint)
                if (mCurrentState == STATE_BUTTON) {
                    canvas.drawText(mText, (width - mTextWidth) / 2, (viewHeight - mTextHeight) / 2 + mPadding * 2, mTextPaint)
                    if ((mTouchX > 0 || mTouchY > 0) && rippleEnable) {
                        canvas.clipRect(0f, mPadding, width.toFloat(), viewHeight - mPadding)
                        canvas.drawCircle(mTouchX, mTouchY, mRippleRadius, mRipplePaint)
                    }
                }
            }
            STATE_ANIMATION_STEP2 -> {
                canvas.drawCircle((width / 2).toFloat(), (viewHeight / 2).toFloat(), (mRadius - mScaleHeight).toFloat(), mPaint)
                canvas.drawCircle((width / 2).toFloat(), (viewHeight / 2).toFloat(), mRadius - mDensity, mStrokePaint)
            }
            STATE_ANIMATION_LOADING -> {
                mPath.reset()
                mPath.addArc(mArcRectF, (270 + mAngle / 2).toFloat(), (360 - mAngle).toFloat())
                if (mAngle != 0) {
                    mMatrix.setRotate(mDegree.toFloat(), (width / 2).toFloat(), (viewHeight / 2).toFloat())
                    mPath.transform(mMatrix)
                    mDegree += 10
                }
                canvas.drawPath(mPath, mStrokePaint)
            }
            STATE_STOP_LOADING -> {
                mPath.reset()
                mPath.addArc(mArcRectF, (270 + mAngle / 2).toFloat(), mEndAngle.toFloat())
                if (mEndAngle != 360) {
                    mMatrix.setRotate(mDegree.toFloat(), (width / 2).toFloat(), (viewHeight / 2).toFloat())
                    mPath.transform(mMatrix)
                    mDegree += 10
                }
                canvas.drawPath(mPath, mStrokePaint)
            }
            STATE_ANIMATION_SUCCESS -> {
                canvas.drawPath(mSuccessPath!!, mPathEffectPaint)
                canvas.drawCircle((width / 2).toFloat(), (viewHeight / 2).toFloat(), mRadius - mDensity, mStrokePaint)
            }
            STATE_ANIMATION_FAILED -> {
                canvas.drawPath(mFailedPath!!, mPathEffectPaint)
                canvas.drawPath(mFailedPath2!!, mPathEffectPaint2)
                canvas.drawCircle((width / 2).toFloat(), (viewHeight / 2).toFloat(), mRadius - mDensity, mStrokePaint)
            }
        }
    }

    /**
     * start loading,play animation
     */
    fun startLoading() {
        if (mCurrentState == STATE_ANIMATION_FAILED && !resetAfterFailed) {
            scaleFailedPath()
            return
        }

        if (mCurrentState == STATE_BUTTON) {
            mCurrentState = STATE_ANIMATION_STEP1
            mPaint.clearShadowLayer()
            playStartAnimation(false)
        }
    }

    /**
     * loading data successful
     */
    fun loadingSuccessful() {
        if (mLoadingAnimatorSet != null && mLoadingAnimatorSet!!.isStarted) {
            mLoadingAnimatorSet!!.end()
            mCurrentState = STATE_STOP_LOADING
            playSuccessAnimation()
        }
    }

    /**
     * loading data failed
     */
    fun loadingFailed() {
        if (mLoadingAnimatorSet != null && mLoadingAnimatorSet!!.isStarted) {
            mLoadingAnimatorSet!!.end()
            mCurrentState = STATE_STOP_LOADING
            playFailedAnimation()
        }
    }

    fun cancelLoading() {
        if (mCurrentState != STATE_ANIMATION_LOADING) {
            return
        }
        cancel()
    }

    /**
     * reset view to Button with animation
     */
    fun reset(){
        when(mCurrentState){
            STATE_ANIMATION_SUCCESS -> scaleSuccessPath()
            STATE_ANIMATION_FAILED -> scaleFailedPath()
        }
    }


    private fun measureTextHeight(paint: Paint): Float{
        val bounds = Rect()
        paint.getTextBounds(mText, 0, mText.length, bounds)
        return bounds.height().toFloat()
    }

    private fun createSuccessPath() {

        if (mSuccessPath != null) {
            mSuccessPath!!.reset()
        } else {
            mSuccessPath = Path()
        }

        val mLineWith = 2 * mDensity

        val left = (width / 2 - mRadius).toFloat() + (mRadius / 3).toFloat() + mLineWith
        val top = mPadding + (mRadius / 2).toFloat() + mLineWith
        val right = (width / 2 + mRadius).toFloat() - mLineWith - (mRadius / 3).toFloat()
        val bottom = (mLineWith + mRadius) * 1.5f + mPadding / 2
        val xPoint = (width / 2 - mRadius / 6).toFloat()

        mSuccessPath = Path().apply {
            moveTo(left, mPadding + mRadius.toFloat() + mLineWith)
            lineTo(xPoint, bottom)
            lineTo(right, top)
        }

        mSuccessPathLength = PathMeasure(mSuccessPath, false).length
        mSuccessPathIntervals = floatArrayOf(mSuccessPathLength, mSuccessPathLength)
    }

    private fun createFailedPath() {

        if (mFailedPath != null) {
            mFailedPath!!.reset()
            mFailedPath2!!.reset()
        } else {
            mFailedPath = Path()
            mFailedPath2 = Path()
        }

        val left = (width / 2 - mRadius + mRadius / 2).toFloat()
        val top = mRadius / 2 + mPadding

        mFailedPath!!.moveTo(left, top)
        mFailedPath!!.lineTo(left + mRadius, top + mRadius)

        mFailedPath2!!.moveTo((width / 2 + mRadius / 2).toFloat(), top)
        mFailedPath2!!.lineTo((width / 2 - mRadius + mRadius / 2).toFloat(), top + mRadius)

        mFailedPathLength = PathMeasure(mFailedPath, false).length
        mFailedPathIntervals = floatArrayOf(mFailedPathLength, mFailedPathLength)

        mPathEffectPaint2.pathEffect = DashPathEffect(mFailedPathIntervals, mFailedPathLength)
    }

    private fun measureDimension(defaultSize: Int, measureSpec: Int) =
            when (MeasureSpec.getMode(measureSpec)) {
                MeasureSpec.EXACTLY -> MeasureSpec.getSize(measureSpec)
                MeasureSpec.AT_MOST -> min(defaultSize, MeasureSpec.getSize(measureSpec))
                MeasureSpec.UNSPECIFIED -> defaultSize
                else -> defaultSize
            }

    private fun updateButtonColor() {
        mPaint.color = if(isEnabled) mColorPrimary else mDisabledBgColor
        mTextPaint.color = if(isEnabled) mTextColor else mDisabledTextColor
        if(backgroundShader != null){
            if(isEnabled) mPaint.shader = backgroundShader else mPaint.shader = null
        }
        invalidate()
    }

    private fun playTouchDownAnimation(){
        ValueAnimator.ofFloat(0f, 1f)
                .apply {
                    duration = 240
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val progress = valueAnimator.animatedValue as Float
                        mPaint.setShadowDepth((2 + 4 * progress) * mDensity)
                        mRippleRadius = width *  progress
                        //mRipplePaint.alpha = (255 * mRippleAlpha * (1 - progress)).toInt()
                        invalidate()
                    }
                }
                .start()
    }


    private fun playRippleAnimation() {
        ValueAnimator.ofFloat(
                1f,
                0f)
                .apply {
                    duration = 240
                    interpolator = DecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val progress = valueAnimator.animatedValue as Float
                        mRipplePaint.alpha = (255 * mRippleAlpha * progress).toInt()
                        mPaint.setShadowDepth((2 + 4 * progress) * mDensity)
                        invalidate()
                    }
                    doOnEnd {
                        doClick()
                    }
                }.start()
    }

    private fun doClick(){
        mTouchX = 0f
        mTouchY = 0f
        mRipplePaint.alpha = (mRippleAlpha * 255).toInt()
        mRippleRadius = 0f
        invalidate()
        performClick()
    }

    private fun playStartAnimation(isReverse: Boolean) {
        val viewHeight = max(height, mMinHeight.toInt())
        val animator = ValueAnimator.ofInt(
                if (isReverse) width / 2 - viewHeight / 2 else 0,
                if (isReverse) 0 else width / 2 - viewHeight / 2)
                .apply {
                    duration = 400
                    interpolator = AccelerateDecelerateInterpolator()
                    startDelay = 100
                    addUpdateListener { valueAnimator ->
                        mScaleWidth = valueAnimator.animatedValue as Int
                        invalidate()
                    }
                    doOnEnd {
                        mCurrentState = if (isReverse) STATE_BUTTON else STATE_ANIMATION_STEP2
                        if (mCurrentState == STATE_BUTTON) {
                            mPaint.setShadowDepth(2 * mDensity)
                            invalidate()
                        }
                    }
                }

        val animator2 = ValueAnimator.ofInt(if (isReverse) mRadius else 0, if (isReverse) 0 else mRadius)
                .apply {
                    duration = 240
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        mScaleHeight = valueAnimator.animatedValue as Int
                        invalidate()
                    }
                    doOnEnd {
                        mCurrentState = if (isReverse) STATE_ANIMATION_STEP1 else STATE_ANIMATION_LOADING
                        if (!isReverse) updateButtonColor()
                    }
                }

        val loadingAnimator = ValueAnimator.ofInt(30, 300)
                .apply {
                    duration = 1000
                    repeatCount = ValueAnimator.INFINITE
                    repeatMode = ValueAnimator.REVERSE
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        mAngle = valueAnimator.animatedValue as Int
                        invalidate()
                    }
                }

        mLoadingAnimatorSet?.cancel()
        mLoadingAnimatorSet = AnimatorSet()
        mLoadingAnimatorSet!!.doOnEnd {
            isEnabled = true
            updateButtonColor()
        }
        if (isReverse) {
            mLoadingAnimatorSet!!.playSequentially(animator2, animator)
            mLoadingAnimatorSet!!.start()
            return
        }
        mLoadingAnimatorSet!!.playSequentially(animator, animator2, loadingAnimator)
        mLoadingAnimatorSet!!.start()
    }

    private fun playSuccessAnimation() {
        createSuccessPath()
        val animator = ValueAnimator.ofInt(360 - mAngle, 360)
                .apply {
                    duration = 240
                    interpolator = DecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        mEndAngle = valueAnimator.animatedValue as Int
                        invalidate()
                    }
                    doOnEnd { mCurrentState = STATE_ANIMATION_SUCCESS }
                }

        val successAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
                .apply {
                    duration = 500
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val value = valueAnimator.animatedValue as Float
                        val pathEffect = DashPathEffect(mSuccessPathIntervals, mSuccessPathLength - mSuccessPathLength * value)
                        mPathEffectPaint.pathEffect = pathEffect
                        invalidate()
                    }
                }

        AnimatorSet().apply {
                    playSequentially(animator, successAnimator)
                    doOnEnd {
                        animationEndAction?.invoke(AnimationType.SUCCESSFUL)
                    }
                }.start()
    }

    private fun playFailedAnimation() {
        createFailedPath()
        val animator = ValueAnimator.ofInt(360 - mAngle, 360)
                .apply {
                    duration = 240
                    interpolator = DecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        mEndAngle = valueAnimator.animatedValue as Int
                        invalidate()
                    }
                    doOnEnd { mCurrentState = STATE_ANIMATION_FAILED }
                }

        val failedAnimator = ValueAnimator.ofFloat(0.0f, 1.0f)
                .apply {
                    duration = 300
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val value = valueAnimator.animatedValue as Float
                        mPathEffectPaint.pathEffect = DashPathEffect(mFailedPathIntervals, mFailedPathLength - mFailedPathLength * value)
                        invalidate()
                    }
                }

        val failedAnimator2 = ValueAnimator.ofFloat(0.0f, 1.0f)
                .apply {
                    duration = 300
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val value = valueAnimator.animatedValue as Float
                        mPathEffectPaint2.pathEffect = DashPathEffect(mFailedPathIntervals, mFailedPathLength - mFailedPathLength * value)
                        invalidate()
                    }
                }

        AnimatorSet().apply {
            playSequentially(animator, failedAnimator, failedAnimator2)
            doOnEnd {
                if (resetAfterFailed) {
                    postDelayed({ scaleFailedPath() }, 1000)
                }else{
                    animationEndAction?.invoke(AnimationType.FAILED)
                }
            }
        }.start()
    }

    private fun cancel() {
        mCurrentState = STATE_STOP_LOADING
        ValueAnimator.ofInt(360 - mAngle, 360)
                .apply {
                    duration = 240
                    interpolator = DecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        mEndAngle = valueAnimator.animatedValue as Int
                        invalidate()
                    }
                    doOnEnd {
                        mCurrentState = STATE_ANIMATION_STEP2
                        playStartAnimation(true)
                    }
                }.start()
    }

    private fun scaleSuccessPath() {
        val scaleMatrix = Matrix()
        val viewHeight = max(height, mMinHeight.toInt())
        ValueAnimator.ofFloat(1.0f, 0.0f)
                .apply {
                    duration = 300
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val value = valueAnimator.animatedValue as Float
                        scaleMatrix.setScale(value, value, (width / 2).toFloat(), (viewHeight / 2).toFloat())
                        mSuccessPath!!.transform(scaleMatrix)
                        invalidate()
                    }
                    doOnEnd {
                        mCurrentState = STATE_ANIMATION_STEP2
                        playStartAnimation(true)
                    }
                }.start()
    }

    private fun scaleFailedPath() {
        val scaleMatrix = Matrix()
        val viewHeight = max(height, mMinHeight.toInt())
        ValueAnimator.ofFloat(1.0f, 0.0f)
                .apply {
                    duration = 300
                    interpolator = AccelerateDecelerateInterpolator()
                    addUpdateListener { valueAnimator ->
                        val value = valueAnimator.animatedValue as Float
                        scaleMatrix.setScale(value, value, (width / 2).toFloat(), (viewHeight / 2).toFloat())
                        mFailedPath!!.transform(scaleMatrix)
                        mFailedPath2!!.transform(scaleMatrix)
                        invalidate()
                    }
                    doOnEnd {
                        mCurrentState = STATE_ANIMATION_STEP2
                        playStartAnimation(true)
                    }
                }.start()
    }
}

private fun Animator.doOnEnd(action: (animator: Animator?) -> Unit) {
    this.addListener(object : Animator.AnimatorListener{
        override fun onAnimationRepeat(animation: Animator?){}

        override fun onAnimationEnd(animation: Animator?) = action(animation)

        override fun onAnimationCancel(animation: Animator?){}

        override fun onAnimationStart(animation: Animator?){}
    })
}

private fun Paint.setShadowDepth(depth: Float){
    this.setShadowLayer(depth, 0f, 2f, 0x6F000000.toInt())
}

相关文章

网友评论

      本文标题:下载 加载 loading。。。。。

      本文链接:https://www.haomeiwen.com/subject/ldtvahtx.html