本文实例为大家分享了android实现可拖动的浮动view,供大家参考,具体内容如下

业务来源

页面最小化后,需要出现一个浮动 view 告知用户,防止遮挡视线,需要对 view 做可滑动处理

已知会遇到的问题

1.view 的依赖的布局类型未知【为了后续方便扩展】

外界传递 viewgroup 自己本身继承 linearlayout【或者其他 viewgroup 】 

class floatchannelview(var mcontext: context?, var viewgroup: viewgroup) : linearlayout(mcontext){
    private var micon: imageview = imageview(context)
    private var mname: textview = textview(context)
    private var mclose: imageview = imageview(context)
    private var iconwh = dip2px(38)
    private var grouppadding = dip2px(3)
    private var mviewgrouph = dip2px(44)
    private var mviewgroupw = dip2px(152)
    private var mboundaryleft: float
    private var mboundarytop: float
    private var mboundaryright: float
    private var mboundarybottom: float
    private var mscreenwidth = getscreenwidth() // 获取屏幕宽高
    private var mscreenheight = getscreenheight()
 
    private var mdowneventx: float = 0f  // 相对控件的x
    private var mdowneventy: float = 0f
    private var mdownx: float = 0f  // 相对屏幕所在的 x
    private var mdowny: float = 0f
 
    private var mlistener: onclicklistener? = null
    private var misstartanimation: boolean = false
 
    private val mdefaultmargin = dip2px(12)
    private var mmarginleft = mdefaultmargin
    private var mmargintop = mdefaultmargin
    private var mmarginright = mdefaultmargin
    private var mmarginbottom = mdefaultmargin
 
    init {
        layoutparams = layoutparams(mviewgroupw, mviewgrouph)
        setpadding(grouppadding, grouppadding, grouppadding, grouppadding)
        setbackgroundresource(r.drawable.backage) // 建议加一些透明
        orientation = horizontal
        gravity = gravity.center_vertical
        mboundaryleft = mmarginleft.tofloat()
        mboundarytop = mmargintop.tofloat()
        mboundaryright = mscreenwidth - mmarginright.tofloat()
        mboundarybottom = (mscreenheight - mmarginbottom - dip2px(85)).tofloat()
        setview()
    }
}

2.拖动事件影响点击,事件分发处理。

override fun ontouchevent(event: motionevent?): boolean {
        if (misstartanimation) {  // 动画正在进行无需处理 ontouch
            return true
        }
        if (event == null) {
            return super.ontouchevent(event)
        }
        misontouch = true
        //悬浮区域左上角坐标
        val x = x
        val y = y
        //悬浮区域宽高
        val width = mviewgroupw
        val height = mviewgrouph
        when (event.actionmasked) {
            action_down -> {
                //点击位置坐标
                mdowneventx = event.x
                mdowneventy = event.y
                mdownx = x
                mdowny = y
            }
            action_up -> {
                muptime = system.currenttimemillis()
            
                if (mismove && abs(mdownx - x) <= 8f && abs(mdowny - y) <= 8f) {
                    mlistener?.onclick(this)
                }
                mismove = false
                // 抬起后处理边界溢出问题
                resilienceanimation(x, y, x + mviewgroupw, y + mviewgrouph)
            }
            action_move -> {
                val changex = event.x.toint() - mdowneventx
                val changey = event.y.toint() - mdowneventy
                mismove = true
                if (changex == 0f && changey == 0f) {
                    return super.ontouchevent(event)
                }
                val left = (x + changex).toint()
                val top = (y + changey).toint()
                val right = left + mviewgroupw
                val bottom = top + mviewgrouph
                layout(left, top, right, bottom)
            }
        }
        return true
    }

3.拖到边界问题。

拖出边界后做了回弹处理

/**
     *  超出边界回弹
     *  @param left 当前 x 方向位置
     *  @param right 当前 y 方向位置
*/
    private fun resilienceanimation(left: float, top: float, right: float, bottom: float) {
       
        var startx = 0f
        var resiliencex = 0f
        if (mboundaryleft <= left && right <= mboundaryright) {  // x 方向在范围内
            // 不处理
        } else if (mboundaryleft > left) {  // left 溢出
            startx = 0f
            resiliencex = mboundaryleft - left
        } else {   // right 方向底部溢出
            startx = 0f
            resiliencex = mboundaryright - right
        }
        var starty = 0f
        var resiliencey = 0f
        if (mboundarytop <= top && bottom <= mboundarybottom) {  // y 方向在范围内
            // 不处理
        } else if (mboundarytop > top) {  // top 溢出
            starty = 0f
            resiliencey = mboundarytop - top
        } else {  // bottom 溢出
            starty = 0f
            resiliencey = mboundarybottom - bottom
        }
        if (resiliencex == 0f && resiliencey == 0f) {  // 在范围内无需回弹
            return
        }
 
        // 超出边界回弹
        val phasefirstduration: long = 400
        var oanimphasefirsttupx: objectanimator? = null
        if (resiliencex != 0f) {
            oanimphasefirsttupx = objectanimator.offloat(this, "translationx", startx, resiliencex)
                    .setduration(phasefirstduration)
        }
        var oanimphasefirsttupy: objectanimator? = null
        if (resiliencey != 0f) {
            oanimphasefirsttupy = objectanimator.offloat(this, "translationy", starty, resiliencey)
                    .setduration(phasefirstduration)
        }
        val animatorset = animatorset()
        if (oanimphasefirsttupx != null && oanimphasefirsttupy != null) {
            animatorset.play(oanimphasefirsttupx).with(oanimphasefirsttupy)
        } else if (oanimphasefirsttupx != null) {
            animatorset.play(oanimphasefirsttupx)
        } else {
            animatorset.play(oanimphasefirsttupy)
        }
        animatorset.childanimations[animatorset.childanimations.size - 1].addlistener(object : animator.animatorlistener {
 
            override fun onanimationstart(animation: animator?) {
                misstartanimation = true
            }
 
            override fun onanimationend(animation: animator?) {
                var l = left
                var t = top
                var r = right
                var b = bottom
                when {
                    mboundaryleft > left -> {  // x左边溢出
                        l = mboundaryleft
                        r = mboundaryleft + mviewgroupw
                    }
                    mboundaryright < right -> {  // x右边溢出
                        l = mboundaryright - mviewgroupw
                        r = mboundaryright
                    }
                    else -> {  // x方向未溢出
                    }
                }
 
                when {
                    mboundarytop > top -> {  // y 顶部溢出
                        t = mboundarytop
                        b = mboundarytop + mviewgrouph
                    }
                    mboundarybottom < bottom -> {  // y 底部溢出
                        t = mboundarybottom - mviewgrouph
                        b = mboundarybottom
                    }
                    else -> {  // y方向未溢出
                    }
                }
                // 只进行偏移,实际位置未变化,需要重置偏移量,并重绘
                this@floatchannelview.translationx = 0f
                this@floatchannelview.translationy = 0f
                layout(l.toint(), t.toint(), r.toint(), b.toint())
                mmarginleft = l.toint()
                mmargintop = t.toint()
                misstartanimation = false
            }
 
            override fun onanimationcancel(animation: animator?) {}
 
            override fun onanimationrepeat(animation: animator?) {}
 
        })
        animatorset.start()

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。