在Android实现光影移动效果【流光效果】

说明

本文是在Android实现光影移动效果【流光效果】

效果如下

图1 ShimmerView
图2 ShimmerTextView

ShimmerView.kt


import android.animation.ValueAnimator
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Paint
import android.graphics.Path
import android.graphics.Point
import android.graphics.RectF
import android.graphics.Shader
import android.util.AttributeSet
import android.view.View
import android.view.animation.LinearInterpolator

class ShimmerView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
    private var mWidth = -1
    private var mSlope: Float = -1F
    private var mAnimMode = 0
    private var mDuration = 1600L
    private var mRepeatCount = 0
    private var mColors = intArrayOf(0x00FFFFFF, 0x5AFFFFFF, 0x5AFFFFFF, 0x00FFFFFF)
    private var mPositions = floatArrayOf(0f, 0.5f, 0.51f, 1f)
    private var mRadius = 0

    private var mPaint: Paint = Paint()
    private var mPath: Path? = null
    private var mClipPath: Path? = null

    private var mValueAnimator: ValueAnimator? = null

    init {
        attrs?.let {
            context.obtainStyledAttributes(attrs, R.styleable.ShimmerView).apply {
                try {
                    mWidth = getDimensionPixelSize(R.styleable.ShimmerView_csWidth, mWidth)
                    mSlope = getFloat(R.styleable.ShimmerView_csSlope, mSlope)
                    mRadius = getDimensionPixelSize(R.styleable.ShimmerView_csRadius, mRadius)
                    mAnimMode = getInt(R.styleable.ShimmerView_csAnimMode, mAnimMode)
                    mDuration =
                        getInt(R.styleable.ShimmerView_csDuration, mDuration.toInt()).toLong()
                    mRepeatCount = getInt(R.styleable.ShimmerView_csRepeat, mRepeatCount)
                    val colorsStr = getString(R.styleable.ShimmerView_csColors)
                    val positionsStr = getString(R.styleable.ShimmerView_csPositions)
                    if (!colorsStr.isNullOrBlank() && !positionsStr.isNullOrBlank()) {
                        val colorArr =
                            colorsStr.split(",".toRegex()).dropLastWhile { it.isEmpty() }
                                .toTypedArray()
                        val positionArr =
                            positionsStr.split(",".toRegex()).dropLastWhile { it.isEmpty() }
                                .toTypedArray()
                        val size = colorArr.size
                        if (size == positionArr.size) {
                            mColors = IntArray(size)
                            mPositions = FloatArray(size)
                            for (i in 0 until size) {
                                mColors[i] = Color.parseColor(colorArr[i])
                                mPositions[i] = positionArr[i].toFloat()
                            }
                        }
                    }
                } finally {
                    recycle()
                }
            }
        }
    }


    public override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        val widthSize = MeasureSpec.getSize(widthMeasureSpec)
        val heightSize = MeasureSpec.getSize(heightMeasureSpec)
        initSetup(widthSize, heightSize)
        if (mAnimMode == 0) {
            showAnimation(widthSize, heightSize, mRepeatCount, mDuration)
        }
    }

    private fun initSetup(width: Int, height: Int) {
        if (mRepeatCount < 0) {
            mRepeatCount = -1
        }
        if (mWidth < 0) {
            mWidth = width / 3
        }
        if (mSlope < 0) {
            mSlope = height / width.toFloat()
        }

        val point1 = Point(0, 0)
        val point2 = Point(width, 0)
        val point3 = Point(width, height)
        val point4 = Point(0, height)
        mPath = Path()
        mPath?.moveTo(point1.x.toFloat(), point1.y.toFloat())
        mPath?.lineTo(point2.x.toFloat(), point2.y.toFloat())
        mPath?.lineTo(point3.x.toFloat(), point3.y.toFloat())
        mPath?.lineTo(point4.x.toFloat(), point4.y.toFloat())
        mPath?.close()



        mClipPath = Path()
        val mRect = RectF()
        mRect[0f, 0f, width.toFloat()] = height.toFloat()
        mClipPath?.addRoundRect(mRect, mRadius.toFloat(), mRadius.toFloat(), Path.Direction.CW)

    }


    public override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        //绘制圆角
        mClipPath?.let { canvas.clipPath(it) }
        //绘制流光
        mPath?.let { canvas.drawPath(it, mPaint) }
    }

    private fun showAnimation(width: Int, height: Int, repeatCount: Int, duration: Long) {
        val offset = mWidth.toFloat()
        mValueAnimator?.cancel()
        mValueAnimator = ValueAnimator.ofFloat(0f - offset * 2, width + offset * 2)
        mValueAnimator?.repeatCount = repeatCount
        mValueAnimator?.interpolator = LinearInterpolator()
        mValueAnimator?.duration = duration
        mValueAnimator?.addUpdateListener { animation: ValueAnimator ->
            val value = animation.animatedValue as Float
            mPaint.shader = LinearGradient(
                value,
                mSlope * value,
                value + offset,
                mSlope * (value + offset),
                mColors,
                mPositions,
                Shader.TileMode.CLAMP
            )
            invalidate()
        }
        mValueAnimator?.start()
    }

    public override fun onDetachedFromWindow() {
        super.onDetachedFromWindow()
        mValueAnimator?.cancel()
        mValueAnimator = null
    }

    fun setColorAndPositions(colors: IntArray, positions: FloatArray) {
        if (colors.size != positions.size) {
            throw RuntimeException("colors&positions的Array.size必须一致")
        }
        this.mColors = colors
        this.mPositions = positions
    }

    fun setSlope(mSlope: Float) {
        this.mSlope = mSlope
    }

    fun setWidth(mWidth: Int) {
        this.mWidth = mWidth
    }

    fun startLightingAnimation(duration: Long = mDuration, repeatCount: Int = mRepeatCount) {
        showAnimation(width, height, repeatCount, duration)
    }


}

