自定义View实现拖动并自动吸边效果以及手势


import android.content.Context
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.view.animation.BounceInterpolator

/**
 * 自定义View实现拖动并自动吸边效果
 *
 *
 * 处理滑动和贴边
 * 处理事件分发
 * * @author: bill
 *  * ls9421@vip.qq.com
 *  * @since: 2023年08月24日18:01:49
 *
 * @attr customIsAttach  //是否需要自动吸边
 * @attr customIsDrag    //是否可拖曳
 */

class AttachButton @JvmOverloads constructor(
    context: Context,
    attrs: AttributeSet? = null,
    defStyleAttr: Int = 0
) :
    View(context, attrs, defStyleAttr) {
    private var mLastRawX = 0f
    private var mLastRawY = 0f
    private val TAG = "AttachButton"
    private var isDrug = false
    private var mRootMeasuredWidth = 0
    private var mRootMeasuredHeight = 0
    private var mRootTopY = 0
    private var customIsAttach = false
    private var customIsDrag = false

    init {
        isClickable = true
        initAttrs(context, attrs)
    }

    /**
     * 初始化自定义属性
     */
    private fun initAttrs(context: Context, attrs: AttributeSet?) {
        val mTypedAttay = context.obtainStyledAttributes(attrs, R.styleable.AttachButton)
        customIsAttach = mTypedAttay.getBoolean(R.styleable.AttachButton_customIsAttach, true)
        customIsDrag = mTypedAttay.getBoolean(R.styleable.AttachButton_customIsDrag, true)
        mTypedAttay.recycle()
    }

    override fun dispatchTouchEvent(event: MotionEvent): Boolean {
        super.dispatchTouchEvent(event)
        return true
    }

    override fun onTouchEvent(ev: MotionEvent): Boolean {
        //判断是否需要滑动
        if (customIsDrag) {
            //当前手指的坐标
            val mRawX = ev.rawX
            val mRawY = ev.rawY
            when (ev.action) {
                MotionEvent.ACTION_DOWN -> {
                    isDrug = false
                    //记录按下的位置
                    mLastRawX = mRawX
                    mLastRawY = mRawY
                    val mViewGroup = parent as ViewGroup
                    if (mViewGroup != null) {
                        val location = IntArray(2)
                        mViewGroup.getLocationInWindow(location)
                        //获取父布局的高度
                        mRootMeasuredHeight = mViewGroup.measuredHeight
                        mRootMeasuredWidth = mViewGroup.measuredWidth
                        //获取父布局顶点的坐标
                        mRootTopY = location[1]
                    }
                }

                MotionEvent.ACTION_MOVE -> if (mRawX >= 0 && mRawX <= mRootMeasuredWidth && mRawY >= mRootTopY && mRawY <= mRootMeasuredHeight + mRootTopY) {
                    //手指X轴滑动距离
                    val differenceValueX = mRawX - mLastRawX
                    //手指Y轴滑动距离
                    val differenceValueY = mRawY - mLastRawY
                    //判断是否为拖动操作
                    if (!isDrug) {
                        isDrug =
                            if (Math.sqrt((differenceValueX * differenceValueX + differenceValueY * differenceValueY).toDouble()) < 2) {
                                false
                            } else {
                                true
                            }
                    }
                    //获取手指按下的距离与控件本身X轴的距离
                    val ownX = x
                    //获取手指按下的距离与控件本身Y轴的距离
                    val ownY = y
                    //理论中X轴拖动的距离
                    var endX = ownX + differenceValueX
                    //理论中Y轴拖动的距离
                    var endY = ownY + differenceValueY
                    //X轴可以拖动的最大距离
                    val maxX = (mRootMeasuredWidth - width).toFloat()
                    //Y轴可以拖动的最大距离
                    val maxY = (mRootMeasuredHeight - height).toFloat()
                    //X轴边界限制
                    endX = if (endX < 0) {
                        0f
                    } else if (endX > maxX) {
                        maxX
                    } else {
                        endX
                    }
                    //Y轴边界限制
                    endY = if (endY < 0) {
                        0f
                    } else if (endY > maxY) {
                        maxY
                    } else {
                        endY
                    }
                    //开始移动
                    x = endX
                    y = endY
                    //记录位置
                    mLastRawX = mRawX
                    mLastRawY = mRawY
                }

                MotionEvent.ACTION_UP ->                     //根据自定义属性判断是否需要贴边
                    if (customIsAttach) {
                        //判断是否为点击事件
                        if (isDrug) {
                            val center = (mRootMeasuredWidth / 2).toFloat()
                            //自动贴边
                            if (mLastRawX <= center) {
                                //向左贴边
                                animate()
                                    .setInterpolator(BounceInterpolator())
                                    .setDuration(500)
                                    .x(0f)
                                    .start()
                            } else {
                                //向右贴边
                                animate()
                                    .setInterpolator(BounceInterpolator())
                                    .setDuration(500)
                                    .x((mRootMeasuredWidth - width).toFloat())
                                    .start()
                            }
                        }
                    }

                else -> {}
            }
        }
        //是否拦截事件
        return if (isDrug) isDrug else super.onTouchEvent(ev)
    }
}


