Android Activity通用悬浮可拖拽View封装的思路详解
作者:熊熊君fly 发布时间:2023-08-08 15:31:48
标签:Android,Activity,拖拽,View
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
0
投稿
猜你喜欢
- 作者: juky_huang 事件的简单解释: 事件是对象发送的消息,以发信号通知操作的发生。操作可能是由用户交互(例如
- 线程池模型一般的池化模型会有两个方法,用于获取资源和释放资源,就像这样:public interface XXPool{ &n
- 前言在上网的时候我们常常遇到文件上传的情况,例如上传头像、上传资料等;当然除了上传,遇见下载的情况也很多,接下来看看我们 servlet 中
- 首先我们知道:JVM发生内存错误的类型1、堆内存泄漏:OutOfMemory:Java heap space此种内存泄漏,增加内存,只能暂时
- 前言前面小空带同学们学了EditText控件,又用其实践做了个验证码功能,以为这就完了吗?然而并没有。Android在5.0以后引入了Mat
- 在java项目开发中。最开始换行符大家一般是在idea中设置新文件为LF,并且对旧文件通过IDEA下方的点击来更换换行符。很显然,对于几千文
- 前言最近在学习spring,抽空会将学习的知识总结下面,本文我们会接触spring 4的新功能:@Conditional注解。在之前的spr
- 本文主要介绍了面向对象的三大特征实例解析,下面看看具体内容。封装封装一个Teacher和Student类package com.hz.tes
- Android 实现获取手机里面的所有图片详解及实例实现代码:public class MainActivity extends Activ
- Spring的七个核心模块,供大家参考,具体内容如下1、Spring core:核心容器核心容器提供spring框架的基本功能。Spring
- 电话号码的字母组合中等给定一个仅包含数字 2-9 的字符串,返回所有它能表示的字母组合。答案可以按 任意顺序 返回。给出数字到字母的映射如下
- 昨天下午快下班的时候,无意中听到公司两位同事在探讨批量向数据库插入数据的性能优化问题,顿时来了兴趣,把自己的想法向两位同事说了一下,于是有了
- 本文实例为大家分享了Java Socket编程实现多人交互聊天室的具体代码,供大家参考,具体内容如下本项目由三个.java文件(
- maven运行依赖于 JAVA_HOME如果各位还没有配置 JAVA_HOME,可以参考我的另一篇博客 JDK环境变量配置 JDK 环境变量
- 事件函数的执行顺序先说一下执行顺序吧。 官方给出的脚本中事件函数的执行顺序如下图: 我们可以做一个小实验来测试一下: 在Hierarchy
- 本文实例讲述了Android编程实现自动检测版本及自动升级的方法。分享给大家供大家参考,具体如下:步骤:1.检测当前版本的信息Android
- 很多时候我们需要在Android设备上下载远程服务器上的图片进行显示,今天整理出两种比较好的方法来实现远程图片的下载。 方法一、直
- 一、导航栏UINavigationBar1、导航栏的使用在iOS开发中,我们通常会使用导航控制器,导航控制器中封装了一个UINavigati
- 本文同时讨论了IComparable和IComparer接口,原因有两点。这两个接口经常一起使用。虽然接口类似且名称相似,但它们却有不同的用
- 为什么要前后端分离?以Java Web项目为例,在传统的开发模式中,前端代码(Html、js、css)写在JSP中,甚至JSP中嵌入Java