Android Activity通用悬浮可拖拽View封装的思路详解
作者:熊熊君fly 时间:2023-08-08 15:31:48
1,背景
在开发中总会遇到一个可拖拽的悬浮View,不管是在开发中,还是在线上,都时长有这样的控件,我们通常遇到这种情况,经常需要自己封装,需要耗费时间,我这边封装了一个可以通用的悬浮可拖拽View,这样使用的时候,只需要传入自己要设计的样式和位置既可
2,思路
2.1,封装通用的基础悬浮View
设计通用的父View
1,传入的childView是可以自定义layout,可以传入任何样式
childView = setChildView()
2,可以设置初始的位置
layoutParams = setChildInitLayoutParams()
3,可以修改自定义view控件
setChildAction(childView)
4,提供点击事件处理
protected abstract fun setEventClick()
子view继承父view就可以实现自己想要的功能了
abstract class AbsParentDragView : FrameLayout, View.OnTouchListener {
//需要添加的子控件
private var childView: View? = null
//子控件的宽
protected var childWidth: Int = 0
//子控件的高
protected var childHeight: Int = 0
//子控件的位置属性
lateinit var layoutParams: LayoutParams
//点击区域偏移量
private var regionW: Int = 0
//判断是否可以移动
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 fun initLayoutParams() {
layoutParams.gravity = Gravity.LEFT or Gravity.TOP
childView?.layoutParams = layoutParams
}
private fun updateLayoutParams(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
}
private var 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
}
}
2.1,继承通用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 = "设置悬浮"
}
override fun setEventClick() {
Toast.makeText(context,"悬浮view",Toast.LENGTH_LONG).show()
}
}
2.3,设计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
}
}
2.4,view的添加和使用
//创建出要显示的View
DragViewManager.getInstance(this).create(new DemoLineView(this,null));
//隐藏要显示的View
DragViewManager.getInstance(this).dismiss();
//显示要显示的View
DragViewManager.getInstance(this).show();
代码链接地址:gitee.com/component_i…
来源:https://juejin.cn/post/7039300295775485965
标签:Android,Activity,拖拽,View
![](/images/zang.png)
![](/images/jiucuo.png)
猜你喜欢
Android UI设计系列之自定义Dialog实现各种风格的对话框效果(7)
2023-11-18 14:26:37
![](https://img.aspxhome.com/file/2023/1/138391_0s.jpg)
C#泛型方法在lua中表示的一种设计详解
2022-08-24 20:03:12
Spring Data JPA查询方式及方法名查询规则介绍
2022-07-13 19:31:20
![](https://img.aspxhome.com/file/2023/4/81294_0s.png)
Android Studio ADB网络调试汇总
2023-07-13 22:10:07
![](https://img.aspxhome.com/file/2023/4/87324_0s.jpg)
android点击无效验证的解决方法
2022-02-28 03:55:08
![](https://img.aspxhome.com/file/2023/2/109212_0s.png)
C#使用GZipStream解压缩数据文件的方法
2022-09-22 15:40:34
一篇文章告诉你JAVA Mybatis框架的核心原理到底有多重要
2023-11-13 06:20:10
![](https://img.aspxhome.com/file/2023/5/59705_0s.png)
java基于UDP实现在线聊天功能
2021-06-08 00:01:44
![](https://img.aspxhome.com/file/2023/3/89603_0s.jpg)
Android开发返回键明暗点击效果的实例代码
2022-06-08 06:39:27
![](https://img.aspxhome.com/file/2023/2/137802_0s.png)
c# 配置文件App.config操作类库的方法
2023-01-19 10:34:37
浅谈MyBatis3 DynamicSql风格语法使用指南
2023-11-25 13:05:06
springBoot Junit测试用例出现@Autowired不生效的解决
2023-01-24 12:57:59
![](https://img.aspxhome.com/file/2023/0/73120_0s.png)
Java线程的五种状态介绍
2023-06-16 01:22:15
![](https://img.aspxhome.com/file/2023/5/80275_0s.png)
Java(基于Struts2) 分页实现代码
2023-11-04 05:58:58
Java实战之基于swing的QQ邮件收发功能实现
2023-11-15 01:34:26
Java获取e.printStackTrace()打印的信息方式
2022-05-18 05:19:26
Spring Boot集成ElasticSearch实现搜索引擎的示例
2021-06-02 05:06:16
![](https://img.aspxhome.com/file/2023/3/63793_0s.png)
Java中的break和continue关键字的使用方法总结
2022-07-13 11:50:46
Java日常练习题,每天进步一点点(33)
2023-09-22 05:32:41
JVM中的flag设置详解
2022-08-11 01:37:20
![](https://img.aspxhome.com/file/2023/7/108207_0s.jpg)