稀土掘金技术社区 2024年12月09日
Android 颜色渐变动画效果
index_new5.html
../../../zaker_core/zaker_tpl_static/wap/tpl_guoji1.html

 

本文介绍实现控件点击后背景颜色渐变效果的过程,包括场景分析及具体实现,强调基础知识的重要性

🎨用渐变解决颜色衔接问题,分析三种渐变类型并选择线性演示

💻通过动态代码实现渐变色,尝试多种方法,最终找到合适方案

🎬使用属性动画让颜色变化过程更丝滑,结合代码展示实现过程

📋总结实现动画渐变效果的步骤,强调基础知识是开发关键

原创 流浪汉kylin 2024-12-09 09:12 重庆

点击关注公众号,“技术干货” 及时达!

点击关注公众号,“技术干货” 及时达!

1. 前言

今天来试一个颜色变化的效果,有个控件点击后,背景的颜色就逐渐变成另一个颜色。类似这样的一个效果


这个如果用自定义View是有一万种方法去实现,但是我们要考虑到希望所有控件都能做到,所以就不用自定义View的方式去实现这种效果,比如之前你的同事做了一个控件,你的产品让你给这个控件的点击事件加个酷炫的效果,大概上边演示的效果。

2. 场景分析

可以先分析一下怎么去实现这个效果,首先,颜色的变化,是一个过程,那这个过程中,两个颜色之间衔接,不能太生硬,比如你x位置还是颜色A,x+1的位置马上变成颜色B,那这样的变化就太生硬了,要做一个颜色变化的过程,我想到的就是用渐变做。

渐变又分3种类型,线性、放射、扫描,具体用哪种,根据你的需求,比如我想变化从左到右,那就用线性,如果想像水波纹一样从右下角去变化,那就用放射,抱歉不是你想,是产品想[狗头]。这里做演示就用线性来演示吧。

颜色衔接的问题是解决了,用渐变能让颜色的变化过程看起来不会很突兀,那如何去实现颜色变化的过程,怎么去实现颜色A到颜色B的变化。颜色是这个控件的一个属性,所以是不是就能马上想到可以用属性动画来实现。

3. 实现效果

ok,通过上面的分析,我们要做两个技术点,一个是渐变,一个是属性动画。

(1)渐变的实现

首先渐变我们平时做,都是用xml去做,这里因为要配合属性动画,所以我们要动态去写代码,动态实现渐变色的效果可以使用GradientDrawable

我们先写一个demo看看效果,这里就用两种颜色来演示,基佬紫和猛男粉

private fun setBg(){      val colors = intArrayOf(          Color.parseColor("#9932CC"),          Color.parseColor("#FF69B4")      )      val drawable = GradientDrawable()      drawable.gradientType = GradientDrawable.LINEAR_GRADIENT      drawable.orientation = GradientDrawable.Orientation.LEFT_RIGHT      drawable.colors = colors      tvTest?.background = drawable  }

可以看到代码很简单,最终实现的效果(我就不贴全代码了,控件就一个TextView)


ok,我们要做的是中间渐变的那段区域从左边屏幕外移动到右边的屏幕外,那就能实现一个颜色变化的过程,这个思路应该能不难理解吧。

那么要怎么移动呢,GradientDrawable我也不熟,没关系,那就去看官方文档嘛,能发现有个方法level,我们试着写个轮询去试试这个level的效果,官方有说这个Level的范围是0-10000(这里开始上动画也行,为了方便也可以先用轮询测效果)

var level = 0
private fun startLooper() { handler.postDelayed({ if (level < 10000) { level += 100 setBg() startLooper() } }, 10) }
private fun setBg(){ val colors = intArrayOf( Color.parseColor("#9932CC"), Color.parseColor("#FF69B4") ) val drawable = GradientDrawable() drawable.gradientType = GradientDrawable.LINEAR_GRADIENT drawable.orientation = GradientDrawable.Orientation.LEFT_RIGHT drawable.useLevel = true drawable.level = level drawable.colors = colors tvTest?.background = drawable }

看最后的效果


看得出这颜色变化只能变一半,没办法全部变完,level为0的效果就是单色的效果,最终的1000的level效果就是原始的样子。那我尝试把level设超过10000会怎样,我设个10W


能看出好像是实现了这个效果。其实不然,首先这个最终也并没有完全是单色(只要你把其中一个颜色换成白色就能很容易看出),其次后半段的速度和前面的不同

所以不能勉强用这个方法去做,那为什么要写这个失败的demo出来呢,因为我觉得用Level应该也能有办法实现这个效果的,只是我没时间去认真研究,就简单的使用的话是做不到,但是通过一些奇技淫巧肯定还是能做到的。

