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

主要功能项:
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(...)
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 194,457评论 5 459
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 81,837评论 2 371
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 141,696评论 0 319
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 52,183评论 1 263
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 61,057评论 4 355
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 46,105评论 1 272
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 36,520评论 3 381
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 35,211评论 0 253
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 39,482评论 1 290
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 34,574评论 2 309
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 36,353评论 1 326
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 32,213评论 3 312
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 37,576评论 3 298
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 28,897评论 0 17
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,174评论 1 250
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 41,489评论 2 341
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 40,683评论 2 335

推荐阅读更多精彩内容