栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

Android可以自由拖动的View 实现自动吸边并且可以绕开某一位置

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Android可以自由拖动的View 实现自动吸边并且可以绕开某一位置

自定义的可以随意拖动并自动贴边的View 自定义view的效果 可以自动绕开某一个view

  • 自定义的可以随意拖动并自动贴边的View
      • 自定义view的效果 可以自动绕开某一个view
      • 代码部分
        • 1.自定义View
        • 2.使用教程
      • 注意事项


接下来开始讲解代码

代码部分 1.自定义View
  1. 要自定义一个View ,当然可以继承任何View,也可以把我的代码封装一个基类
    这里我使用的kotlin,如果想看java的话 可以根据逻辑自己写

  2. 变量的定义 请直接查看注释

 	private var mIsDrug = true //判断是否是点击事件
    private var mCustomIsAttach = true //是否需要自动吸附
    private var mCustomIsDrag = true  // 是否可以拖拽

    private var mLastRawX = 0f //最终位置
    private var mLastRawY = 0f //最终位置
    private var mRootWindowMeasuredWidth = 0 //父布局的宽度
    private var mRootWindowMeasuredHeight = 0 //父布局的高度
    private var mRootTopY = 0 //父布局的顶部
    //用来规避的View 位置
    private val rectF: RectF = RectF(0f, 0f, 0f, 0f) //这里直接创建对象 后面进行内部赋值操作
  1. 最关键的一点 我们要阻止事件的分发
	 override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        super.dispatchTouchEvent(event)
        return true
    }
  1. 设置我们所需要的规避View的坐标(矩形坐标),也就是View的上下左右四个边,也可以通过XY算
	fun setSkipViewRectF(viewRectF: RectF) {
        this.rectF.bottom = viewRectF.bottom
        this.rectF.top = viewRectF.top
        this.rectF.left = viewRectF.left
        this.rectF.right = viewRectF.right
    }
  1. 接下来就是整个代码的灵魂部分了 也是喜闻乐见的重写我们的onTouchEvent()
 override fun onTouchEvent(event: MotionEvent?): Boolean {
        if (mCustomIsDrag) {
            val mRawX = event?.rawX
            val mRawY = event?.rawY
            when (event?.action) {
                MotionEvent.ACTION_DOWN -> {
                    mIsDrug = false
                    mLastRawX = mRawX!!
                    mLastRawY = mRawY!!
                    val viewGroup = parent as ViewGroup?
                    if (viewGroup != null) {
                        val location = IntArray(2)
                        viewGroup.getLocationInWindow(location)
                        mRootWindowMeasuredHeight = viewGroup.measuredHeight
                        mRootWindowMeasuredWidth = viewGroup.measuredWidth
                        mRootTopY = location[1]
                    }
                }
                MotionEvent.ACTION_MOVE -> {
                    if (mRawX!! >= 0 && mRawX <= mRootWindowMeasuredWidth && mRawY!! >= mRootTopY && mRawY <= (mRootWindowMeasuredHeight + mRootTopY)) {
                        //手指X轴的滑动距离
                        val changeX = mRawX - mLastRawX;
                        //手指Y轴的滑动距离
                        val changeY = mRawY - mLastRawY;
                        //判断是否为拖动操作
                        if (!mIsDrug) {
                            mIsDrug =
                                sqrt(changeX * changeX + changeY * changeY) >= 2
                        }
                        //获取手指按下的距离与控件本身X轴的距离
                        val ownX = x
                        //获取手指按下的距离与控件本身Y轴的距离
                        val ownY = y
                        //理论中X轴拖动的距离
                        var endX = ownX + changeX
                        //理论中Y轴拖动的距离
                        var endY = ownY + changeY
                        //X轴可以拖动的最大距离
                        val maxX = mRootWindowMeasuredWidth - width.toFloat()
                        //Y轴可以拖动的最大距离
                        val maxY = mRootWindowMeasuredHeight - height.toFloat()
                        //X轴边界限制
                        endX = if (endX < 0f) {
                            0f
                        } else {
                            endX.coerceAtMost(maxX)
                        }
                        //Y轴边界限制
                        endY = if (endY < 0f) {
                            0f
                        } else {
                            endY.coerceAtMost(maxY)
                        }
                        //开始移动
                        x = endX
                        y = endY
                        //记录位置
                        mLastRawX = mRawX
                        mLastRawY = mRawY
                    }
                }
                MotionEvent.ACTION_UP -> {
                    if (mCustomIsAttach) {
                        //判断是否为点击事件
                        if (mIsDrug) {
                            val center = (mRootWindowMeasuredWidth shr 1).toFloat()
                            //自动贴边
                            if (mLastRawX <= center) {
                                mLastRawX = 0f
                                animate()
                                    .setInterpolator(BounceInterpolator())
                                    .setDuration(1500)
                                    .x(mLastRawX)
                                    .start()
                            } else {
                                mLastRawX = (mRootWindowMeasuredWidth - width).toFloat()
                                animate()
                                    .setInterpolator(BounceInterpolator())
                                    .setDuration(1500)
                                    .x(mLastRawX)
                                    .start()
                            }
                            //这里是因为我所做的跳过的view是固定右边的 而且自动吸边 所以判断的是上面的mLastRawX
                            if (mLastRawX != 0f) {
                            //获取View的Y中线
                                val y = y + measuredHeight / 2
                               
                                if (y >= rectF.top && y < rectF.bottom) {
                                    animate()
                                        .setInterpolator(BounceInterpolator())
                                        .setDuration(1500)
                                        .y(rectF.top - height)
                                        .start()
                                }
                            }
                        } else {
                            //这里可以处理点击事件
                        }
                    }

                }
            }
        }
        return if (mIsDrug) {
            mIsDrug
        } else {
            super.onTouchEvent(event)
        }

    }

注释大部分代码都给予了说明,这里可能有人不知道怎么实现的自动贴边,这里其实是用了属性动画,可以直接设置View移动到指定的坐标,而不是移动了多少。

  1. 规避某一个View,自动去它的上方(这里可以在代码里自己算,我这里只写了在右侧的上方)
                            if (mLastRawX != 0f) {
                                val y = y + measuredHeight / 2
                                if (y >= rectF.top && y < rectF.bottom) {
                                    animate()
                                        .setInterpolator(BounceInterpolator())
                                        .setDuration(1500)
                                        .y(rectF.top - height)
                                        .start()
                                }
                            }
2.使用教程
		//设置规避的view坐标 主要 请务必在view完全绘制完成后调用 不然会拿不到坐标 常用方法是使用View.post(R r);
        //这里我用了kotlin-android-extensions 这个就是id可以直接用
        //这里如果看不懂 emm 就是kt的语法糖
        skipView.post {
            moveView.setSkipViewRectF(
                //这里创建矩形对象 然后把四个边传入进去 这里我是直接算的 当然也可以直接获得
                RectF(
                    skipView.x,
                    skipView.y,
                    skipView.x + skipView.measuredWidth,
                    skipView.y + skipView.measuredHeight
                )
            )
        }
注意事项

请务必保证View在一个ViewGroup里面,如果没办法保证,可以运用屏幕坐标来实现。
请务必在View完成绘制并准备好了之后去获取坐标,不然有极大的可能是空的。

参考:_____
代码:GitHub

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/322208.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号