Android--›模仿QQ7.0底部导航效果(kotlin版)

来一波效果图

qq_nav_gif3.gif

有没有一种蠢蠢欲动的想法? 反正我已经动了.

分析一波

1:选中状态, 和未选中状态显示的图片不一样. 这个简单:一个Boolean成员变量控制.
2:当手指360°滑动的时候,图片会跟随移动.这个就是核心了, 需要计算手指距离图片中心的角度, 然后计算出偏移的dx,dy值.
3:细心的你, 可能已经发现了, 笑脸其实并不是相对滚动的, 是有滚动差的. 这个可以在步骤2计算的dx,dy中做一些放大操作就行.
4:当第一次,未选中状态点击的时候, 有一个缩放效果. 这个简单,就用一个动画可以的;

注意:以下代码使用kotlin编写,如果你还不会, 点击传送门去学习吧!


只针对上述步骤2做详解, 其他的太简单了:

override fun onTouchEvent(event: MotionEvent): Boolean {
        super.onTouchEvent(event) //这里调用super的目的是,为了支持一些默认效果. 比如:onClickListener, 如果你没有调用super,那么onClickListener是不会回调的哦. 还有就是:如果你使用了background属性,那么background属性的selector也是不会有效果的哦.

        val eventX = event.x
        val eventY = event.y

        when (MotionEventCompat.getActionMasked(event)) {
            ACTION_DOWN -> {
                scaleAnimation.cancel()
                if (!mSelected) {
                    scaleAnimation.start()//当第一次未选中的时候点击, 播放缩放动画. 为了极致的模仿QQ效果而存在.
                }
            }
            ACTION_MOVE -> {
                downX = eventX
                downY = eventY

                //计算出, 当前手指距离图片中心的距离.
                val dx = downX - imageCenterX
                val dy = downY - imageCenterY

                var atan = Math.atan((dy / dx).toDouble()) //拿到角度, 此函数返回的角度是弧度制的.
                val degrees = Math.toDegrees(atan) //这个方法可以把弧度制的角度, 转换成角度制.如果30°之类的.
                if (lastDegrees != null && Math.abs(lastDegrees!! - degrees) < 5) {
                    //如果手指移动时的夹角小于5°,不处理...防止图片抖动.
                } else {
                    lastDegrees = degrees

                    //这是一个很关键的点, 因为理想状态下, 我们想要的角度范围应该是0-360°
                    //而你手指转一圈, 函数返回结果却是2个-90°-90°的取值范围.
                    //所以...数学当中的四象限你还记得么?
                    if (dx < 0) {
                        atan -= Math.PI  //注意 atan是弧度制的角度哦, 因为cos函数,sin函数,都要求是弧度制的参数.
                    }
                  
                    //有了角度之后, 就可以计算出三角形的邻边和对边了. 这2个值, 就是图片需要偏移中心点的距离.
                    mDrawOffsetX = (mMaxMoveOffset * Math.cos(atan)).toFloat()
                    mDrawOffsetY = (mMaxMoveOffset * Math.sin(atan)).toFloat()

                    //为了产生视差效果, 笑脸的偏移大小更大一点就行了.
                    mSubDrawOffsetX = (mSubMaxMoveOffset * Math.cos(atan)).toFloat()
                    mSubDrawOffsetY = (mSubMaxMoveOffset * Math.sin(atan)).toFloat()

                    //重绘....这个方法你不懂,就可以离职了.
                    postInvalidate()
                }
            }
            ACTION_UP -> {
                //恢复默认值
                onTouchUp()
            }
            ACTION_CANCEL -> {
                onTouchUp()
            }
        }
        return true
    }

    private fun onTouchUp() {
        downX = 0f
        downY = 0f
        mDrawOffsetX = 0f
        mDrawOffsetY = 0f
        mSubDrawOffsetX = 0f
        mSubDrawOffsetY = 0f

        lastDegrees = null

        postInvalidate()
    }
    override fun onDraw(canvas: Canvas) {
        canvas.save()

        //绘制图片
        if (drawDrawable != null) {
            //下面2行代码, 是为了缩放做准备的, 首先移动到图片的中心, 然后再进行缩放处理.
            canvas.translate((measuredWidth / 2).toFloat(), measuredHeight / 2 - subHeight / 2 + imageHeight / 2)
            canvas.scale(animScale, animScale)

            canvas.save()
            canvas.translate(-imageWidth / 2 + mDrawOffsetX,
                    -imageHeight / 2 + mDrawOffsetY)
            drawDrawable!!.draw(canvas)
            canvas.restore()

          //笑脸的绘制.
            if (subDrawDrawable != null) {
                canvas.save()
                canvas.translate(-imageWidth / 2 + mSubDrawOffsetX,
                        -imageHeight / 2 + mSubDrawOffsetY)
                subDrawDrawable!!.draw(canvas)
                canvas.restore()
            }
        }
        canvas.restore()
    }

源码地址

联系作者

请使用QQ扫码加群, 小伙伴们在等着你哦!

关注我的公众号, 每天都能一起玩耍哦!

  • 8
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值