枕上从妨一夜睡,灯前读尽十年诗。这篇文章主要讲述Android 悬浮窗悬浮球开发相关的知识,希望能为你提供帮助。
原文:Android 悬浮窗、悬浮球开发1、权限管理直接看我另外一篇博客吧,传送门:
https://my.oschina.net/u/1462828/blog/1933162
2、Base类BaseSuspend
import android.content.Context;
import android.graphics.PixelFormat;
import android.os.Build;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.WindowManager;
import com.imxiaoyu.common.utils.entity.SizeEntity;
public abstract class BaseSuspend {
private Context context;
private View view;
private boolean isShowing = false;
/**
* UI
*/
private WindowManager.LayoutParams wmParams;
//悬浮窗的布局/**
* 变量
*/
private WindowManager mWindowManager;
//创建浮动窗口设置布局参数的对象/**
* 接口
*/
private OnSuspendDismissListener onSuspendDismissListener;
public BaseSuspend(Context context) {
this.context = context;
view = LayoutInflater.from(context).inflate(getLayoutId(), null);
init();
initView();
onCreateSuspension();
}public void init() {
if (mWindowManager == null) {
mWindowManager = (WindowManager) context.getApplicationContext().getSystemService(Context.WINDOW_SERVICE);
}
wmParams = getParams();
//设置好悬浮窗的参数
// 悬浮窗默认显示以左上角为起始坐标
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
}/**
* 布局文件id,这里是用不到的,但还是建议填写,方便跳转到布局管理
*
* @return
*/
protected abstract int getLayoutId();
/**
* 注册需要使用的控件
*/
protected abstract void initView();
protected abstract void onCreateSuspension();
/**
* 根据id快速找到控件
*
* @param id
* @param <
E>
* @return
*/
public final <
E extends View>
E findView(int id) {
try {
return (E) view.findViewById(id);
} catch (ClassCastException ex) {
throw ex;
}
}/**
* 根据id快速找到控件
*
* @param id
* @param onClickListener
* @param <
E>
* @return
*/
public final <
E extends View>
E findView(int id, View.OnClickListener onClickListener) {
E e = findView(id);
e.setOnClickListener(onClickListener);
return e;
}/**
* 对windowManager进行设置
*
* @return
*/
public WindowManager.LayoutParams getParams() {
wmParams = new WindowManager.LayoutParams();
//设置window type 下面变量2002是在屏幕区域显示,2003则可以显示在状态栏之上
//wmParams.type = LayoutParams.TYPE_PHONE;
if (Build.VERSION.SDK_INT <
Build.VERSION_CODES.O) {
wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
} else {
wmParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
}
//wmParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ERROR;
//设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
//wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE;
//设置可以显示在状态栏上
wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR |
WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
return wmParams;
}/**
* 全屏显示悬浮视图
*/
public void showSuspend() {
showSuspend(0, 0, true);
}/**
* 显示悬浮视图
*
* @param sizeEntity
* @param isMatchParent 是否全屏显示
*/
public void showSuspend(SizeEntity sizeEntity, boolean isMatchParent) {
if (sizeEntity != null) {
showSuspend(sizeEntity.getWidth(), sizeEntity.getHeight(), isMatchParent);
}
}/**
* 显示悬浮视图
*
* @param width
* @param height
*/
public void showSuspend(int width, int height, boolean isMatchParent) {
//设置悬浮窗口长宽数据
if (isMatchParent) {
wmParams.width = WindowManager.LayoutParams.MATCH_PARENT;
wmParams.height = WindowManager.LayoutParams.MATCH_PARENT;
} else {
wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
}
//悬浮窗的开始位置,读取缓存
wmParams.x = width;
wmParams.y = height;
if (isShowing) {
removeView();
}
mWindowManager.addView(view, wmParams);
isShowing = true;
}/**
* 更新当前视图的位置
*
* @param x 更新后的X轴的增量
* @param y 更新后的Y轴的增量
*/
public void updateSuspend(int x, int y) {
if (view != null) {
//必须是当前显示的视图才给更新
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) view.getLayoutParams();
layoutParams.x += x;
layoutParams.y += y;
mWindowManager.updateViewLayout(view, layoutParams);
}
}/**
* 移除当前悬浮窗
*/
public void dismissSuspend() {
if (view != null) {
mWindowManager.removeView(view);
isShowing = false;
if (onSuspendDismissListener != null) {
onSuspendDismissListener.onDismiss();
}
}
}public Context getContext() {
return context;
}public View getView() {
return view;
}/**
* 是否正在显示
*
* @return
*/
public boolean isShowing() {
return isShowing;
}/**
* 移除弹窗的时候回调
*
* @param onSuspendDismissListener
*/
public void setOnSuspendDismissListener(OnSuspendDismissListener onSuspendDismissListener) {
this.onSuspendDismissListener = onSuspendDismissListener;
}public interface OnSuspendDismissListener {
public void onDismiss();
}
}
还有里面用到的一个size类:
/**
* 宽高实体
* Created by 她叫我小渝 on 2016/11/4.
*/public class SizeEntity {
private int width;
private int height;
public SizeEntity(){}
public SizeEntity(int width,int height){
setWidth(width);
setHeight(height);
}public int getWidth() {
return width;
}public void setWidth(int width) {
this.width = width;
}public int getHeight() {
return height;
}public void setHeight(int height) {
this.height = height;
}
}
3、定制视图和使用要实现的逻辑是,显示一个悬浮球,然后可以拖动移动悬浮球的位置,效果图:
文章图片
? ? ? ? ??
文章图片
然后新建一个类,LogoSuspend继承BaseSuspend,里面引用到了一些工具类就不贴出来了,用到的地方我会加上注释
/**
* 悬浮球
* Created by 她叫我小渝 on 2017/1/1.
*/public class LogoSuspend extends BaseSuspend {/**
* ui
*/
private ImageView ivLogo;
/**
* 变量
*/
private int width, height;
private float mStartX, mStartY, mStopX, mStopY, touchStartX, touchStartY;
private long touchStartTime;
/**
* 接口
*/
private View.OnClickListener onClickListener;
public LogoSuspend(Context context) {
super(context);
}@Override
protected int getLayoutId() {
return R.layout.suspend_logo;
}@Override
protected void initView() {
ivLogo = findView(R.id.iv_logo);
ivLogo.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent event) {
final int action = event.getAction();
mStopX = event.getRawX();
mStopY = event.getRawY();
switch (action) {
case MotionEvent.ACTION_DOWN:
// 以当前父视图左上角为原点
mStartX = event.getRawX();
mStartY = event.getRawY();
touchStartX = event.getRawX();
touchStartY = event.getRawY();
touchStartTime = DateUtil.getTimeForLong();
//获取当前时间戳
break;
case MotionEvent.ACTION_MOVE:
width = (int) (mStopX - mStartX);
height = (int) (mStopY - mStartY);
mStartX = mStopX;
mStartY = mStopY;
updateSuspend(width, height);
break;
case MotionEvent.ACTION_UP:
width = (int) (mStopX - mStartX);
height = (int) (mStopY - mStartY);
updateSuspend(width, height);
WindowManager.LayoutParams layoutParams = (WindowManager.LayoutParams) getView().getLayoutParams();
SuspensionCache.setSuspendSize(getContext(), new SizeEntity(layoutParams.x + width, layoutParams.y + height));
//缓存一下当前位置
if ((mStopX - touchStartX) <
30 &
&
(mStartY - touchStartY) <
30 &
&
(DateUtil.getTimeForLong() - touchStartTime) <
300) {
//左右上下移动距离不超过30的,并且按下和抬起时间少于300毫秒,算是单击事件,进行回调
if (onClickListener != null) {
onClickListener.onClick(view);
}
}
break;
}
return true;
}
});
}@Override
protected void onCreateSuspension() {}/**
* 设置点击监听
*
* @param onClickListener
*/
public void setOnClickListener(View.OnClickListener onClickListener) {
this.onClickListener = onClickListener;
}
}
布局文件syspend_logo.xml
<
RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/rly_bg"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<
ImageView
android:id="@+id/iv_logo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="https://www.songbingjia.com/android/@drawable/ic_home_add_normal" />
<
/RelativeLayout>
因为Activity是有生命周期的,所以打开悬浮窗的Context上下文,不要用Activity的,而是用Service的
创建并注册一个Service,然后在onCreate方法中执行调用代码就好
@Override
public void onCreate() {
super.onCreate();
ALog.e("服务已创建");
if (logoSuspend == null) {
logoSuspend = new LogoSuspend(this);
}
logoSuspend.showSuspend(SuspensionCache.getSuspendSize(this), false);
//从缓存中提取上一次显示的位置
logoSuspend.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
//处理单击事件
}
});
}
4、废话????上面的例子,其实还是比较简单的,但一般开发对于悬浮球的需求并不算很大,Base类的话,目前只是最基础的东西,在开发的过程中,需要用到什么了再往里面加就好,问题不大。
????目前代码支持同时显示多个悬浮窗、悬浮球,主要用于在于悬浮窗交互的时候,直接弹出其他的交互界面(也是以悬浮窗的状态出现),但建议每一个页面都有关闭按钮或者做返回键关闭的相关操作,毕竟是显示在最前端的,要是关不掉就点哪里都没用,只能是强制关机了…………()& @()……()@#*¥)()@*#…………@#)()*¥)()…………
【Android 悬浮窗悬浮球开发】????
推荐阅读
- Android Studio3.1.4如何添加Genymotion插件并显示
- Android 调用系统分享文字图片文件,可直达微信朋友圈QQQQ空间微博
- Android 悬浮窗权限校验
- Android TV开发中所有的遥控器按键监听及注意事项,新增home键监听
- 知买app小程序是什么-知买怎么使用
- 从小白角度探索Android事件分发机制
- 解决(Android 8.0检测不到当前的activity)
- react native 安卓home返回键页面刷新
- cordova APP 检查更新