手势处理的view


import android.content.Context
import android.content.res.TypedArray
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.ViewGroup
import android.widget.LinearLayout

/**
 * @author: bill
 * ls9421@vip.qq.com
 * @since: 2023年08月24日18:01:49
 */
class GestureView : LinearLayout {
    private var canRotation = false
    private var canScale = false

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        val typedArray: TypedArray = context.obtainStyledAttributes(attrs, R.styleable.GestureView)
        canRotation = typedArray.getBoolean(R.styleable.GestureView_canRotation, false)
        canScale = typedArray.getBoolean(R.styleable.GestureView_canScale, false)
        typedArray.recycle()
    }


    val gesture = BestGestureDetector(this).apply {

        setOnTouchListener(object : OnSimpleTouchListener() {
            override fun onTouchMove(detector: BestGestureDetector): Boolean {
                val parentwidth = (parent as ViewGroup).width
                val parentHeight = (parent as ViewGroup).height
                when {
                    left + detector.moveX < 0 -> {
                        val offset = left
                        left -= offset
                        right -= offset
                    }

                    right + detector.moveX > parentwidth -> {
                        val offset = parentwidth - right
                        left += offset
                        right += offset
                    }

                    else -> {
                        offsetLeftAndRight(detector.moveX.toInt())
                    }
                }

                when {
                    top + detector.moveY < 0 -> {
                        val offset = top
                        top -= offset
                        bottom -= offset
                    }

                    bottom + detector.moveY > parentHeight -> {
                        val offset = parentHeight - bottom
                        top += offset
                        bottom += offset
                    }

                    else -> {
                        offsetTopAndBottom(detector.moveY.toInt())
                    }
                }


                return super.onTouchMove(detector)
            }
        })
        setMoveListener(object : OnSimpleMoveListener() {
            override fun onMove(detector: BestGestureDetector): Boolean {
                val parentwidth = (parent as ViewGroup).width
                val parentHeight = (parent as ViewGroup).height
                when {
                    left + detector.moveX < 0 -> {
                        val offset = left
                        left -= offset
                        right -= offset
                    }

                    right + detector.moveX > parentwidth -> {
                        val offset = parentwidth - right
                        left += offset
                        right += offset
                    }

                    else -> {
                        offsetLeftAndRight(detector.moveX.toInt())
                    }
                }

                when {
                    top + detector.moveY < 0 -> {
                        val offset = top
                        top -= offset
                        bottom -= offset
                    }

                    bottom + detector.moveY > parentHeight -> {
                        val offset = parentHeight - bottom
                        top += offset
                        bottom += offset
                    }

                    else -> {
                        offsetTopAndBottom(detector.moveY.toInt())
                    }
                }

                return super.onMove(detector)
            }
        })
        setRotationListener(object : OnSimpleRotateListener() {
            override fun onRotate(detector: BestGestureDetector): Boolean {
                if (canRotation) {
                    this@GestureView.rotation += detector.rotation
                }
                return super.onRotate(detector)
            }
        })
        setScaleListener(object : OnSimpleScaleListener() {
            override fun onScale(detector: BestGestureDetector): Boolean {
                if (canScale) {
                    scaleX *= detector.scaleFactor
                    scaleY *= detector.scaleFactor
                }
                return super.onScale(detector)
            }
        })
    }

    override fun onTouchEvent(event: MotionEvent): Boolean {
        return gesture.onTouchEvent(event)
    }
}
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
    <declare-styleable name="GestureView">
        <attr name="canRotation" format="boolean"/>
        <attr name="canScale" format="boolean"/>/>
    </declare-styleable>

    <declare-styleable name="AttachButton">
        <!--是否需要自动吸边-->
        <attr name="customIsAttach" format="boolean" />
        <!--是否可拖曳-->
        <attr name="customIsDrag" format="boolean" />
    </declare-styleable>

</resources>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值