在我们移动应用开发过程中,偶尔有可能会接到这种需求:
1、在手机桌面创建一个窗口,类似于360的悬浮窗口,点击这个窗口可以响应(至于窗口拖动我们可以后面再扩展)。
2、自己开发的应用去启动一个非本应用B,在B应用的某个界面增加一个引导窗口。
3、在应用的页面上触发启动这个窗口,该窗口悬浮在这个页面上,但又不会影响界面的其他 *** 作。即不像PopupWindow那样要么窗口消失要么页面不可响应
以上需求都有几个共同特点,1、窗口的承载页面不一定不是本应用页面(Activity),即不是类似dialog,PopupWindow之类的页面。2、窗口的显示不会影响用户对其他界面的 *** 作。
根据以上特点,我们发现这类的窗口其不影响其他界面 *** 作特点有点像Toast,但又不完全是,因为Toast是自己消失的。其界面可以恒显示又有点像popupwindow,只当调用了消失方法才会消失。所以我们在做这样的控件的时候可以去参考一下Toast和PopupWIndow如何实现。最主要的时候Toast。好了说了这么多大概的思路我们已经明白了。
透过Toast,PopupWindow源码我们发现,Toast,Popup的实现都是通过windowManager的addvIEw和removeVIEw以及通过设置LayoutParams实现的。因此后面设计就该从这里入手,废话不说了----去实现。
第一步设计类似Toast的类Floatwindow
package com.floatwindowtest.john.floatwindowtest.wiget; import androID.app.Activity; import androID.content.Context; import androID.graphics.PixelFormat; import androID.vIEw.Gravity; import androID.vIEw.KeyEvent; import androID.vIEw.MotionEvent; import androID.vIEw.VIEw; import androID.vIEw.VIEwGroup; import androID.vIEw.WindowManager; import androID.Widget.FrameLayout; import androID.Widget.linearLayout; import static androID.vIEw.VIEwGroup.LayoutParams.MATCH_PARENT; import static androID.vIEw.VIEwGroup.LayoutParams.WRAP_CONTENT; import static androID.vIEw.WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; import static androID.vIEw.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_touch; /** * Created by john on 2017/3/10. */ class Floatwindow { private final Context mContext; private WindowManager windowManager; private VIEw floatVIEw; private WindowManager.LayoutParams params; public Floatwindow(Context mContext) { this.mContext = mContext; this.params = new WindowManager.LayoutParams(); } /** * 显示浮动窗口 * @param vIEw * @param x vIEw距离左上角的x距离 * @param y vIEw距离左上角的y距离 */ voID show(VIEw vIEw,int x,int y) { this.windowManager = (WindowManager) this.mContext.getSystemService(Context.WINDOW_SERVICE); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.wIDth = WindowManager.LayoutParams.WRAP_CONTENT; params.gravity = Gravity.top | Gravity.left; params.format = PixelFormat.TRANSLUCENT; params.x = x; params.y = y; params.type = WindowManager.LayoutParams.TYPE_TOAST; params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | FLAG_NOT_FOCUSABLE | FLAG_WATCH_OUTSIDE_touch | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM; floatVIEw = vIEw; windowManager.addVIEw(floatVIEw,params); } /** * 显示浮动窗口 * @param vIEw * @param x * @param y * @param Listener 窗体之外的监听 * @param backListener 返回键盘监听 */ voID show(VIEw vIEw,int y,OutsIDetouchListener Listener,KeyBackListener backListener) { this.windowManager = (WindowManager) this.mContext.getSystemService(Context.WINDOW_SERVICE); final FloatwindowContainerVIEw containerVIEw = new FloatwindowContainerVIEw(this.mContext,Listener,backListener); containerVIEw.addVIEw(vIEw,WRAP_CONTENT,WRAP_CONTENT); params.height = WindowManager.LayoutParams.WRAP_CONTENT; params.wIDth = WindowManager.LayoutParams.WRAP_CONTENT; params.gravity = Gravity.top | Gravity.left; params.format = PixelFormat.TRANSLUCENT; params.x = x; params.y = y; params.type = WindowManager.LayoutParams.TYPE_TOAST; // // params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON // | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_touch // | WindowManager.LayoutParams. FLAG_NOT_FOCUSABLE ; params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_touch | WindowManager.LayoutParams.FLAG_NOT_touch_MODAL; floatVIEw = containerVIEw; windowManager.addVIEw(floatVIEw,params); } /** * 更新vIEw对象文职 * * @param offset_X x偏移量 * @param offset_Y Y偏移量 */ public voID updateWindowLayout(float offset_X,float offset_Y) { params.x += offset_X; params.y += offset_Y; windowManager.updateVIEwLayout(floatVIEw,params); } /** * 关闭界面 */ voID dismiss() { if (this.windowManager == null) { this.windowManager = (WindowManager) this.mContext.getSystemService(Context.WINDOW_SERVICE); } if (floatVIEw != null) { windowManager.removeVIEw(floatVIEw); } floatVIEw = null; } public voID justHIDeWindow() { this.floatVIEw.setVisibility(VIEw.GONE); } private class FloatwindowContainerVIEw extends FrameLayout { private OutsIDetouchListener Listener; private KeyBackListener backListener; public FloatwindowContainerVIEw(Context context,KeyBackListener backListener) { super(context); this.Listener = Listener; this.backListener = backListener; } @OverrIDe public boolean dispatchKeyEvent(KeyEvent event) { if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { if (getKeydispatcherState() == null) { if (backListener != null) { backListener.onKeyBackpressed(); } return super.dispatchKeyEvent(event); } if (event.getAction() == KeyEvent.ACTION_DOWN && event.getRepeatCount() == 0) { KeyEvent.dispatcherState state = getKeydispatcherState(); if (state != null) { state.startTracking(event,this); } return true; } else if (event.getAction() == KeyEvent.ACTION_UP) { KeyEvent.dispatcherState state = getKeydispatcherState(); if (state != null && state.isTracking(event) && !event.isCanceled()) { System.out.println("dsfdfdsfds"); if (backListener != null) { backListener.onKeyBackpressed(); } return super.dispatchKeyEvent(event); } } return super.dispatchKeyEvent(event); } else { return super.dispatchKeyEvent(event); } } @OverrIDe public boolean ontouchEvent(MotionEvent event) { final int x = (int) event.getX(); final int y = (int) event.getY(); if ((event.getAction() == MotionEvent.ACTION_DOWN) && ((x < 0) || (x >= getWIDth()) || (y < 0) || (y >= getHeight()))) { return true; } else if (event.getAction() == MotionEvent.ACTION_OUTSIDE) { if (Listener != null) { Listener.onOutsIDetouch(); } System.out.println("dfdf"); return true; } else { return super.ontouchEvent(event); } } } }
大家可能会注意到
// params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON // | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_touch // | WindowManager.LayoutParams. FLAG_NOT_FOCUSABLE ; params.flags = WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON | WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_touch | WindowManager.LayoutParams.FLAG_NOT_touch_MODAL;
这些设置有所不同,这就是我们要实现既能够监听窗口之外的触目事件,又不会影响他们自己的 *** 作的关键地方 ,同时| WindowManager.LayoutParams. FLAG_NOT_FOCUSABLE ;和| WindowManager.LayoutParams.FLAG_NOT_touch_MODAL; 窗体是否监听到返回键的关键设置 需要指出的是一旦窗体监听到返回键事件,则当前Activity不会再监听到返回按钮事件了,所以大家可根据自己的实际情况出发做出选择。
为了方便管理这些浮动窗口的显示和消失,还写了一个管理窗口显示的类FloatwindowManager。这是一个单例模式 对应的显示窗口也是只显示一个。大家可以根据自己的需求是改变 这里不再明细。
package com.floatwindowtest.john.floatwindowtest.wiget; import androID.content.Context; import androID.vIEw.VIEw; /** * * Created by john on 2017/3/10. */ public class FloatwindowManager { private static FloatwindowManager manager; private Floatwindow floatwindow; private FloatwindowManager(){ } public static synchronized FloatwindowManager getInstance(){ if(manager==null){ manager=new FloatwindowManager(); } return manager; } public voID showFloatwindow(Context context,VIEw vIEw,int y){ if(floatwindow!=null){ floatwindow.dismiss(); } floatwindow=new Floatwindow(context); floatwindow.show(vIEw,x,y); } public voID showFloatwindow(Context context,KeyBackListener backListener){ if(floatwindow!=null){ floatwindow.dismiss(); } floatwindow=new Floatwindow(context); floatwindow.show(vIEw,backListener); } public voID dismissFloatwindow(){ if(floatwindow!=null){ floatwindow.dismiss(); } } public voID justHIDeWindow(){ floatwindow.justHIDeWindow(); } /** * 更新位置 * @param offsetX * @param offsetY */ public voID updateWindowLayout(float offsetX,float offsetY){ floatwindow.updateWindowLayout(offsetX,offsetY); }; }
还有设计类似悬浮球的窗口等 大家可以自己运行一遍比这里看千遍更有用。
附件:Android浮动窗口
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
总结以上是内存溢出为你收集整理的Android自定义覆盖层控件 悬浮窗控件全部内容,希望文章能够帮你解决Android自定义覆盖层控件 悬浮窗控件所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)