Android利用WindowManager生成悬浮按钮及悬浮菜单
简介
本文模仿实现的是360手机卫士基础效果,同时后续会补充一些windowmanager的原理知识。
整体思路
360手机卫士的内存球其实就是一个没有画面的应用程序,整个应用程序的主体是一个service。我们的程序开始以后,启动一个service,同时关闭activity即可:
public class mainactivity extends activity { private static final string tag = mainactivity.class.getsimplename(); @override protected void oncreate(bundle savedinstancestate) { super.oncreate(savedinstancestate); startservice(new intent(this, floatwindowservice.class)); finish(); } } import android.os.ibinder; import android.util.log; import java.util.timer; import java.util.timertask; public class floatwindowservice extends service { private static final string tag = floatwindowservice.class.getsimplename(); public floatwindowservice() { } @override public int onstartcommand(intent intent, int flags, int startid) { log.d(tag, "on start command"); floatwindowmanager.instance(getapplicationcontext()).createfloatwindow(); return super.onstartcommand(intent, flags, startid); } @override public ibinder onbind(intent intent) { // todo: return the communication channel to the service. throw new unsupportedoperationexception("not yet implemented"); } }
我们要注意的是,传统的service默认是运行在ui线程中的,这点与封装了一个thread和handler的intentservice不同,所以我们可以直接在service中更改ui相关的内容。
再来看一下floatwindowmanager中的方法:
public void createfloatwindow() { if (iswindowshowing()) return; windowmanager windowmanager = getwindowmanger(context); int screenwidth = windowmanager.getdefaultdisplay().getwidth(); int screenheight = windowmanager.getdefaultdisplay().getheight(); if (floatlayout == null) { floatlayout = new floatlayout(context); if (smalllayoutparams == null) { smalllayoutparams = new windowmanager.layoutparams(); smalllayoutparams.type = windowmanager.layoutparams.type_phone; smalllayoutparams.format = pixelformat.rgba_8888; smalllayoutparams.flags = windowmanager.layoutparams.flag_not_touch_modal | windowmanager.layoutparams.flag_not_focusable; smalllayoutparams.gravity = gravity.left | gravity.top; smalllayoutparams.width = floatlayout.viewwidth; smalllayoutparams.height = floatlayout.viewheight; smalllayoutparams.x = screenwidth; smalllayoutparams.y = screenheight / 2; } } windowmanager.addview(floatlayout,smalllayoutparams); }
以及自定义的view:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/small_layout" android:background="@drawable/bg_small" android:orientation="vertical" android:layout_width="60dip" android:layout_height="25dip"> <textview android:layout_width="match_parent" android:gravity="center" android:text="悬浮窗" android:layout_height="match_parent" /> </linearlayout>
public class floatlayout extends linearlayout { public static int viewwidth; public static int viewheight; private windowmanager windowmanager; public floatlayout(final context context) { super(context); windowmanager = (windowmanager) context.getsystemservice(context.window_service); layoutinflater.from(context).inflate(r.layout.small_layout, this); view view = findviewbyid(r.id.small_layout); viewwidth = view.getlayoutparams().width; viewheight = view.getlayoutparams().height; setontouchlistener(new ontouchlistener() { @override public boolean ontouch(view v, motionevent event) { floatwindowmanager.instance(context).createfloatmenu(); return true; } }); } }
自定义的view除了加载了一个布局,就是设置了一个touch监听器,用于点击悬浮窗弹出菜单。注意这里要使用 view.getlayoutparams() 来获取视图的宽和高,因为在构造方法中,这个view并没有被measure完成,所以采用view.getheight得到的宽高是0。
创建菜单的方法类似,同样通过windowmanager:
public void createfloatmenu() { if (menulayout != null) return; log.d(tag, "create float menu"); windowmanager windowmanager = getwindowmanger(context); if (menulayout == null){ menulayout = new menulayout(context); menulayoutparams = new windowmanager.layoutparams(); menulayoutparams.type = windowmanager.layoutparams.type_phone; menulayoutparams.format = pixelformat.rgba_8888; } windowmanager.addview(menulayout,menulayoutparams); }
自定义的菜单将背景设置成半透明,同时分成上下两部分,上部分点击删除菜单,下部分是一些展示的内容:
<?xml version="1.0" encoding="utf-8"?> <linearlayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:background="#96000000" android:layout_height="match_parent"> <linearlayout android:layout_width="match_parent" android:id="@+id/trans_part" android:orientation="horizontal" android:layout_weight="1" android:layout_height="0dp"></linearlayout> <linearlayout android:layout_width="match_parent" android:layout_weight="1" android:background="@color/colorprimary" android:layout_height="0dp"> <textview android:layout_width="match_parent" android:text="存放content" android:layout_height="match_parent" /> </linearlayout> </linearlayout>
public class menulayout extends linearlayout { public menulayout(final context context) { super(context); layoutinflater.from(context).inflate(r.layout.transparent_layout,this); view view = findviewbyid(r.id.trans_part); view.setonclicklistener(new onclicklistener() { @override public void onclick(view v) { floatwindowmanager.instance(context).removemenulayout(); } }); } }
可以看见,实现悬浮窗,其实就是通过windowmanager.addview 时,在layoutparam 的type设置为type_phone,这样你的视图就是系统级视图,可以覆盖在全部程序的最上面。其余的,更多的是自定义view的知识。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
上一篇: Servlet实现代理文件下载功能
下一篇: Linux配置NodeJS环境