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


猜你喜欢
- 一个项目可能会有不同的环境,例如dev/stating/prod等,不同的环境的配置文件是不同的,如何根据环境快速的切换到对应的配置文件很重
- SqlssionFactory1.SqlSessionFactory是MyBatis的关键对象,它是个单个数据库映射关系经过编译后的内存镜像
- 本文实例讲述了C#生成随机数功能。分享给大家供大家参考,具体如下:using System;using System.Collections
- 关于滑动效果,在Android中用得比较多,本示例实现的滑动效果是使用ViewFlipper来实现的,当然也可以使用其它的View来实现。接
- 前言本节将介绍如何设置和使用MongoDB 驱动程序,通过 java实现与MongoDB服务端的通信功能,用户可以在此基础上进行各种Java
- 很不错的手电筒APP,分享给大家,希望大家喜欢。1. Java代码 package com.
- 首先写个单例:public class SingleDemo { private static SingleDemo s =
- aop,即面向切面编程,面向切面编程的目标就是分离关注点,比如:一个骑士只需要关注守护安全,或者远征,而骑士辉煌一生的事迹由谁来记录和歌颂呢
- 背景公司开发框架增加了web系统license授权证书校验模块,实行一台机器一个授权证书,初步方案是增加 * 针对全局请求进行拦截校验,评估
- 一、概述本质上,这是一个包含有可选值的包装类,这意味着 Optional 类既可以含有对象也可以为空。Optional 是 Java 实现函
- @RequestBody搭配@Data的坑如果用@Data修饰实体类,里面的属性最好不要用连续几个相同字母,如果用千万别用大写。比如下面这个
- C# 中闭包(Closure)详解这个问题是在最近一次英格兰 Brighton ALT.NET Beers 活动中提出来的。我发现,如果不用
- 前言学习Java和Android将近一年的时间了,期间的成果应该就是独立完成了一个Android客户端,并且保证了其在主线版本的稳定性。期间
- Zuul 简介Zuul 的主要功能是路由转发和过滤器。路由功能是微服务的一部分,比如 /api/admin 转发到到 Admin 服务,/a
- 目录一、概述二、环境配置及代码步骤1. 环境配置2. 代码步骤一、概述PDF打印小册子是指将PDF格式文档在打印成刊物前需要提前进行的页面排
- 目录基于Java的guava开源库工具类1、guava的maven配置引入 2、LoadingCache3、Multimap 和
- 一内容:设计一个手写字体识别程序。二实现①建立一个存放手写字体的数据库②activity_main.xml<?xml version=
- 特性(Attribute)是将额外数据关联到一个属性(以及其他构造)的一种方式,而枚举则是在编程中最常用的一种构造,枚举本质上其实是一些常量
- 1.下载JRebel and XRebel for Intellij插件2. 激活请查看这个文章http://www.cicoding.cn
- 前言之前探讨过的 sealed class 和 sealed interface 存在 module 的限制,但其主要用于密封 class