既然level实现不了我们想要的效果,没关系,我们再看看文档,发现其实setColors方法是有一个参数的,也有两个参数的,双参的方法中是一个offsets数组,那我感觉这个就是我要找的方法了,把代码改一下。

var x = 0f
private fun startLooper() { handler.postDelayed({ if (x < 1.1f) { x += 0.1f setBg() startLooper() } }, 1000) }
private fun setBg() { val colors = intArrayOf( Color.parseColor("#9932CC"), Color.parseColor("#FF69B4") ) val offsets = floatArrayOf( x - 0.1f, x ) val drawable = GradientDrawable() drawable.gradientType = GradientDrawable.LINEAR_GRADIENT drawable.orientation = GradientDrawable.Orientation.LEFT_RIGHT if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { drawable.setColors(colors, offsets) } // drawable.colors = colors tvTest?.background = drawable }

看看最终的效果


有点意思,怎么搞得像个进度条一样,无所谓,总之这样就能实现颜色渐变的这个功能,实现第一个目标。

(2)颜色变化过程的实现

让上面的效果更加丝滑,我们使用属性动画。按理来说这个过程也应该要用属性动画来实现,这样才符合属性动画的定义。

我们写一个动画

private var mValueAnimator1: ValueAnimator? = null
fun startToStopChange() { if (mValueAnimator1 == null) { mValueAnimator1 = ValueAnimator.ofFloat(0f, 1.1f) mValueAnimator1?.addUpdateListener { x = it.animatedValue as Float setBg() } } mValueAnimator1?.setDuration(1000)?.start() }

整体的代码结合起来就是

var tvTest: TextView? = null  var x = 0f    private var mValueAnimator1: ValueAnimator? = null    @SuppressLint("MissingInflatedId")  override fun onCreate(savedInstanceState: Bundle?) {      super.onCreate(savedInstanceState)      setContentView(R.layout.test_main)        tvTest = findViewById(R.id.tv_test)      setBg()        tvTest?.setOnClickListener {          startToStopChange()      }  }    private fun setBg() {      val colors = intArrayOf(          Color.parseColor("#9932CC"),          Color.parseColor("#FF69B4")      )      val offsets = floatArrayOf(          x - 0.1f,          x      )      val drawable = GradientDrawable()      drawable.gradientType = GradientDrawable.LINEAR_GRADIENT      drawable.orientation = GradientDrawable.Orientation.LEFT_RIGHT      if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {          drawable.setColors(colors, offsets)      }      // drawable.colors = colors      tvTest?.background = drawable  }    fun startToStopChange() {      if (mValueAnimator1 == null) {          mValueAnimator1 = ValueAnimator.ofFloat(0f, 1.1f)          mValueAnimator1?.addUpdateListener {              x = it.animatedValue as Float              setBg()          }      }      mValueAnimator1?.setDuration(2000)?.start()  }

加了个点击事件,看看最终的效果


ok,看得出最终的效果非常的丝滑。当然我这个是demo,所以这样简单写,主要是为了说明怎么实现,但真实要运用到项目中的话了,肯定还是需要在这代码的基础上做些优化、封装操作的。

4. 总结

简单来说,实现这样的一个动画渐变的效果很简单,只需要两步,第一步用GradientDrawable实现两个颜色之间的渐变效果,第二步用属性动画实现颜色变化的过程。

这其实也就是一些很简单的基础知识,基础知识就是你的武器库,一个复杂的效果,如果你基础牢固的话,你会把这个复杂的效果去拆分成粒度更小的效果,你知道用哪些基础知识能实现这样的效果,这才是一个真正的开发过程,而不是说产品让你实现一个你没做过的效果,你第一反应就是去搜索抄别人的代码,第一反应应该是看看你的武器库,有没有能实现这个目标的武器。我有时也会看别人的代码,但也主要是为了看实现的思路。

再复杂的效果,也是由很多个很简单的原理去实现的。比如这里的效果如果放到recyclerView的item中,还要从recyclerView的那块角度加点东西,才能兼容。

点击关注公众号,“技术干货” 及时达!

阅读原文

跳转微信打开

Fish AI Reader

Fish AI Reader

AI辅助创作,多种专业模板,深度分析,高质量内容生成。从观点提取到深度思考,FishAI为您提供全方位的创作支持。新版本引入自定义参数,让您的创作更加个性化和精准。

FishAI

FishAI

鱼阅,AI 时代的下一个智能信息助手,助你摆脱信息焦虑

联系邮箱 441953276@qq.com

相关标签

颜色变化 渐变 属性动画 基础知识
相关文章