1, the background,

In development, there will always be a draggable floating View. Whether in development or online, there will always be such a control. We often encounter this situation, often need to package themselves, it takes time, I packaged a general floating draggable View, so when using it, Just pass in the style and location you want to design

2, the train of thought

2.1, encapsulate the general base suspension View

Design a generic parent View

1. The childView is a custom layout that can be passed in any style

childView = setChildView()
Copy the code

2. You can set the initial position

layoutParams = setChildInitLayoutParams()
Copy the code

3, you can modify the custom view control

setChildAction(childView)
Copy the code

4. Provide click event handling

protected abstract fun setEventClick()
Copy the code

The child view inherits the parent view and can implement its desired functions


abstract class AbsParentDragView : FrameLayout.View.OnTouchListener {

    // A child control to add
    private var childView: View? = null
    // The width of the child control
    protected var childWidth: Int = 0
    // The height of the child control
    protected var childHeight: Int = 0
    // The location property of the child control
    lateinit var layoutParams: LayoutParams
    // Click the region offset
    private var regionW: Int = 0
    // Check whether it can be moved
    private var isCanMove: Boolean = false

    private val MIN_TAP_TIME = 1000

    private val MIN_DISTANCE_MOVE = 4

    private var mState = TouchState.STATE_STOP

    private var distance: Int = 0

    private enum class TouchState {
        STATE_MOVE, STATE_STOP
    }

    constructor(context: Context) : super(context) {
        initView()
    }

    constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
        initView()
    }

    constructor(context: Context, attrs: AttributeSet? , defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
        initView()
    }


    private fun initView() {

        childView = setChildView()

        setChildAction(childView)
        addView(childView)
        setOnTouchListener(this) layoutParams = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT) regionW = DensityUtil.dip2px(context, 3f) distance = DensityUtil.dip2px(context,1f) * MIN_DISTANCE_MOVE post { childView? .width? .let { childWidth = it } childView? .height? .let { childHeight = it } layoutParams = setChildInitLayoutParams() initLayoutParams() } } protected abstract fun setChildView(): View? protected abstract fun setChildInitLayoutParams(): FrameLayout.LayoutParams protected abstract fun setChildAction(childView: View?) private funinitLayoutParams(){ layoutParams.gravity = Gravity.LEFT or Gravity.TOP childView? .layoutParams = layoutParams } private funupdateLayoutParams(dx: Int, dy: Int) {
        layoutParams.gravity = Gravity.LEFT or Gravity.TOP
        layoutParams.leftMargin = dx - childWidth / 2
        layoutParams.topMargin = dy - childHeight / 2- StateUtils.getStatusBarHeight(context) childView? .layoutParams = layoutParams } privatevar mStartX:Int = 0
    private var mStartY:Int = 0
    override fun onTouch(view: View, event: MotionEvent): Boolean {
        val x = event.rawX.toInt()
        val y = event.rawY.toInt()
        when(event.action){
            MotionEvent.ACTION_DOWN -> {
                mStartX = x
                mStartY = y
                if (isPointChoice(x, y)) {
                    isCanMove = true
                }
            }
            MotionEvent.ACTION_MOVE -> {
                if (Math.abs(x - mStartX) < distance
                    && Math.abs(y - mStartY) < distance) {
                    if (mState == TouchState.STATE_STOP) {
                        return true
                        //break}}else if(mState ! = TouchState.STATE_MOVE) { mState = TouchState.STATE_MOVE }if(isCanMove){
                    updateLayoutParams(x, y)
                }

                mState = TouchState.STATE_MOVE
            }
            MotionEvent.ACTION_UP -> {
                isCanMove = false

                if(mState ! = TouchState.STATE_MOVE && event.eventTime - event.downTime < MIN_TAP_TIME) { setEventClick() } mState = TouchState.STATE_STOP } }return isCanMove
    }

    protected abstract fun setEventClick()

    private fun isPointChoice(x: Int, y: Int): Boolean {
        val cLocation = IntArray(2) childView? .getLocationOnScreen(cLocation) val horizontalMatch = x > (cLocation[0] + regionW) && x < (cLocation[0] + childWidth + regionW)
        val verticalMatch =
            y < (cLocation[1] + childHeight + DensityUtil.dip2px(context,10f)) && y > (cLocation[1] - regionW)
       
        if (horizontalMatch && verticalMatch) {
            return true
        }

        return false}}Copy the code

2.1, inherit generic View


class DemoLineView(context: Context.attrs: AttributeSet?). :AbsParentDragView(context.attrs) {


    override fun setChildView(): View? {
        return LayoutInflater.from(context).inflate(R.layout.layout_draw_item, this.false)
    }


    override fun setChildInitLayoutParams(): LayoutParams {
        layoutParams.topMargin = DensityUtil.getScreenHeight(
            context
        ) - childHeight - DensityUtil.dip2px(context, 80f)
        layoutParams.leftMargin = DensityUtil.getScreenWidth(
            context
        ) - childWidth - DensityUtil.dip2px(context, 20f)
        return layoutParams
    }

    override fun setChildAction(childView: View?){ val tvSafeLine = childView? .findViewById<TextView>(R.id.tvSafeLine) tvSafeLine? .text ="Set levitation"
    }

    override fun setEventClick() {
        Toast.makeText(context,"Suspended the view",Toast.LENGTH_LONG).show()

    }

}
Copy the code

2.3. Design the controller of view


open class DragViewManager private constructor(context: Activity) {


    private var activity: Activity = context

    companion object : SingletonHolder<DragViewManager, Activity>(::DragViewManager)

    private lateinit var dragView: AbsParentDragView

    private val contentView = activity.window.decorView.findViewById<View>(android.R.id.content) as FrameLayout

    fun create(dragView: AbsParentDragView){
        this.dragView = dragView
        if(contentView.contains(dragView)){
            contentView.removeView(dragView)
        }
        contentView.addView(dragView)
    }

    fun show(){
        dragView.visibility = View.VISIBLE
    }

    fun dismiss(){
        dragView.visibility = View.INVISIBLE
    }
}
Copy the code

2.4. View addition and use


// Create the View to display
DragViewManager.getInstance(this).create(new DemoLineView(this.null));

// Hide the View to display

DragViewManager.getInstance(this).dismiss();

// Display the View to display

DragViewManager.getInstance(this).show();

Copy the code

Code link address: gitee.com/component_i…