ShimmerView定义的 attrs:


    <declare-styleable name="ShimmerView">
        <!--自动还是手动-->
        <attr name="csAnimMode" format="enum">
            <enum name="auto" value="0" />
            <enum name="manual" value="1" />
        </attr>
        <!--光影宽度-->
        <attr name="csWidth" format="dimension" />
        <!--光影斜率 范围【-1 ~ 1-->
        <attr name="csSlope" format="float" />
        <!--控件的圆角大小-->
        <attr name="csRadius" format="dimension" />
        <!-- -1:无限循环,0:其他代表重复执行几次-->
        <attr name="csRepeat" format="integer" />
        <!--动画时长 单位ms-->
        <attr name="csDuration" format="integer" />
        <!--颜色值 举例:{0x00FFFFFF, 0x88FFFFFF, 0x00FFFFFF}-->
        <attr name="csColors" format="string" />
        <!--颜色值对应的位置数组  (值范围0~1) 举例:[0f,0.5f,1f]  与csAngle数组大小必须一致-->
        <attr name="csPositions" format="string" />
    </declare-styleable>

ShimmerTextView.kt


import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Matrix
import android.graphics.Paint
import android.graphics.Shader
import android.util.AttributeSet
import androidx.appcompat.widget.AppCompatTextView

class ShimmerTextView @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) : AppCompatTextView(context, attrs, defStyleAttr) {
    private var mLinearGradient: LinearGradient? = null

    private var mGradientMatrix: Matrix? = null

    private var mViewWidth = 0

    private var mTranslate = 0

    private var mAnimating = true

    private val speed = 50
    private var mPaint: Paint? = null

    private var textColor = 0
    private var shimmerColor = 0

    init {
        attrs?.let {
            context.obtainStyledAttributes(attrs, R.styleable.ShimmerTextView).apply {
                try {
                    textColor = getColor(R.styleable.ShimmerTextView_stvTextColor, Color.BLACK)
                    shimmerColor =
                        getColor(R.styleable.ShimmerTextView_stvShimmerColor, Color.WHITE)
                    mPaint = paint
                    mGradientMatrix = Matrix()
                } finally {
                    recycle()
                }

            }
        }

    }

    fun setShimmer(isShimmer: Boolean) {
        mAnimating = isShimmer
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        mViewWidth = measuredWidth
    }

    fun initLinearGradient() {
        mLinearGradient = LinearGradient(
            0f,
            0f,
            mViewWidth.toFloat(),
            0f,
            intArrayOf(textColor, shimmerColor, textColor),
            null,
            Shader.TileMode.CLAMP
        )
        mPaint?.shader = mLinearGradient
    }

    override fun onDraw(canvas: Canvas) {
        super.onDraw(canvas)
        if (mAnimating) {
            if (mGradientMatrix != null && mLinearGradient != null) {
                mTranslate += mViewWidth / 10
                if (mTranslate > 2 * mViewWidth) {
                    mTranslate = -mViewWidth
                }
                mGradientMatrix?.setTranslate(mTranslate.toFloat(), 0f)
                mLinearGradient?.setLocalMatrix(mGradientMatrix)
            } else {
                initLinearGradient()
            }
            postInvalidateDelayed(speed.toLong())
        }
    }
}

