当前位置: 移动技术网 > 移动技术>移动开发>Android > Android自定义覆盖层控件 悬浮窗控件

Android自定义覆盖层控件 悬浮窗控件

2019年07月24日  | 移动技术网移动技术  | 我要评论

在我们移动应用开发过程中,偶尔有可能会接到这种需求:

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 x, 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, outsidetouchlistener listener, 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 x,int y){ 
  if(floatwindow!=null){ 
   floatwindow.dismiss(); 
  } 
  floatwindow=new floatwindow(context); 
  floatwindow.show(view,x,y); 
 } 
 
 
 
 public void showfloatwindow(context context, view view, int x, int y, outsidetouchlistener listener,keybacklistener backlistener){ 
  if(floatwindow!=null){ 
   floatwindow.dismiss(); 
  } 
  floatwindow=new floatwindow(context); 
  floatwindow.show(view,0,0,listener,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); 
 }; 
} 

还有设计类似悬浮球的窗口等 大家可以自己运行一遍比这里看千遍更有用。

附件:

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持移动技术网。

如对本文有疑问, 点击进行留言回复!!

相关文章:

验证码:
移动技术网