博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android点赞控件--仿掘金点赞七成效果
阅读量:7074 次
发布时间:2019-06-28

本文共 13197 字,大约阅读时间需要 43 分钟。

0.

前些日子偶然看到掘金推荐里的点赞效果,感觉有些酷炫,然后在一个无所事事的早上,我决定实现一个类似的功能,但是只有七成的效果,效果图如下。

1.

这个效果我把它们分成了几个阶段:

(1)默认阶段:就是一个竖起来的大拇指

(2)收缩阶段:大拇指逐渐缩小直到消失

(3)放大阶段:圆圈由小到大

(4)圆环阶段:圆圈有中心破裂,露出大拇指

(5)卫星阶段:出现卫星,向远处逐渐偏离同时逐渐消失

2.

话不多说,上代码吧

package com.skateboard.favouriteviewimport android.animation.Animatorimport android.animation.ValueAnimatorimport android.content.Contextimport android.graphics.*import android.support.v4.content.ContextCompatimport android.util.AttributeSetimport android.view.Viewclass FavouriteView(context: Context, attrs: AttributeSet?, defStyle: Int) : View(context, attrs, defStyle){    private lateinit var paint: Paint    private lateinit var statellitePaint: Paint    private lateinit var path: Path    private var spaceBetweenHandAndShoulder = 5    private var state = STATE_NORMAL    private lateinit var valueAnimator: ValueAnimator    private var size = 0    private var centerX = 0f    private var centerY = 0f    private var strokeWithScaleFraction = 1f    private var statelliteOffsetFraction = 0f    private var selectedColor = Color.BLACK    private var normalColor = Color.BLACK    companion object    {        val STATE_SELECTED = 1        val STATE_CIRCLE = 2        val STATE_RING = 3        val STATE_STATELLITE = 4        val STATE_NORMAL = 0    }    constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)    constructor(context: Context) : this(context, null, 0)    init    {        if (attrs != null)        {            initParse(attrs)        }        initPaint()        initStatellitePaint()        initClickEvent()    }    private fun initParse(attrs: AttributeSet)    {        val typedArray = context.obtainStyledAttributes(attrs, R.styleable.FavouriteView)        selectedColor = typedArray.getColor(R.styleable.FavouriteView_selected_color, Color.BLACK)        normalColor = typedArray.getColor(R.styleable.FavouriteView_normal_color, Color.BLACK)        typedArray.recycle()    }    private fun initPaint()    {        paint = Paint(Paint.ANTI_ALIAS_FLAG)        paint.color = normalColor        paint.style = Paint.Style.STROKE        paint.strokeWidth = 4f        path = Path()        val cornerPathEffect = CornerPathEffect(10f)        paint.pathEffect = cornerPathEffect    }    private fun initStatellitePaint()    {        statellitePaint = Paint(Paint.ANTI_ALIAS_FLAG)        statellitePaint.color = selectedColor        statellitePaint.style=Paint.Style.FILL    }    private fun initAnimator()    {        valueAnimator = ValueAnimator.ofFloat(0f, 400f)        valueAnimator.duration = 500        valueAnimator.addUpdateListener(updateListener)        valueAnimator.addListener(object : Animator.AnimatorListener        {            override fun onAnimationStart(animation: Animator?)            {            }            override fun onAnimationRepeat(animation: Animator?)            {            }            override fun onAnimationEnd(animation: Animator?)            {                scaleX = Math.max(1f, scaleX)                scaleY = Math.max(1f, scaleY)                setState(STATE_SELECTED)            }            override fun onAnimationCancel(animation: Animator?)            {            }        })    }    private val updateListener = ValueAnimator.AnimatorUpdateListener {        val time = Math.round(it.animatedValue as Float)        when        {            time <= 100.0f ->            {                scaleX = Math.max(0f, 1f - time / 100f)                scaleY = Math.max(0f, 1f - time / 100f)            }            time in 101..200 ->            {                scaleX = Math.min(1f, (time - 100) / 100f)                scaleY = Math.min(1f, (time - 100) / 100f)                setState(STATE_CIRCLE)            }            time in 201..300 ->            {                scaleX = 1f                scaleY = 1f                strokeWithScaleFraction = ((time - 200) / 100f)                setState(STATE_RING)                postInvalidate()            }            else ->            {                statelliteOffsetFraction = ((time - 300) / 100f)                setState(STATE_STATELLITE)                postInvalidate()            }        }    }    private fun setState(newState: Int)    {        if (state != newState)        {            state = newState            postInvalidate()        }    }    override fun onAttachedToWindow()    {        super.onAttachedToWindow()        initAnimator()    }    private fun initClickEvent()    {        setOnClickListener {            if (state == STATE_NORMAL)            {                startAnimate()            } else            {                setState(STATE_NORMAL)            }        }    }    private fun startAnimate()    {        if (valueAnimator.isRunning)        {            return        } else        {            valueAnimator.start()        }    }    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int)    {        val widthSize = MeasureSpec.getSize(widthMeasureSpec)        val heightSize = MeasureSpec.getSize(heightMeasureSpec)        size = Math.min(widthSize, heightSize)        super.onMeasure(widthMeasureSpec, heightMeasureSpec)    }    override fun onDraw(canvas: Canvas?)    {        super.onDraw(canvas)        prepareToDraw()        if (canvas != null)        {            when (state)            {                STATE_NORMAL ->                {                    resetPaintColor()                    drawFinger(canvas)                }                STATE_SELECTED ->                {                    resetPaintColor()                    drawFinger(canvas)                }                STATE_CIRCLE ->                {                    resetPaintColor()                    drawCircle(canvas)                }                STATE_RING ->                {                    resetPaintColor()                    drawRing(canvas)                    drawFinger(canvas)                }                STATE_STATELLITE ->                {                    resetPaintColor()                    drawStatellite(canvas)                }            }        }    }    private fun resetPaintColor()    {        when        {            state == STATE_NORMAL ->            {                paint.colorFilter = null                paint.color = normalColor            }            state != STATE_STATELLITE ->            {                paint.colorFilter = null                paint.color = selectedColor            }            else ->            {                paint.color = selectedColor                if (statelliteOffsetFraction <= 0.5f)                {                    val parameter = 1f                    statellitePaint.colorFilter = ColorMatrixColorFilter(floatArrayOf(1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, parameter, 0f))                } else                {                    val parameter = 1 - statelliteOffsetFraction                    statellitePaint.colorFilter = ColorMatrixColorFilter(floatArrayOf(1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, parameter, 0f))                }            }        }    }    private fun prepareToDraw()    {        path.reset()        centerX = (width / 2).toFloat()        centerY = (height / 2).toFloat()    }    private fun drawFinger(canvas: Canvas)    {        paint.style = Paint.Style.STROKE        paint.strokeWidth = 4f        val fingerWidth = (size / 2).toFloat()        val fingerHeight = (size / 2).toFloat()        val centerX = (width / 2).toFloat() + fingerWidth / 8        val centerY = (height / 2).toFloat() + fingerHeight / 8        path.addRect(centerX - fingerWidth / 2, centerY - fingerHeight / 4, centerX - fingerWidth / 3, centerY + fingerHeight / 4, Path.Direction.CW)        path.moveTo(centerX - fingerWidth / 3 + spaceBetweenHandAndShoulder, centerY - fingerHeight / 4)        path.rLineTo(fingerWidth / 8, 0f)        path.rLineTo(fingerWidth / 8, -fingerHeight / 2)        path.rLineTo(fingerWidth / 6, fingerHeight / 4)        path.rLineTo(-fingerWidth / 8, fingerHeight / 4)        path.rLineTo(fingerWidth / 2 - fingerWidth / 6 - spaceBetweenHandAndShoulder, 0f)        path.rLineTo(-fingerWidth / 8, fingerHeight / 2)        path.lineTo(centerX - fingerWidth / 3 + spaceBetweenHandAndShoulder, centerY + fingerHeight / 4)        path.close()        canvas.drawPath(path, paint)    }    private fun drawCircle(canvas: Canvas)    {        val radius = (size.toFloat()) / 3        paint.style = Paint.Style.FILL        canvas.drawCircle(centerX, centerY, radius, paint)    }    private fun drawStatellite(canvas: Canvas)    {        drawFinger(canvas)        drawSmallStatellites(canvas)    }    private fun drawRing(canvas: Canvas)    {        val radius = (size.toFloat()) / 3        paint.style = Paint.Style.STROKE        paint.strokeWidth = ((1 - strokeWithScaleFraction) * radius)        canvas.drawCircle(centerX, centerY, radius - paint.strokeWidth / 2, paint)    }    private fun drawSmallStatellites(canvas: Canvas)    {        val bigRadius = (size.toFloat()) / 3        val smallRadius = (centerY - bigRadius) / 3        val offset = size / 2 - bigRadius - 2 * smallRadius        canvas.drawCircle(centerX, centerY - bigRadius - offset * statelliteOffsetFraction, smallRadius, statellitePaint)        canvas.drawCircle(centerX + bigRadius + offset * statelliteOffsetFraction, centerY, smallRadius, statellitePaint)        canvas.drawCircle(centerX, centerY + bigRadius + offset * statelliteOffsetFraction, smallRadius, statellitePaint)        canvas.drawCircle(centerX - bigRadius - offset * statelliteOffsetFraction, centerY, smallRadius, statellitePaint)    }}复制代码

