广告
返回顶部
首页 > 资讯 > 移动开发 >Android自定义覆盖层控件 悬浮窗控件
  • 235
分享到

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

Android 2022-06-06 04:06:27 235人浏览 泡泡鱼
摘要

在我们移动应用开发过程中,偶尔有可能会接到这种需求: 1、在手机桌面创建一个窗口,类似于360的悬浮窗口,点击这个窗口可以响应(至于窗口拖动我们可以后面再扩展)。 2、自己

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

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; 
 
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(); 
 } 
  
 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); 
 } 
  
 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); 
 } 
  
 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; 
 
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(); 
 } 
  
 public void updateWindowLayout(float offsetX, float offsetY){ 
  floatWindow.updateWindowLayout(offsetX,offsetY); 
 }; 
} 

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

附件:Android浮动窗口

您可能感兴趣的文章:Android RecycleView使用(CheckBox全选、反选、单选)Android使用RecycleView实现拖拽交换item位置Android 中RecycleView实现item的点击事件Android ScrollView实现向上滑动控件顶部悬浮效果浅谈Android应用内悬浮控件实践方案总结Android 实现控件悬浮效果实例代码Android开发之基于RecycleView实现的头部悬浮控件


--结束END--

本文标题: Android自定义覆盖层控件 悬浮窗控件

本文链接: https://www.lsjlt.com/news/22055.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android自定义覆盖层控件 悬浮窗控件
    在我们移动应用开发过程中,偶尔有可能会接到这种需求: 1、在手机桌面创建一个窗口,类似于360的悬浮窗口,点击这个窗口可以响应(至于窗口拖动我们可以后面再扩展)。 2、自己...
    99+
    2022-06-06
    Android
  • 怎么中Android中自定义一个悬浮窗控件
    今天就跟大家聊聊有关怎么中Android中自定义一个悬浮窗控件,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。第一步设计类似Toast的类FloatWindowpackage ...
    99+
    2023-05-31
    android roi %d
  • Android自定义控件之自定义组合控件(三)
    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件基本原理详解(一)、Android自定义控件之自定义属性(二)。今天重点介绍一下如何通过自定义组合控件来提高布...
    99+
    2022-06-06
    Android
  • Android自定义控件之自定义属性(二)
    前言: 上篇介绍了自定义控件的基本要求以及绘制的基本原理,本篇文章主要介绍如何给自定义控件自定义一些属性。本篇文章将继续以上篇文章自定义圆形百分比为例进行讲解。有关原理知识请参...
    99+
    2022-06-06
    属性 自定义属性 Android
  • android 自定义控件 使用declare
    在Android中,可以使用`declare-styleable`来定义和使用自定义控件的属性。下面是一个简单的示例:1. 在res...
    99+
    2023-09-21
    Android
  • android自定义View之复合控件
    复合控件可以很好地创建出具有重用功能的控件集合。 很多的APP都有一些共通的UI界面,为了统一应用程序的风格,下面我们就以一个Topbar为实例讲解复合控件。 实现效果如图: 第一...
    99+
    2022-11-12
  • Android自定义实现日历控件
    本文实例为大家分享了Android自定义实现日历控件的具体代码,供大家参考,具体内容如下 1. Calendar类 2. 布局 创建calendar_layout.xml <...
    99+
    2022-11-12
  • Android自定义星星评分控件
    下面为控件的实现历程: 此控件高效,直接使用ondraw绘制,先亮照: 由于Android自身的星星评分控件样式可以改,但是他的大小不好调整的缺点,只能用small n...
    99+
    2022-06-06
    Android
  • 详解Android自定义控件属性
    在Android开发中,往往要用到自定义的控件来实现我们的需求或效果。在使用自定义 控件时,难免要用到自定义属性,那怎么使用自定义属性呢? 在文件res/values/下新建...
    99+
    2022-06-06
    属性 Android
  • Android自定义播放器控件VideoView
    介绍 最近要使用播放器做一个简单的视频播放功能,开始学习VideoView,在横竖屏切换的时候碰到了点麻烦,不过在查阅资料后总算是解决了。在写VideoView播放视频时候定...
    99+
    2022-06-06
    videoview Android
  • Android自定义双向滑动控件
    本文实例为大家分享了Android自定义双向滑动控件的具体代码,供大家参考,具体内容如下 先看一下效果图 1.SeekBarPressure工具类 public class See...
    99+
    2022-11-13
  • Android自定义模拟时钟控件
    本文实例为大家分享了Android自定义模拟时钟控件的具体代码,供大家参考,具体内容如下 自定义view—透明模拟时钟显示 项目中要用到模拟时钟的显示,查了一些资料根据自...
    99+
    2022-11-12
  • Android入门篇自定义Button控件
    在Android中,可以通过继承Button类来创建自定义Button控件。下面是一个简单的例子,演示如何创建一个带有圆角背景和自定...
    99+
    2023-09-22
    Android
  • Android中怎么自定义Progress控件
    Android中怎么自定义Progress控件,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。主要就是需求就是椭圆进度,百分比跟随渐变背景,这样一想其实就是一个布局,然后控制...
    99+
    2023-05-31
    android progress
  • android怎么自定义组合控件
    要自定义一个组合控件,你可以按照以下步骤进行:1. 创建一个新的类,继承自现有的Android控件类,例如LinearLayout或...
    99+
    2023-08-09
    android
  • Android如何自定义评分控件
    今天小编给大家分享一下Android如何自定义评分控件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。自定义参数为了方便扩展,...
    99+
    2023-06-30
  • android怎么自定义开关控件
    要自定义开关控件,可以使用以下步骤:1. 创建一个自定义的开关控件类,继承自Switch或CompoundButton类。2. 在自...
    99+
    2023-08-16
    android
  • Android自定义控件实现简单的轮播图控件
    最近要做一个轮播图的效果,网上看了几篇文章,基本上都能找到实现,效果还挺不错,但是在写的时候感觉每次都要单独去重新在Activity里写一堆代码。于是自己封装了一下。本篇轮播图...
    99+
    2022-06-06
    轮播图 轮播 Android
  • android 自定义控件 自定义属性详细介绍
    自定义控件在android中无处不见,自定义控件给了我们很大的方便。比如说,一个视图为imageview ,imagebutton ,textview 等诸多控件的组合,用的地...
    99+
    2022-06-06
    自定义 自定义控件 属性 自定义属性 Android
  • Android怎么在XML文件中自定义控件
    今天小编给大家分享一下Android怎么在XML文件中自定义控件的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、为什么需要...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作