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…