这个控件的所有代码就在这里了,重点分一下几个关键点

3.

绘制大拇指 private fun drawFinger(canvas: Canvas) { paint.style = Paint.Style.STROKE paint.strokeWidth = 4f val fingerWidth = (size / 2).toFloat() val fingerHeight = (size / 2).toFloat() val centerX = (width / 2).toFloat() + fingerWidth / 8 val centerY = (height / 2).toFloat() + fingerHeight / 8 path.addRect(centerX - fingerWidth / 2, centerY - fingerHeight / 4, centerX - fingerWidth / 3, centerY + fingerHeight / 4, Path.Direction.CW) path.moveTo(centerX - fingerWidth / 3 + spaceBetweenHandAndShoulder, centerY - fingerHeight / 4) path.rLineTo(fingerWidth / 8, 0f) path.rLineTo(fingerWidth / 8, -fingerHeight / 2) path.rLineTo(fingerWidth / 6, fingerHeight / 4) path.rLineTo(-fingerWidth / 8, fingerHeight / 4) path.rLineTo(fingerWidth / 2 - fingerWidth / 6 - spaceBetweenHandAndShoulder, 0f) path.rLineTo(-fingerWidth / 8, fingerHeight / 2) path.lineTo(centerX - fingerWidth / 3 + spaceBetweenHandAndShoulder, centerY + fingerHeight / 4) path.close() canvas.drawPath(path, paint) } 大拇指的绘制是通过path来实现的,size=Math.min(width,height)大拇指占整个size的1/2,另外还要注意的是,当然还要注意为path设置patheffect private fun initPaint() { paint = Paint(Paint.ANTI_ALIAS_FLAG) paint.color = normalColor paint.style = Paint.Style.STROKE paint.strokeWidth = 4f path = Path() val cornerPathEffect = CornerPathEffect(10f) paint.pathEffect = cornerPathEffect } 否则,你的大拇指将尖锐无比。

