首先,感谢大佬得悬浮窗控件
EasyFloat:Android悬浮窗框架
先看效果图
如果是你想要的效果,继续往下看!
效果图
实现以上效果只需要三步 !!
这是对大佬源码的修改,需要把悬浮窗控件以lib的方式加到本地。
具体怎么去添加本地lib,请参考链接
添加github依赖库到本地
开始动手之前首先思考大佬做的悬浮窗和我们想要的结果差距需要改什么样的功能
- 悬浮窗没有半隐藏,我们需要半隐藏。
- 点击后需要显示全悬浮窗按钮,方便再次点击打开其他页面
- 拖动后需要处理自动半隐藏贴边
代码中需要用到的变量 :
//view大小 / 2
private val size = viewSize / 2
//是否自动半贴边显示
private var isAutoHide = true
private var isDrag = false //是否正在拖动
//创建handle
private var handler: MyHandler? = null
//view是半隐藏状态,ture 半隐藏
private var isSemiHidden = false
//点击时需要发送消息,handle msg 处理
private const val MESSAGE_CLICK = 100
//拖动结束后发送消息handle msg 处理
private const val MESSAGE_DRAG_END = 200
步骤 1 :在源码EasyFloat类中添加代码,方便链式调用
主要功能: 悬浮窗没有半隐藏,我们需要半隐藏。
/**
* 设置贴边方式
* @param isAutoHide 自动贴边半隐藏
*/
fun setAutoHide(isAutoHide: Boolean) = apply { config.isAutoHide = isAutoHide }
具体添加位置在这里
调用创建悬浮窗时可以调用到 setAutoHide
EasyFloat.with(activity!!).setLayout(R.layout.custom_floating_layout)
// 设置浮窗显示类型,默认只在当前Activity显示,可选一直显示、仅前台显示
.setShowPattern(ShowPattern.CURRENT_ACTIVITY)
// 设置吸附方式,共15种模式,详情参考SidePattern
.setSidePattern(SidePattern.RESULT_HORIZONTAL)
//这就是添加的代码
.setAutoHide(isAutoHide)
//设置tag
.setTag("当前页面的标记")
// 设置浮窗的对齐方式和坐标偏移量
.setGravity(Gravity.RIGHT or Gravity.BOTTOM, 0, -DpUtils.dp2px(146f))
.registerCallback {
createResult { isCreated, _, view ->
if (isCreated){
//创建悬浮窗成功
initAutoHide()
val imageView = view?.findViewById<ImageView>(R.id.imageCustomer)
imageView?.onClick {
//处理点击事件显示隐藏view
val dispose = clickWeltDispose(tag, imageView)
//贴边半隐藏状态点击不处理其他事件
if (dispose){
return@onClick
}
//点击客服悬浮窗跳转其他页面
ARouter.getInstance().build(RouteConstant.Order.Test2).navigation(view.context)
}
}
}
drag { v, event ->
//获取是否是正在拖动中
dragDispose(event)
}
dragEnd { v ->
//拖动结束后处理贴边隐藏
dragEndDispose(tag, v)
}
}.show()
其次,需要在源码FloatingWindowHelper类得setGravity方法中添加一下代码
if (config.isAutoHide){
//让悬浮窗突破屏幕外
params.flags = FLAG_NOT_TOUCH_MODAL or FLAG_NOT_FOCUSABLE or FLAG_LAYOUT_NO_LIMITS
if (params.x > (parentRect.width() / 2)){
//在右边,需要向左偏移一些
params.x += (view.measuredWidth / 2)
} else {
params.x -= (view.measuredWidth / 2)
}
}
这是源码内容位置
至此,第一步已经完成。
步骤2 :点击后显示,两秒后隐藏
主要功能: 点击后需要显示全悬浮窗按钮,方便再次点击打开其他页面
private fun clickWeltDispose(tag: String, v: View?): Boolean{
//是否是自动贴边半隐藏
if (!isAutoHide) return false
//点击隐藏,或者点击显示时。移除拖动后隐藏消息,以防再次隐藏
getHandle().removeMessages(MESSAGE_DRAG_END)
val location = IntArray(2)
// 获取 View 在屏幕中的位置
v?.getLocationOnScreen(location)
val viewX = location[0]
val viewCenterX = viewX + size
// 判断 View 中心是在屏幕的左边还是右边
val isLeft = viewCenterX < DpUtils.getScreenWidth() / 2
//创建view时是是半隐藏 isSemiHidden 为 true
if (isSemiHidden) {
//view在左边时
if (isLeft) {
//手动显示全view
EasyFloat.updateFloat(
tag,
viewX + size,
location[1],
v?.measuredWidth.defVal(),
v?.measuredHeight.defVal()
)
} else {
//手动显示全view
EasyFloat.updateFloat(
tag,
viewX - size,
location[1],
v?.measuredWidth.defVal(),
v?.measuredHeight.defVal()
)
}
//设置为显示
isSemiHidden = false
//如果点击后没有再次点击,一秒后恢复半隐藏贴边
val bundle = Bundle()
bundle.putString("tag", tag)
val message = Message.obtain()
message.what = MESSAGE_CLICK
message.obj = v
message.data = bundle
getHandle().sendMessageDelayed(message, 3000)
} else {
//再次点击后移除点击消息,这里会直接做隐藏
getHandle().removeMessages(MESSAGE_CLICK)
if (isLeft) {
//view在左边
//手动恢复半隐藏
EasyFloat.updateFloat(
tag,
viewX - size,
location[1],
v?.measuredWidth.defVal(),
v?.measuredHeight.defVal()
)
} else {
//view在右边
//手动恢复半隐藏
EasyFloat.updateFloat(
tag,
viewX + size,
location[1],
v?.measuredWidth.defVal(),
v?.measuredHeight.defVal()
)
}
//再次点击后,恢复view为贴边半隐藏状态
isSemiHidden = true
}
return !isSemiHidden
}
至此,第二步已经完成。
步骤3 :拖动后需要处理自动半隐藏贴边
首先判断是否是判断是否拖动中
/**
* 判断是否拖动中
*/
private fun dragDispose(event: MotionEvent) {
//先判断是否是自动隐藏贴边功能
if (isAutoHide) {
when (event.action) {
MotionEvent.ACTION_MOVE -> {
isDrag = true
}
MotionEvent.ACTION_UP -> {
isDrag = false
//拖动时肯定不是半隐藏状态,所以up时为false
isSemiHidden = false
}
}
}
}
其次拖动结束后发送消息,去处理
/**
* 拖动结束后发送消息,去处理
*/
private fun dragEndDispose(tag: String, v: View) {
if (isAutoHide) {
//拖动后移除点击消息后半隐藏消息
getHandle().removeMessages(MESSAGE_CLICK)
val bundle = Bundle()
bundle.putString("tag", tag)
val message = Message.obtain()
message.what = MESSAGE_DRAG_END
message.obj = v
message.data = bundle
getHandle().sendMessageDelayed(message, 2000)
}
}
最后处理贴边
private fun weltDispose(tag: String, v: View?) {
val location = IntArray(2)
// 获取 View 在屏幕中的位置
v?.getLocationOnScreen(location)
val viewX = location[0]
val viewCenterX = viewX + size
// 判断 View 中心是在屏幕的左边还是右边
val isLeft = viewCenterX < DpUtils.getScreenWidth() / 2
//检测是否已经贴边
if ((viewX < 0 && viewX <= -size) || viewX > 0 && viewX >= viewX + size) return
//从贴边显示一半到全部显示view
if (isLeft) {
//恢复隐藏
EasyFloat.updateFloat(
tag,
-size,
location[1],
v?.measuredWidth.defVal(),
v?.measuredHeight.defVal()
)
} else {
//恢复隐藏
EasyFloat.updateFloat(
tag,
viewX + size,
location[1],
v?.measuredWidth.defVal(),
v?.measuredHeight.defVal()
)
}
//恢复半隐藏
isSemiHidden = true
}
handle 代码
private class MyHandler : Handler(Looper.getMainLooper(), Callback { msg: Message ->
when (msg.what) {
MESSAGE_CLICK -> {
//拖动不处理
if (isDrag){
return@Callback true
}
val v = msg.obj as ImageView
val tag = msg.data.getString("tag")
weltDispose(tag.defVal(currentCustomer),v)
}
MESSAGE_DRAG_END ->{
val v = msg.obj as View
val tag = msg.data.getString("tag")
weltDispose(tag.defVal(currentCustomer),v)
}
}
true
})
至此 完成这个功能