ShimmerTextView 定义的 attrs:

    <declare-styleable name="ShimmerTextView">
        <attr name="stvTextColor" format="color" />
        <attr name="stvShimmerColor" format="color" />
    </declare-styleable>

DEMO

  1. Demo.apk 点击下载

项目和演示效果可以去Github查看

项目地址: https://github.com/logan0817/shinningview 。

如果你有任何疑问可以留言。

如果这篇文章对你有帮助,可以赏个赞支持一下作者。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/372649.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

从零开始 TensorRT(4)命令行工具篇:trtexec 基本功能

前言 学习资料&#xff1a; TensorRT 源码示例 B站视频&#xff1a;TensorRT 教程 | 基于 8.6.1 版本 视频配套代码 cookbook 参考源码&#xff1a;cookbook → 07-Tool → trtexec 官方文档&#xff1a;trtexec 在 TensorRT 的安装目录 xxx/TensorRT-8.6.1.6/bin 下有命令行…

PHP安装后错误处理

一&#xff1a;问题 安装PHP后提示错误如下 二&#xff1a;解决 1&#xff1a;Warning: Module mysqli already loaded in Unknown on line 0解决 原因&#xff1a;通过php.ini配置文件开启mysqli扩展的时候&#xff0c;开启了多次 解决&#xff1a;将php.ini配置文件中多个…

如何计算JMeter性能和稳定性测试中的TPS?

1、普通计算公式 TPS 总请求数 / 总时间 按照需求得到基础数据&#xff0c;比如在去年第xxx周&#xff0c;某平台有5万的浏览量那么总请求数我们可以估算为5万&#xff08;1次浏览都至少对应1个请求&#xff09; 总请求数 50000请求数 总时间&#xff1a;由于不知道每个请…

DBeaver添加阿里maven镜像

1、点击数据库->驱动管理器 2、选择任意数据库&#xff0c;点击编辑按钮 3、点击下载/更新(D) 4、点击下载配置 5、点击添加 6、添加阿里云地址 http://maven.aliyun.com/nexus/content/groups/public/ 7、将阿里云地址移动到首位并点击"应用并关闭"

【漏洞复现】大华智慧园区综合管理平台bitmap接口存在任意文件上传漏洞

漏洞描述 大华智慧园区综合管理平台是一款综合管理平台,具备园区运营、资源调配和智能服务等功能。平台意在协助优化园区资源分配,满足多元化的管理需求,同时通过提供智能服务,增强使用体验。大华智慧园区综合管理平台bitmap接口存在任意文件上传漏洞,但未在上传的文件类…

路由聚合问题和子网划分问题范例

看到网上有人询问下面的问题&#xff1a; 前者是路由聚合的问题&#xff0c;后者是子网划分计算的问题。解答过程如下&#xff1a; 第五题&#xff0c;路由聚合答案是B 路由聚合可以减少路由条目&#xff0c;提高效率&#xff0c;一般都要通过减小掩码值来完成。 首先&am…

MATLAB实现高通滤波(附完整代码)

1.MATLAB实现高通滤波器 以下是一个使用MATLAB实现高通滤波器的例子。在这个例子中&#xff0c;我们将设计一个简单的数字高通滤波器&#xff0c;然后将其应用到一个包含低频和高频成分的信号上。 clc;close all;clear all;warning off;%清除变量 rand(seed, 500); randn(s…

ANTLR4规则解析生成器(一):入门

文章目录 1 什么是ANTLR42 为什么需要ANTLR43 环境搭建4 官方示例4.1 编写语法规则文件4.2 生成语法解析器4.3 基于SDK实现逻辑 5 总结 1 什么是ANTLR4 ANTLR是ANother Tool for Language Recognition的缩写&#xff0c;它是一个强大的用于读取、处理、执行和翻译结构化文本或…

你了解引用和指针的区别吗?

前言&#xff1a; 在计算机编程中&#xff0c;引用和指针是两个重要的概念&#xff0c;它们用于处理内存中的数据。它们在很多编程语言中都有相应的支持&#xff0c;例如C和C。对于c语言来说&#xff0c;指针是最重要的概念之一&#xff0c;想要学好c语言就难以绕开对于指针的学…

想上岸?有这个神器足矣!

