android实现可*移动、监听点击事件的悬浮窗
程序员文章站
2022-05-16 12:09:37
最近因为项目需要,自己实现了个可以*移动,并且长按可以跳出一个控制播放的,大的悬浮窗。
好,开始吧。首先我们先聊权限,悬浮窗需要在manifest中声明一个权限:...
最近因为项目需要,自己实现了个可以*移动,并且长按可以跳出一个控制播放的,大的悬浮窗。
好,开始吧。首先我们先聊权限,悬浮窗需要在manifest中声明一个权限:
<uses-permission android:name="android.permission.system_alert_window" />
然后呢,嗯,我们来讲讲关于悬浮窗实现的原理。
在andriod中,所有的界面元素都要通过windowmanger来实现,像activity、fragment等等这些也是在其上实现。因此,我们的悬浮窗自然要通过这个实现。
这个项目中,我们自定义了两个悬浮窗view。我们以其中一个比较简单的为例:
我们自定义一个管理可以统一管理悬浮窗的类mywindowmanager,负责创建,删除悬浮窗
/** * created by shiwe on 2017/3/7. * 悬浮窗管理 * 创建,移除 * 单例模式 */ public class mywindowmanager { private floatnormalview normalview; private floatcontrolview controlview; private static mywindowmanager instance; private mywindowmanager() { } public static mywindowmanager getinstance() { if (instance == null) instance = new mywindowmanager(); return instance; } /** * 创建小型悬浮窗 */ public void createnormalview(context context) { if (normalview == null) normalview = new floatnormalview(context); } /** * 移除悬浮窗 * * @param context */ public void removenormalview(context context) { if (normalview != null) { windowmanager windowmanager = (windowmanager) context.getsystemservice(context.window_service); windowmanager.removeview(normalview); normalview = null; } } /** * 创建小型悬浮窗 */ public void createcontrolview(context context) { if (controlview == null) controlview = new floatcontrolview(context); } /** * 移除悬浮窗 * * @param context */ public void removecontrolview(context context) { if (controlview != null) { windowmanager windowmanager = (windowmanager) context.getsystemservice(context.window_service); windowmanager.removeview(controlview); controlview = null; } } }
然后看看我们自定义的一个view,其继承自linearlayout,我们在initlayoutparams初始化这个控件的位置等其他参数;在initevent方法中定义随手指移动的监听事件以及长按的监听事件。
public class floatnormalview extends linearlayout { private context context = null; private view view = null; private imageview ivshowcontrolview = null; private windowmanager.layoutparams lp = new windowmanager.layoutparams(); private static windowmanager windowmanager; private float mtouchstartx; private float mtouchstarty; private float x; private float y; private boolean initviewplace = false; private mywindowmanager mywindowmanager; private boolean iscontrolviewshowing = false; public floatnormalview(context context) { super(context); this.context = context; mywindowmanager = mywindowmanager.getinstance(); layoutinflater.from(context).inflate(r.layout.float_normal_view, this); view = findviewbyid(r.id.ll_float_normal); ivshowcontrolview = (imageview) findviewbyid(r.id.iv_show_control_view); windowmanager = (windowmanager) context.getsystemservice(context.window_service); initlayoutparams(); initevent(); } /** * 初始化参数 */ private void initlayoutparams() { //屏幕宽高 int screenwidth = windowmanager.getdefaultdisplay().getwidth(); int screenheight = windowmanager.getdefaultdisplay().getheight(); //总是出现在应用程序窗口之上。 lp.type = windowmanager.layoutparams.type_phone; // flag_not_touch_modal不阻塞事件传递到后面的窗口 // flag_not_focusable 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题 lp.flags = windowmanager.layoutparams.flag_not_focusable | windowmanager.layoutparams.flag_not_touch_modal; //悬浮窗默认显示的位置 lp.gravity = gravity.start | gravity.top; //指定位置 lp.x = screenwidth - view.getlayoutparams().width * 2; lp.y = screenheight / 2 + view.getlayoutparams().height * 2; //悬浮窗的宽高 lp.width = windowmanager.layoutparams.wrap_content; lp.height = windowmanager.layoutparams.wrap_content; lp.format = pixelformat.transparent; windowmanager.addview(this, lp); } /** * 设置悬浮窗监听事件 */ private void initevent() { ivshowcontrolview.setonlongclicklistener(new onlongclicklistener() { @override public boolean onlongclick(view view) { if (!iscontrolviewshowing) { mywindowmanager.createcontrolview(context); iscontrolviewshowing = true; } else { mywindowmanager.removecontrolview(context); iscontrolviewshowing = false; } return true; } }); view.setontouchlistener(new ontouchlistener() { @override public boolean ontouch(view v, motionevent event) { switch (event.getaction()) { case motionevent.action_down: if (!initviewplace) { initviewplace = true; //获取初始位置 mtouchstartx += (event.getrawx() - lp.x); mtouchstarty += (event.getrawy() - lp.y); } else { //根据上次手指离开的位置与此次点击的位置进行初始位置微调 mtouchstartx += (event.getrawx() - x); mtouchstarty += (event.getrawy() - y); } break; case motionevent.action_move: // 获取相对屏幕的坐标,以屏幕左上角为原点 x = event.getrawx(); y = event.getrawy(); updateviewposition(); break; case motionevent.action_up: break; } return true; } }); } /** * 更新浮动窗口位置 */ private void updateviewposition() { lp.x = (int) (x - mtouchstartx); lp.y = (int) (y - mtouchstarty); windowmanager.updateviewlayout(this, lp); }
最后,只需要在activity中调用mywindowmanager中调用createxxx方法就可以。
public class mainactivity extends appcompatactivity { mywindowmanager mywindowmanager; @override protected void oncreate(@nullable bundle savedinstancestate) { super.oncreate(savedinstancestate); setcontentview(r.layout.activity_main); mywindowmanager = mywindowmanager.getinstance(); mywindowmanager.createnormalview(this.getapplicationcontext()); } }
最后,附上demo项目的下载地址: android实现悬浮窗
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。