#3. 动画开启后,根据时间来区分状态,这里我决定用valueanimator,众所周知,valueanimator是用来做动画的,但事实上他只是对传入的数值进行修改,但是并不会对view起到什么作用,真正起作用的是对valueanimator设置updatelistener,根据改变的数值修改view的属性才起到动画作用的。这正是我们需要的,所以看updatelistener吧 `` private val updateListener = ValueAnimator.AnimatorUpdateListener {

val time = Math.round(it.animatedValue as Float)    when    {复制代码

//收缩阶段 time <= 100.0f -> { scaleX = Math.max(0f, 1f - time / 100f) scaleY = Math.max(0f, 1f - time / 100f) } //放大阶段 time in 101..200 -> { scaleX = Math.min(1f, (time - 100) / 100f) scaleY = Math.min(1f, (time - 100) / 100f) setState(STATE_CIRCLE)

}复制代码

//圆环阶段 time in 201..300 -> { scaleX = 1f scaleY = 1f strokeWithScaleFraction = ((time - 200) / 100f) setState(STATE_RING) postInvalidate() } //卫星阶段 else -> { statelliteOffsetFraction = ((time - 300) / 100f) setState(STATE_STATELLITE) postInvalidate() } } } #4. 卫星的绘制有两点,一是一个距离的改变,二是颜色的逐渐alpha逐渐变0. private fun drawSmallStatellites(canvas: Canvas) { val bigRadius = (size.toFloat()) / 3 val smallRadius = (centerY - bigRadius) / 3 val offset = size / 2 - bigRadius - 2 * smallRadius canvas.drawCircle(centerX, centerY - bigRadius - offset * statelliteOffsetFraction, smallRadius, statellitePaint) canvas.drawCircle(centerX + bigRadius + offset * statelliteOffsetFraction, centerY, smallRadius, statellitePaint) canvas.drawCircle(centerX, centerY + bigRadius + offset * statelliteOffsetFraction, smallRadius, statellitePaint) canvas.drawCircle(centerX - bigRadius - offset * statelliteOffsetFraction, centerY, smallRadius, statellitePaint) } `` 距离的改变是根据statelliteOffsetFraction决定的,statelliteOffsetFraction变量这个是在updatelistener里进行不断改变的。