之前说的给大家一个大惊喜&#xff01;今天终于迎来了见证时刻&#xff01; 我们的官网上线啦&#xff01;&#xff01;&#xff01; 截止目前我已经做了200套名校真题&#xff0c;100所择校分析&#xff0c;150篇分院校重点勾画&#xff0c;以及非常非常多的文章&#xff0c…

简单的JavaScript去下载转换为Base64的PDF文件

新建一个文件&#xff0c;内容填写如下&#xff0c;然后保存为 .html 类型的文件 再用浏览器打开&#xff0c;就会是下面这样子&#xff1a; 图一红色textarea里面&#xff0c;可以将PDF文件转换成BASE64位后的内容贴进去&#xff0c;点击下载时&#xff0c;就可以直接下载成PD…

Matlab数字图像处理——图像复原与滤波算法应用方法

图像处理领域一直以来都是计算机科学和工程学的一个重要方向&#xff0c;图像复原则是其中一个重要的研究方向之一。图像复原旨在通过运用各种滤波算法&#xff0c;对图像进行去噪、恢复和改善&#xff0c;以提高图像的质量和可视化效果。在本文中&#xff0c;我们将介绍如下内…

WordPress主题YIA如何将首页的置顶小工具改为站长推荐小工具?

YIA主题有“置顶推荐”小工具&#xff0c;首页文章列表页有置顶功能&#xff0c;可在YIA主题设置 >> 列表 >> 首页-最新发布 >> 显示置顶文章中开启或关闭。如果将“置顶推荐”小工具添加到“首页顶栏”&#xff0c;同时也开启首页最新发布的“显示置顶文章”…

kernel32.dll文件缺失要如何解决?科学分享kernel32.dll文件

面对 kernel32.dll 文件丢失的问题&#xff0c;别担心&#xff01;这篇文章将为您提供多种有效的解决策略&#xff0c;不论您是电脑专家还是刚入门的新手&#xff0c;我们的指南都能帮到您。详细的步骤和每种方法的具体注意点都在这里&#xff0c;按照指南操作&#xff0c;您将…

【c/python】GtkGrid

一、GtkGrid GtkGrid 是 GTK (GIMP Toolkit) 中的一个基础容器构件&#xff08;widget&#xff09;&#xff0c;它可以用来安排其他构件在一个灵活的多行多列的网格中。每个加入网格的构件都可以占据一个或多个行和列。由于 GtkGrid 提供了在二维空间中安排构件的方式&#xf…

Jenkins配置http请求github,发布release

学无止境&#xff0c;气有浩然&#xff01; Jenkins配置http请求github&#xff0c;发布release 前言Jenkins配置github配置在这里插入图片描述 打完收工! 前言 工作中进行了github迁移&#xff0c;原先的gitlab中配置的Jenkins的CI/CD步骤需要发布到Github发布release版本&am…

【发票识别】新增针对图片发票的识别(升级中)

说明 为了完善发票识别的功能&#xff0c;目前发票识别支持发票图片格式的识别&#xff0c;增加可用性。 体验 体验地址&#xff1a;https://invoice.behappyto.cn/invoice-service/ 体验地址上面有示例的发票&#xff0c;可以下载上传识别或者复制url地址进行识别。 技术栈…

Windows下Node.js下载安装及环境变量配置教程

Windows下Node.js下载安装及环境变量配置教程 安装版本&#xff1a;node-v18.19.0-x64.msi 文章目录 Windows下Node.js下载安装及环境变量配置教程一、Node.js和NPM简介二、下载地址三、安装步骤四、环境配置五、安装淘宝镜像总结 一、Node.js和NPM简介 1、Node.js &#xf…

产品经理学习-产品运营《如何策划一场活动》

互联网活动怎么玩 最常听到的有&#xff1a; 注册有奖、拉新有奖 签到积分 秒杀、大促、神券 和过去相比&#xff0c;现在活动的特征变化&#xff1a; 线上化、形式丰富、覆盖人群广、即时性、效果可控 什么是活动运营 通过策划不同形式的活动&#xff0c;进行有效的资源和…

渗透测试练习题解析 2(CTF web)

题目均来自 BUUCTF 1、[极客大挑战 2019]Upload 1 考点&#xff1a;文件上传漏洞 进入靶场 一看就知道是考察文件上传漏洞&#xff0c;看源码有没有敏感信息 没有什么敏感信息&#xff0c;那我们试着按要求传一张图片看看结果&#xff0c;但是传了 png、jpg 类型的图片后发现上…
最新文章