This article shares with you the example of android to implement draggable floating view for your reference. The specific content is as follows
Source of business
After the page is minimized, a floating view needs to appear to inform the user to prevent the view from being blocked. The view needs to be swiped
Known issues
The layout type of dependency is unknown [for subsequent expansion]
The outside world passes ViewGroup itself inherits LinearLayout [or other 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() // Get screen width and height private var mScreenHeight = getScreenHeight() private var mDownEventX: Float = 0f // x relative to the control private var mDownEventY: Float = 0f private var mDownX: Float = 0f // The x relative to the screen 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() // It is recommended to add some transparency orientation = HORIZONTAL gravity = Gravity.CENTER_VERTICAL mBoundaryLeft = () mBoundaryTop = () mBoundaryRight = mScreenWidth - () mBoundaryBottom = (mScreenHeight - mMarginBottom - dip2Px(85)).toFloat() setView() } }
2. Drag the event to affect clicks and event distribution processing.
override fun onTouchEvent(event: MotionEvent?): Boolean { if (mIsStartAnimation) { // The animation is in progress without processing onTouch return true } if (event == null) { return (event) } mIsOnTouch = true //The upper left corner coordinate of the floating area val x = x val y = y //The width and height of the suspended area val width = mViewGroupW val height = mViewGroupH when () { ACTION_DOWN -> { //Click on the position coordinate mDownEventX = mDownEventY = mDownX = x mDownY = y } ACTION_UP -> { mUpTime = () if (mIsMove && abs(mDownX - x) <= 8f && abs(mDownY - y) <= 8f) { mListener?.onClick(this) } mIsMove = false // Handle boundary overflow issues after lifting resilienceAnimation(x, y, x + mViewGroupW, y + mViewGroupH) } ACTION_MOVE -> { val changeX = () - mDownEventX val changeY = () - mDownEventY mIsMove = true if (changeX == 0f && changeY == 0f) { return (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. Drag to the boundary issue.
After dragging it out of the boundary, it rebounded
/** * Boom beyond the boundary * @param left Current x direction position * @param right Current y direction position */ private fun resilienceAnimation(left: Float, top: Float, right: Float, bottom: Float) { var startX = 0f var resilienceX = 0f if (mBoundaryLeft <= left && right <= mBoundaryRight) { // x direction is within range // Not handled } else if (mBoundaryLeft > left) { // left overflow startX = 0f resilienceX = mBoundaryLeft - left } else { // right direction overflows at the bottom startX = 0f resilienceX = mBoundaryRight - right } var startY = 0f var resilienceY = 0f if (mBoundaryTop <= top && bottom <= mBoundaryBottom) { // The y direction is within range // Not handled } else if (mBoundaryTop > top) { // top overflow startY = 0f resilienceY = mBoundaryTop - top } else { // bottom overflow startY = 0f resilienceY = mBoundaryBottom - bottom } if (resilienceX == 0f && resilienceY == 0f) { // No rebound required in range return } // Bounce back beyond the boundary val phaseFirstDuration: Long = 400 var oAnimPhaseFirstTUpX: ObjectAnimator? = null if (resilienceX != 0f) { oAnimPhaseFirstTUpX = (this, "translationX", startX, resilienceX) .setDuration(phaseFirstDuration) } var oAnimPhaseFirstTUpY: ObjectAnimator? = null if (resilienceY != 0f) { oAnimPhaseFirstTUpY = (this, "translationY", startY, resilienceY) .setDuration(phaseFirstDuration) } val animatorSet = AnimatorSet() if (oAnimPhaseFirstTUpX != null && oAnimPhaseFirstTUpY != null) { (oAnimPhaseFirstTUpX).with(oAnimPhaseFirstTUpY) } else if (oAnimPhaseFirstTUpX != null) { (oAnimPhaseFirstTUpX) } else { (oAnimPhaseFirstTUpY) } [ - 1].addListener(object : { 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 -> { // The left side of x overflows l = mBoundaryLeft r = mBoundaryLeft + mViewGroupW } mBoundaryRight < right -> { // The right side of x overflows l = mBoundaryRight - mViewGroupW r = mBoundaryRight } else -> { // The x direction does not overflow } } when { mBoundaryTop > top -> { // y top overflow t = mBoundaryTop b = mBoundaryTop + mViewGroupH } mBoundaryBottom < bottom -> { // y bottom overflow t = mBoundaryBottom - mViewGroupH b = mBoundaryBottom } else -> { // The y direction has not overflowed } } // Only offset is performed, the actual position has not changed, the offset needs to be reset and redrawn this@ = 0f this@ = 0f layout((), (), (), ()) mMarginLeft = () mMarginTop = () mIsStartAnimation = false } override fun onAnimationCancel(animation: Animator?) {} override fun onAnimationRepeat(animation: Animator?) {} }) ()
The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.