美文网首页
记录贴1:可拖动,可吸附侧面的悬浮控件

记录贴1:可拖动,可吸附侧面的悬浮控件

作者: 蔡小树 | 来源:发表于2022-04-25 17:42 被阅读0次
    主要功能项:
    1. 可以拖动
    2. 拖动完可以自动贴边
    3. 可处理点击事件 
    4. 可变化背景图(美术改图,弃之不用啊,可惜可惜)
       4.1 吸附左边的时候,背景为左边直角右边圆角的矩形
       4.2 吸附右边的时候,背景为右边直角左边圆角的矩形
       4.3 移动的时候,背景为圆角的矩形
    

    第一步.上代码

    class FloatView(context: Context, attributeSet: AttributeSet) :
        RelativeLayout(context, attributeSet) {
    
        private val MORE_SPACE = 10.0 //移动距离像素,超过才算移动
        private var mView: View? = null
    
        init {
            mView = LayoutInflater.from(context).inflate(R.layout.layout_float_view, this, true)
    
            val rotation = ObjectAnimator.ofFloat(mView?.iv_playing_bg, "rotation", 0f, 360f)
            rotation.duration = 15000
            rotation.repeatCount = -1
            rotation.start()
            val scaleX = ObjectAnimator.ofFloat(mView?.iv_playing_bg, "scaleX", 0.5f, 1f, 0.5f)
            scaleX.duration = 15000
            scaleX.repeatCount = -1
            scaleX.start()
            val scaleY = ObjectAnimator.ofFloat(mView?.iv_playing_bg, "scaleY", 0.5f, 1f, 0.5f)
            scaleY.duration = 15000
            scaleY.repeatCount = -1
            scaleY.start()
        }
        
        //控件的宽高
        private var mWidth = 0
        private var mHeight = 0
        override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
            super.onSizeChanged(w, h, oldw, oldh)
            mWidth = w
            mHeight = h
        }
    
        //屏幕宽高以及半屏和上下边距
        private var mScreenWith = 0
        private var mScreenHeight = 0
        private var halfWith = 0
        private var mTop = 0
        private var mBottom = 0
    
        /**
         * 设置父布局的大小
         */
        fun setParentSize(width: Int, height: Int, top: Int, bottom: Int) {
            mScreenWith = width
            mScreenHeight = height
            halfWith = width / 2
    
            mTop = top
            mBottom = bottom
        }
    
        private var lastX = 0f //按下x
        private var lastY = 0f //按下y
        private var isMove = false //是拖动?
        private var isPress = false //是点击?
        private var offsetX = 0f  //偏移量x,移动的时候不加偏移量,按钮会发生位置错误,手指下会是按钮的左上角,而不是最初按下位置
        private var offsetY = 0f //偏移量y
        private var isChangeBg = false //吸边的时候可以更改变化按钮的左右两边圆角,后面美术改了个方图,不用了,但思路留在这
    
        override fun onTouchEvent(ev: MotionEvent): Boolean {
            val x = ev.rawX
            val y = ev.rawY
            when (ev.action) {
                MotionEvent.ACTION_DOWN -> {
                    isMove = false
                    isPress = true
                    lastX = x
                    lastY = y
                    offsetX = getX() - x
                    offsetY = getY() - y
                }
                MotionEvent.ACTION_MOVE -> {
                    isMove = isMove(ev)
                    isPress = if (isMove) {
                        if (!isChangeBg) {
                            isChangeBg = true
                            //思路:这里换背景图
                        }
                        val xandY = getXandY(x + offsetX, y + offsetY)
                        setX(xandY[0].toFloat())
                        setY(xandY[1].toFloat())
                        false
                    } else {
                        true
                    }
                }
                MotionEvent.ACTION_UP -> {
                    isChangeBg = false
                    if (isPress) {
                        callback?.click()
                        isPress = true
                    }
                    if (isMove) {
                        isMove = false
                    }
                    if (x != 0f || x != (mScreenWith - mWidth).toFloat()) {
                        if (x <= halfWith) {
                            //在左半边,吸附左边
                            if (x >lastX) {
                                animate().setInterpolator(DecelerateInterpolator())
                                    .setDuration(500)
                                    .xBy(-x - offsetX)
                                    .start()
                                    //思路:这里换背景图
                            }
                        } else {
                            //在右半边,吸附右边
                            if (x < laxtX) {
                                animate().setInterpolator(DecelerateInterpolator())
                                    .setDuration(500)
                                    .xBy(mScreenWith - mWidth - x - offsetX)
                                    .start()
                                   //思路:这里换背景图
                            }
                        }
                    }
                }
            }
            return true
        }
    
        /**
         * 获取移动到的x和y
         */
        private fun getXandY(x: Float, y: Float): IntArray {
            val resultX = when {
                x < 0 -> {
                    0
                     //思路:这里换背景图
                }
                x > mScreenWith - mWidth -> {
                    mScreenWith - mWidth
                     //思路:这里换背景图
                }
                else -> {
                    x.toInt()
                }
            }
            val resultY: Int = when {
                y < mTop -> {
                    mTop
                }
                y > mScreenHeight - mBottom - mWidth -> {
                    mScreenHeight - mBottom - mWidth
                }
                else -> {
                    y.toInt()
                }
            }
            return intArrayOf(resultX, resultY)
        }
    
    
        private var callback: OnClickCallback? = null
    
        fun setOnClickCallback(callback: OnClickCallback) {
            this.callback = callback
        }
    
        interface OnClickCallback {
            fun click()
        }
    
        private fun isMove(ev: MotionEvent): Boolean {
            val x = ev.rawX
            val y = ev.rawY
            val moreSpace =
                Math.sqrt(((x - lastX) * (x - lastX) + (y - lastY) * (y - lastY)).toDouble())
            return moreSpace > MORE_SPACE
        }
    }
    

    第二步.上布局

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:layout_width="@dimen/dp_60"
        android:layout_height="@dimen/dp_60">
    
        <ImageView
            android:id="@+id/iv_playing_bg"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@mipmap/bg_playing" />
    
        <ImageView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:src="@mipmap/playing"
            android:layout_margin="@dimen/dp_5" />
    
    </RelativeLayout>
    

    第三步.使用

    //设置可移动范围,正常是要贴边手机两侧,所以仅需要特别标注上下边距,毕竟不能滑动到状态栏和底部tab上去
    floatView.setParentSize(手机宽,手机高,上边距,下边距)
    //点击事件嘛,处理点击后的业务
    floatView.setOnClickCallback(...)
    

    相关文章

      网友评论

          本文标题:记录贴1:可拖动,可吸附侧面的悬浮控件

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