美文网首页
Android-->模仿QQ7.0底部导航效果

Android-->模仿QQ7.0底部导航效果

作者: angcyo | 来源:发表于2017-06-06 13:35 被阅读107次
    来一波效果图
    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扫码加群, 小伙伴们在等着你哦!

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

    相关文章

      网友评论

          本文标题:Android-->模仿QQ7.0底部导航效果

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