至于颜色的变化 paint.color = selectedColor if (statelliteOffsetFraction <= 0.5f) { val parameter = 1f statellitePaint.colorFilter = ColorMatrixColorFilter(floatArrayOf(1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, parameter, 0f)) } else { val parameter = 1 - statelliteOffsetFraction statellitePaint.colorFilter = ColorMatrixColorFilter(floatArrayOf(1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, parameter, 0f)) } 这里使用了colormatrix。

#5. 为什么说是七成效果,因为卫星的位置不一样,圆环阶段爆炸后大拇指有一个由大到正常的恢复过程,还有wrap_content以及padding等没有实现,为什么会这样?一个字,懒,懒得做。。。

#结尾 终于写完了,关灯,睡觉。

转载地址:http://alkml.baihongyu.com/

你可能感兴趣的文章
关于OSC项目演示平台maven的一点疑问
查看>>
ntp同步两台机器的时间
查看>>
我的友情链接
查看>>
Office365管理员操作手册-1
查看>>
Mysql学习总结(7)——MySql索引原理与使用大全
查看>>
电脑端下载今日头条的短视频
查看>>
新浪微博数据爬取
查看>>
Tile chart
查看>>
我的友情链接
查看>>
php---编译安装 PHP 的 Redis 扩展
查看>>
python ---- urllib2
查看>>
Windows的定时任务(Schedule Task)设置
查看>>
rndc: connect failed: 127.0.0.1#953: connection refused
查看>>
在论坛中出现的比较难的sql问题:12(递归问题2)
查看>>
PXE结合kiskstart实现自动化安装系统
查看>>
Mysql isam数据库恢复实战
查看>>
mysql LINESTRING ,POINT 类型操作
查看>>
centos7 双网卡双ip内外网设置最小化安装
查看>>
第十次课作业(风险管理、项目收尾、知识产权)
查看>>
如果在ecshop中自定义添加模板
查看>>