现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的 *** 作。
今天这篇文章,就是介绍如何实现桌面悬浮窗效果的。
首先,看一下效果图。
悬浮窗一共分为两个部分,一个是平常显示的小窗口,另外一个是点击小窗口显示出来的二级悬浮窗口。
首先,先看一下这个项目的目录结构。
最关键的就是红框内的四个类。
首先,floatwindowservice是一个后台的服务类,主要负责在后台不断的刷新桌面上的小悬浮窗口,否则会导致更换界面之后,悬浮窗口也会随之消失,因此需要不断的刷新。下面是实现代码。
package com.qust.floatwindow; import java.util.Timer; import java.util.TimerTask; import androID.app.Service; import androID.content.Context; import androID.content.Intent; import androID.os.Handler; import androID.os.IBinder; /** * 悬浮窗后台服务 * * @author zhaokaiqiang * */ public class floatwindowservice extends Service { public static final String LAYOUT_RES_ID = "layoutResID"; public static final String ROOT_LAYOUT_ID = "rootLayoutID"; // 用于在线程中创建/移除/更新悬浮窗 private Handler handler = new Handler(); private Context context; private Timer timer; // 小窗口布局资源ID private int layoutResID; // 布局根布局ID private int rootLayoutID; @OverrIDe public int onStartCommand(Intent intent,int flags,int startID) { context = this; layoutResID = intent.getIntExtra(LAYOUT_RES_ID,0); rootLayoutID = intent.getIntExtra(ROOT_LAYOUT_ID,0); if (layoutResID == 0 || rootLayoutID == 0) { throw new IllegalArgumentException( "layoutResID or rootLayoutID is illegal"); } if (timer == null) { timer = new Timer(); // 每500毫秒就执行一次刷新任务 timer.scheduleAtFixedrate(new RefreshTask(),500); } return super.onStartCommand(intent,flags,startID); } @OverrIDe public voID onDestroy() { super.onDestroy(); // Service被终止的同时也停止定时器继续运行 timer.cancel(); timer = null; } private class RefreshTask extends TimerTask { @OverrIDe public voID run() { // 当前界面没有悬浮窗显示,则创建悬浮 if (!FloatwindowManager.getInstance(context).iswindowshowing()) { handler.post(new Runnable() { @OverrIDe public voID run() { FloatwindowManager.getInstance(context) .createSmallWindow(context,layoutResID,rootLayoutID); } }); } } } @OverrIDe public IBinder onBind(Intent intent) { return null; } }
除了后台服务之外,我们还需要两个自定义的布局,分别是floatwindowsmallVIEw和FloatwindowBigVIEw,这两个自定义的布局,主要负责悬浮窗的前台显示,我们分别看一下代码实现。
首先是floatwindowsmallVIEw类的实现。
package com.qust.floatwindow; import java.lang.reflect.FIEld; import androID.annotation.Suppresslint; import androID.content.Context; import androID.graphics.PixelFormat; import androID.vIEw.Gravity; import androID.vIEw.LayoutInflater; import androID.vIEw.MotionEvent; import androID.vIEw.VIEw; import androID.vIEw.WindowManager; import androID.Widget.linearLayout; import androID.Widget.TextVIEw; import com.qust.demo.ScreenUtils; import com.qust.floatingwindow.R; /** * 小悬浮窗,用于初始显示 * * @author zhaokaiqiang * */ public class floatwindowsmallVIEw extends linearLayout { // 小悬浮窗的宽 public int vIEwWIDth; // 小悬浮窗的高 public int vIEwHeight; // 系统状态栏的高度 private static int statusbarHeight; // 用于更新小悬浮窗的位置 private WindowManager windowManager; // 小悬浮窗的布局参数 public WindowManager.LayoutParams smallWindowParams; // 记录当前手指位置在屏幕上的横坐标 private float xInScreen; // 记录当前手指位置在屏幕上的纵坐标 private float yInScreen; // 记录手指按下时在屏幕上的横坐标,用来判断单击事件 private float xDownInScreen; // 记录手指按下时在屏幕上的纵坐标,用来判断单击事件 private float yDownInScreen; // 记录手指按下时在小悬浮窗的VIEw上的横坐标 private float xInVIEw; // 记录手指按下时在小悬浮窗的VIEw上的纵坐标 private float yInVIEw; // 单击接口 private OnClickListener Listener; /** * 构造函数 * * @param context * 上下文对象 * @param layoutResID * 布局资源ID * @param rootLayoutID * 根布局ID */ public floatwindowsmallVIEw(Context context,int layoutResID,int rootLayoutID) { super(context); windowManager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); LayoutInflater.from(context).inflate(layoutResID,this); VIEw vIEw = findVIEwByID(rootLayoutID); vIEwWIDth = vIEw.getLayoutParams().wIDth; vIEwHeight = vIEw.getLayoutParams().height; statusbarHeight = getStatusbarHeight(); TextVIEw percentVIEw = (TextVIEw) findVIEwByID(R.ID.percent); percentVIEw.setText("悬浮窗"); smallWindowParams = new WindowManager.LayoutParams(); // 设置显示类型为phone smallWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE; // 显示图片格式 smallWindowParams.format = PixelFormat.RGBA_8888; // 设置交互模式 smallWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_touch_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 设置对齐方式为左上 smallWindowParams.gravity = Gravity.left | Gravity.top; smallWindowParams.wIDth = vIEwWIDth; smallWindowParams.height = vIEwHeight; smallWindowParams.x = ScreenUtils.getScreenWIDth(context); smallWindowParams.y = ScreenUtils.getScreenHeight(context) / 2; } @Suppresslint("ClickableVIEwAccessibility") @OverrIDe public boolean ontouchEvent(MotionEvent event) { switch (event.getAction()) { // 手指按下时记录必要的数据,纵坐标的值都减去状态栏的高度 case MotionEvent.ACTION_DOWN: // 获取相对与小悬浮窗的坐标 xInVIEw = event.getX(); yInVIEw = event.getY(); // 按下时的坐标位置,只记录一次 xDownInScreen = event.getRawX(); yDownInScreen = event.getRawY() - statusbarHeight; break; case MotionEvent.ACTION_MOVE: // 时时的更新当前手指在屏幕上的位置 xInScreen = event.getRawX(); yInScreen = event.getRawY() - statusbarHeight; // 手指移动的时候更新小悬浮窗的位置 updateVIEwposition(); break; case MotionEvent.ACTION_UP: // 如果手指离开屏幕时,按下坐标与当前坐标相等,则视为触发了单击事件 if (xDownInScreen == event.getRawX() && yDownInScreen == (event.getRawY() - getStatusbarHeight())) { if (Listener != null) { Listener.click(); } } break; } return true; } /** * 设置单击事件的回调接口 */ public voID setonClickListener(OnClickListener Listener) { this.Listener = Listener; } /** * 更新小悬浮窗在屏幕中的位置 */ private voID updateVIEwposition() { smallWindowParams.x = (int) (xInScreen - xInVIEw); smallWindowParams.y = (int) (yInScreen - yInVIEw); windowManager.updateVIEwLayout(this,smallWindowParams); } /** * 获取状态栏的高度 * * @return */ private int getStatusbarHeight() { try { Class<?> c = Class.forname("com.androID.internal.R$dimen"); Object o = c.newInstance(); FIEld fIEld = c.getFIEld("status_bar_height"); int x = (Integer) fIEld.get(o); return getResources().getDimensionPixelSize(x); } catch (Exception e) { e.printstacktrace(); } return 0; } /** * 单击接口 * * @author zhaokaiqiang * */ public interface OnClickListener { public voID click(); } }
在这个类里面,主要的工作是实现悬浮窗口在桌面前端的实现,还有就是位置的移动和单击事件的判断以及处理。这里使用的是主要是WindowManager类的一些方法和属性,下一篇会详细说明,这篇只说实现。
除了小悬浮窗之外,点击之后d出的二级悬浮窗也是类似的方式添加到桌面上,下面是二级悬浮窗的代码。
package com.qust.floatwindow; import androID.content.Context; import androID.graphics.PixelFormat; import androID.vIEw.Gravity; import androID.vIEw.LayoutInflater; import androID.vIEw.VIEw; import androID.vIEw.WindowManager; import androID.Widget.linearLayout; import androID.Widget.TextVIEw; import com.qust.demo.ScreenUtils; import com.qust.floatingwindow.R; public class FloatwindowBigVIEw extends linearLayout { // 记录大悬浮窗的宽 public int vIEwWIDth; // 记录大悬浮窗的高 public int vIEwHeight; public WindowManager.LayoutParams bigWindowParams; private Context context; public FloatwindowBigVIEw(Context context) { super(context); this.context = context; LayoutInflater.from(context).inflate(R.layout.float_window_big,this); VIEw vIEw = findVIEwByID(R.ID.big_window_layout); vIEwWIDth = vIEw.getLayoutParams().wIDth; vIEwHeight = vIEw.getLayoutParams().height; bigWindowParams = new WindowManager.LayoutParams(); // 设置显示的位置,默认的是屏幕中心 bigWindowParams.x = ScreenUtils.getScreenWIDth(context) / 2 - vIEwWIDth / 2; bigWindowParams.y = ScreenUtils.getScreenHeight(context) / 2 - vIEwHeight / 2; bigWindowParams.type = WindowManager.LayoutParams.TYPE_PHONE; bigWindowParams.format = PixelFormat.RGBA_8888; // 设置交互模式 bigWindowParams.flags = WindowManager.LayoutParams.FLAG_NOT_touch_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; bigWindowParams.gravity = Gravity.left | Gravity.top; bigWindowParams.wIDth = vIEwWIDth; bigWindowParams.height = vIEwHeight; initVIEw(); } private voID initVIEw() { TextVIEw tv_back = (TextVIEw) findVIEwByID(R.ID.tv_back); tv_back.setonClickListener(new OnClickListener() { @OverrIDe public voID onClick(VIEw v) { FloatwindowManager.getInstance(context).removeBigWindow(); } }); } }
这些基本的类建立起来之后,剩下的就是最重要的类FloatwindowManager的实现。这个类实现的就是对悬浮窗的 *** 作。
package com.qust.floatwindow; import androID.content.Context; import androID.content.Intent; import androID.vIEw.WindowManager; /** * 悬浮窗管理器 * * @author zhaokaiqiang * */ public class FloatwindowManager { // 小悬浮窗对象 private floatwindowsmallVIEw smallWindow; // 大悬浮窗对象 private FloatwindowBigVIEw bigWindow; // 用于控制在屏幕上添加或移除悬浮窗 private WindowManager mWindowManager; // FloatwindowManager的单例 private static FloatwindowManager floatwindowManager; // 上下文对象 private Context context; private FloatwindowManager(Context context) { this.context = context; } public static FloatwindowManager getInstance(Context context) { if (floatwindowManager == null) { floatwindowManager = new FloatwindowManager(context); } return floatwindowManager; } /** * 创建小悬浮窗 * * @param context * 必须为应用程序的Context. */ public voID createSmallWindow(Context context,int rootLayoutID) { WindowManager windowManager = getwindowManager(); if (smallWindow == null) { smallWindow = new floatwindowsmallVIEw(context,rootLayoutID); windowManager.addVIEw(smallWindow,smallWindow.smallWindowParams); } } /** * 将小悬浮窗从屏幕上移除 * * @param context */ public voID removeSmallWindow() { if (smallWindow != null) { WindowManager windowManager = getwindowManager(); windowManager.removeVIEw(smallWindow); smallWindow = null; } } public voID setonClickListener(floatwindowsmallVIEw.OnClickListener Listener) { if (smallWindow != null) { smallWindow.setonClickListener(Listener); } } /** * 创建大悬浮窗 * * @param context * 必须为应用程序的Context. */ public voID createBigWindow(Context context) { WindowManager windowManager = getwindowManager(); if (bigWindow == null) { bigWindow = new FloatwindowBigVIEw(context); windowManager.addVIEw(bigWindow,bigWindow.bigWindowParams); } } /** * 将大悬浮窗从屏幕上移除 * * @param context */ public voID removeBigWindow() { if (bigWindow != null) { WindowManager windowManager = getwindowManager(); windowManager.removeVIEw(bigWindow); bigWindow = null; } } public voID removeAll() { context.stopService(new Intent(context,floatwindowservice.class)); removeSmallWindow(); removeBigWindow(); } /** * 是否有悬浮窗显示(包括小悬浮窗和大悬浮) * * @return 有悬浮窗显示在桌面上返回true,没有的话返回false */ public boolean iswindowshowing() { return smallWindow != null || bigWindow != null; } /** * 如果WindowManager还未创建,则创建新的WindowManager返回。否则返回当前已创建的WindowManager * * @param context * @return */ private WindowManager getwindowManager() { if (mWindowManager == null) { mWindowManager = (WindowManager) context .getSystemService(Context.WINDOW_SERVICE); } return mWindowManager; } }
还有个获取屏幕宽高的帮助类。
package com.qust.demo; import androID.content.Context; import androID.vIEw.WindowManager; /** * 屏幕帮助类 * * @author zhaokaiqiang * */ public class ScreenUtils { /** * 获取屏幕宽度 * * @return */ @SuppressWarnings("deprecation") public static int getScreenWIDth(Context context) { return ((WindowManager) context .getSystemService(Context.WINDOW_SERVICE)).getDefaultdisplay() .getWIDth(); } /** * 获取屏幕宽度 * * @return */ @SuppressWarnings("deprecation") public static int getScreenHeight(Context context) { return ((WindowManager) context .getSystemService(Context.WINDOW_SERVICE)).getDefaultdisplay() .getHeight(); } }
完成这些,我们就可以直接用了。
package com.qust.demo; import androID.app.Activity; import androID.content.Context; import androID.content.Intent; import androID.os.Bundle; import androID.vIEw.KeyEvent; import androID.vIEw.VIEw; import com.qust.floatingwindow.R; import com.qust.floatwindow.FloatwindowManager; import com.qust.floatwindow.floatwindowservice; import com.qust.floatwindow.floatwindowsmallVIEw.OnClickListener; /** * 示例 * * @Classname: com.qust.demo.MainActivity * @Description: * @author zhaokaiqiang * @date 2014-10-23 下午11:30:13 * */ public class MainActivity extends Activity { private FloatwindowManager floatwindowManager; private Context context; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); context = this; floatwindowManager = FloatwindowManager.getInstance(context); } /** * 显示小窗口 * * @param vIEw */ public voID show(VIEw vIEw) { // 需要传递小悬浮窗布局,以及根布局的ID,启动后台服务 Intent intent = new Intent(context,floatwindowservice.class); intent.putExtra(floatwindowservice.LAYOUT_RES_ID,R.layout.float_window_small); intent.putExtra(floatwindowservice.ROOT_LAYOUT_ID,R.ID.small_window_layout); startService(intent); } /** * 显示二级悬浮窗 * * @param vIEw */ public voID showBig(VIEw vIEw) { // 设置小悬浮窗的单击事件 floatwindowManager.setonClickListener(new OnClickListener() { @OverrIDe public voID click() { floatwindowManager.createBigWindow(context); } }); } /** * 移除所有的悬浮窗 * * @param vIEw */ public voID remove(VIEw vIEw) { floatwindowManager.removeAll(); } @OverrIDe public boolean onKeyDown(int keyCode,KeyEvent event) { // 返回键移除二级悬浮窗 if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) { floatwindowManager.removeBigWindow(); return true; } return super.onKeyDown(keyCode,event); } }
项目下载地址:https://github.com/ZhaoKaiQiang/FloatWindow
在上面文章中,我们介绍了如何实现桌面悬浮窗口,在这个效果的实现过程中,最重要的一个类就是WindowManager,今天这篇文章,将对WindowManager的使用进行介绍,并且实现一个使用WindowManager来实现用户打开APP,显示首次使用教学蒙板的效果。
WindowManager类实现了VIEwManager接口,VIEwManager接口允许我们在Activity上添加或者是移除vIEw,因此WindowManager也允许我们在Activity上进行VIEw的添加和移除 *** 作。
我们可以通过下面的方法获取一个WindowManager对象
复制代码 代码如下:
Context.getSystemService(Context.WINDOW_SERVICE)
在Activity之中,我们可以直接通过getwindowManager()获取到一个WindowManager对象。
每一个WindowManager实例都被绑定到一个独有的display对象上面,如果我们想获取不同display的WindowManager对象,我们可以通过createdisplayContext(display)获取到这个display的Context对象,然后使用上面的方法,也可以获取到一个WindowManager对象。
我们在使用WindowManager类的时候,通常使用下面的几个方法:
windowManager.addVIEw(VIEw,WindowManager.LayoutParam);windowManager.removeVIEw();windowManager.getDefaultdisplay();
windowManager.addVIEw()方法用来向当前的窗口上添加VIEw对象,需要接受两个参数,VIEw是要添加到窗口的VIEw对象,而WindowManager.LayoutParam则是添加的窗口的参数,在上一篇添加悬浮窗的 *** 作的时候,需要对LayoutParam设置很多参数,下面我们看一下常用的设置
// 设置LayoutParams参数 LayoutParams params = new WindowManager.LayoutParams(); //设置显示的类型,TYPE_PHONE指的是来电话的时候会被覆盖,其他时候会在最前端,显示位置在statebar下面,其他更多的值请查阅文档 params.type = WindowManager.LayoutParams.TYPE_PHONE; //设置显示格式 params.format = PixelFormat.RGBA_8888; //设置对齐方式 params.gravity = Gravity.left | Gravity.top; //设置宽高 params.wIDth = ScreenUtils.getScreenWIDth(this); params.height = ScreenUtils.getScreenHeight(this); //设置显示的位置 params.x; params.y;
设置好LayoutParam之后,我们就可以通过windowManager.addVIEw(VIEw,WindowManager.LayoutParam)将VIEw添加到窗口之上,不过,我们需要申明权限
<uses-permissionAndroID:name="androID.permission.SYstem_ALERT_WINDOW"/>
添加完成之后,我们就可以在窗口上看到我们添加的VIEw对象了。如果我们想将添加的VIEw移除,我们只需要调用windowManager.removeVIEw()即可,参数就是我们前面使用的VIEw对象,使用很简单。除了这个方法,还有个windowManager.removeVIEwImmediate(),也可以将VIEw移除,但是文档中说,这个方法并不是给一般程序调用的,因此需要小心使用,我们开发的都属于一般程序,建议不要使用这个方法。
除了这两个方法之外,我们最常用的另外一个方法就是windowManager.getDefaultdisplay(),通过这个方法,我们可以获取到当前界面的display的一个对象,然后我们就可以获取到当前屏幕的一些参数,比如说宽高。
下面是我常用的一个工具类。
package com.qust.teachmask; import androID.content.Context; import androID.vIEw.WindowManager; /** * 屏幕帮助类 * * @author zhaokaiqiang * */ public class ScreenUtils { /** * 获取屏幕宽度 * * @return */ @SuppressWarnings("deprecation") public static int getScreenWIDth(Context context) { return ((WindowManager) context .getSystemService(Context.WINDOW_SERVICE)).getDefaultdisplay() .getWIDth(); } /** * 获取屏幕宽度 * * @return */ @SuppressWarnings("deprecation") public static int getScreenHeight(Context context) { return ((WindowManager) context .getSystemService(Context.WINDOW_SERVICE)).getDefaultdisplay() .getHeight(); } }
知道上面这些之后,我们就可以实现教学模板效果了,首先看效果图。
下面是代码实现
package com.qust.teachmask; import androID.app.Activity; import androID.graphics.PixelFormat; import androID.os.Bundle; import androID.vIEw.Gravity; import androID.vIEw.VIEw; import androID.vIEw.VIEw.OnClickListener; import androID.vIEw.WindowManager; import androID.vIEw.WindowManager.LayoutParams; import androID.Widget.ImageVIEw; import androID.Widget.ImageVIEw.ScaleType; public class MainActivity extends Activity { private ImageVIEw img; private WindowManager windowManager; @OverrIDe protected voID onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentVIEw(R.layout.activity_main); windowManager = getwindowManager(); // 动态初始化图层 img = new ImageVIEw(this); img.setLayoutParams(new LayoutParams( androID.vIEw.VIEwGroup.LayoutParams.MATCH_PARENT,androID.vIEw.VIEwGroup.LayoutParams.MATCH_PARENT)); img.setScaleType(ScaleType.FIT_XY); img.setimageResource(R.drawable.guIDe); // 设置LayoutParams参数 LayoutParams params = new WindowManager.LayoutParams(); // 设置显示的类型,TYPE_PHONE指的是来电话的时候会被覆盖,其他时候会在最前端,显示位置在statebar下面,其他更多的值请查阅文档 params.type = WindowManager.LayoutParams.TYPE_PHONE; // 设置显示格式 params.format = PixelFormat.RGBA_8888; // 设置对齐方式 params.gravity = Gravity.left | Gravity.top; // 设置宽高 params.wIDth = ScreenUtils.getScreenWIDth(this); params.height = ScreenUtils.getScreenHeight(this); // 添加到当前的窗口上 windowManager.addVIEw(img,params); // 点击图层之后,将图层移除 img.setonClickListener(new OnClickListener() { @OverrIDe public voID onClick(VIEw arg0) { windowManager.removeVIEw(img); } }); } }
本文非原创,转载于:http://blog.csdn.net/zhaokaiqiang1992
以上所述是小编给大家介绍的AndroID实现桌面悬浮窗、蒙板效果实例代码,希望对大家有所帮助!
总结以上是内存溢出为你收集整理的Android实现桌面悬浮窗、蒙板效果实例代码全部内容,希望文章能够帮你解决Android实现桌面悬浮窗、蒙板效果实例代码所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)