Android 悬浮窗口

一.创建悬浮窗口步骤
    1.实现一个ViewGroup类,作为悬浮窗口的界面类,以便在里面重写onInterceptTouchEvent和onTouchEvent方法,实现移动界面的目的.
      在本例中实现了一个FloatLayer类,可以作为通用的类,使用时需要传入WindowManager对象以实现移动窗口.

// FloatLayer ~
package com.example.hellofloatingwnd; import static com.ahai.util.DebugMessage.d;
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.RelativeLayout; public class FloatLayer extends RelativeLayout { // flags: 须设置成 FLAG_NOT_FOCUSABLE, 否则悬浮窗口下面的窗口不能取得焦点, 无法响应触摸事件
// type: 值低的窗口在值高的下层,相同的 type值,后创建的窗口显示在先创建的窗口上面.
// 对应的type需要相应的权限,否则会报异常 BadTokenException.
// 对于权限 android.permission.SYSTEM_ALERT_WINDOW 可使用以下几个值:
// TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY
// 其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能响应触摸事件
public static class FloatLayoutParams extends WindowManager.LayoutParams { public FloatLayoutParams() {
super(TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);
gravity = Gravity.LEFT | Gravity.TOP;
} public FloatLayoutParams(int type) {
super(type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);
gravity = Gravity.LEFT | Gravity.TOP;
} public FloatLayoutParams(int xpos, int ypos) {
super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,
ypos, TYPE_PHONE, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);
gravity = Gravity.LEFT | Gravity.TOP;
} public FloatLayoutParams(int xpos, int ypos, int type) {
super(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT, xpos,
ypos, type, FLAG_NOT_FOCUSABLE, PixelFormat.RGBA_8888);
gravity = Gravity.LEFT | Gravity.TOP;
}
} private WindowManager mWindowManager;
private int mStatusBarHeight;
private int mMoveX;
private int mMoveY; public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mStatusBarHeight = getStatusBarHeight();
} public FloatLayer(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mStatusBarHeight = getStatusBarHeight();
} public FloatLayer(Context context, AttributeSet attrs) {
super(context, attrs);
mStatusBarHeight = getStatusBarHeight();
} public FloatLayer(Context context) {
super(context);
mStatusBarHeight = getStatusBarHeight();
} public void setWindowManager(WindowManager windowManager) {
mWindowManager = windowManager;
} /** 取得系统状态栏的高度 */
private int getStatusBarHeight() {
int statusBarHeight = 0;
int resourceId = getResources().getIdentifier("status_bar_height",
"dimen", "android");
if (resourceId > 0) {
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
// d("statusBarHeight=" + statusBarHeight);
return statusBarHeight;
} @Override
public boolean onInterceptTouchEvent(MotionEvent event) { final int action = event.getAction();
if (action == MotionEvent.ACTION_MOVE) {
if (handleMoveEvent(event))
return true;
} else if (action == MotionEvent.ACTION_DOWN) {
mMoveX = (int) event.getRawX();
mMoveY = (int) event.getRawY();
}
return super.onInterceptTouchEvent(event);
} @Override
public boolean onTouchEvent(MotionEvent event) { final int action = event.getAction();
if (action == MotionEvent.ACTION_MOVE) {
if (handleMoveEvent(event))
return true;
} else if (action == MotionEvent.ACTION_DOWN) {
mMoveX = (int) event.getRawX();
mMoveY = (int) event.getRawY();
}
return super.onTouchEvent(event);
} private boolean handleMoveEvent(MotionEvent event) { try {
if (mWindowManager != null) { // 通过以下消息可知getLayoutParams得到的对象即为 addView 传入的 LayoutParams 对象
// d("class:" + getLayoutParams().getClass());
final int x = (int) event.getRawX();
final int y = (int) event.getRawY(); int[] location = new int[2];
getLocationOnScreen(location);
FloatLayoutParams layoutParams = (FloatLayoutParams) getLayoutParams();
layoutParams.x = location[0] + (x - mMoveX);
layoutParams.y = location[1] + (y - mMoveY) - mStatusBarHeight;
mWindowManager.updateViewLayout(this, layoutParams); mMoveX = x;
mMoveY = y;
return true;
}
} catch (Exception e) {
d("", e);
} return false;
}
}

2.在res/layout中创建一个布局文件,实现界面布局.如float_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<com.example.hellofloatingwnd.FloatLayer xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content" > <ImageButton
android:id="@+id/mBtnHide"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:background="@drawable/hide_button_selector" /> <TextView
android:id="@+id/mTvHello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/mBtnHide"
android:layout_centerHorizontal="true"
android:text="@string/hello"
android:textColor="#21d" /> </com.example.hellofloatingwnd.FloatLayer>

3.取得WindowManager对象.
        在 Activity 中可以通过以下方法取得, 其中前面的3个方法实际取得的是当前Activity的应用程序窗口对象,在Activity销毁等情况下,
    WindowManager对象也就不存在了,需要将悬浮窗口移除,否则会报错.
        WindowManager windowManager;
        windowManager = getWindow().getWindowManager();
        windowManager = getWindowManager();
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
        windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
        在 Service 中, 以下两个方法均可, 在 onDestory 中将悬浮窗口移除即可.
        windowManager = (WindowManager) getApplication().getSystemService(WINDOW_SERVICE);
        windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
   
    4.创建View并显示. 通过以下两个行代码完成:
        mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);
        windowManager.addView(mFloatView, layoutParams);
        其中layoutParams是实现悬浮窗口的关键,窗口的配置及移动都通过其指定.
        LayoutParams需使用WindowManager.LayoutParams对象或继承自该类重写.
        移动窗口通过下面的代码实现:
            mWindowManager.updateViewLayout(mFloatView, layoutParams);
        layoutParams最重要的两个参数是flags和type:
        flags: 须设置成 FLAG_NOT_FOCUSABLE, 否则悬浮窗口下面的窗口不能取得焦点,不能响应触摸事件.
        type: 值低的窗口在值高的下层,相同的 type值,后创建的窗口显示在先创建的窗口上面.
              对应的type需要相应的权限,否则会报异常 BadTokenException.
        对于权限 android.permission.SYSTEM_ALERT_WINDOW, type可使用以下几个值:
            TYPE_PHONE, TYPE_SYSTEM_ALERT, TYPE_TOAST, TYPE_SYSTEM_OVERLAY
        其中 TYPE_TOAST, TYPE_SYSTEM_OVERLAY 不能响应触摸事件

  在Service中实现

  

package com.example.hellofloatingwnd;

import android.app.Application;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.ImageButton;
import android.widget.Toast; public class FloatingService extends Service { private FloatLayer mFloatView;
private WindowManager mWindowManager;
private ImageButton mBtnHide; @Override
public void onCreate() {
super.onCreate(); Application app = getApplication(); mWindowManager = (WindowManager) app.getSystemService(WINDOW_SERVICE);
LayoutInflater inflater = LayoutInflater.from(app); mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);
mFloatView.setWindowManager(mWindowManager); mBtnHide = (ImageButton) mFloatView.findViewById(R.id.mBtnHide);
mBtnHide.setOnClickListener(mClickListener); FloatLayer.FloatLayoutParams layoutParams;
layoutParams = new FloatLayer.FloatLayoutParams(10, 100);
mWindowManager.addView(mFloatView, layoutParams);
} @Override
public void onDestroy() {
super.onDestroy();
if (mFloatView != null) {
mWindowManager.removeView(mFloatView);
mFloatView = null;
}
} @Override
public IBinder onBind(Intent arg0) {
return null;
} private OnClickListener mClickListener = new OnClickListener() {
@Override
public void onClick(View view) {
if (view.getId() == R.id.mBtnHide) {
Toast.makeText(getApplicationContext(),
"on float button clicked.", Toast.LENGTH_SHORT).show();
FloatingService.this.stopSelf();
}
}
};
}

  在Activity中实现

package com.example.hellofloatingwnd;

import android.app.Activity;
import android.app.Application;
import android.content.Intent;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.TextView; public class MainActivity extends Activity { private Button mBtnStart;
private Button mBtnStop; private FloatLayer mFloatView; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); mBtnStart = (Button) findViewById(R.id.mBtnStart);
mBtnStop = (Button) findViewById(R.id.mBtnStop); mBtnStart.setOnClickListener(mClickListener);
mBtnStop.setOnClickListener(mClickListener); WindowManager windowManager;
// windowManager = getWindow().getWindowManager();
windowManager = getWindowManager();
// windowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
// windowManager = (WindowManager)
// getApplication().getSystemService(WINDOW_SERVICE); Application application = getApplication();
LayoutInflater inflater = LayoutInflater.from(application); mFloatView = (FloatLayer) inflater.inflate(R.layout.float_layout, null);
mFloatView.setWindowManager(windowManager);
TextView textView = (TextView) mFloatView.findViewById(R.id.mTvHello);
textView.setText("This create by activity."); FloatLayer.FloatLayoutParams layoutParams;
layoutParams = new FloatLayer.FloatLayoutParams(50, 200);
windowManager.addView(mFloatView, layoutParams);
} @Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mFloatView != null) {
WindowManager windowManager = getWindow().getWindowManager();
windowManager.removeView(mFloatView);
mFloatView = null;
}
} private OnClickListener mClickListener = new OnClickListener() { @Override
public void onClick(View view) {
if (view.getId() == R.id.mBtnStart) {
Intent intent = new Intent(MainActivity.this,
FloatingService.class);
startService(intent);
} else if (view.getId() == R.id.mBtnStop) {
Intent intent = new Intent(MainActivity.this,
FloatingService.class);
stopService(intent);
}
}
};
}

5.在AndroidManifest.xml文件中添加权限,在layoutParams中配置不同的type类型需要不同的权限.
        <uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

Android 悬浮窗口的更多相关文章

  1. Android悬浮窗口的实现

    效果图:(悬浮框可拖动) 在项目开发中有一个需求:弹出悬浮窗后,响应悬浮窗的事件再弹出对话框,但是对话框怎么也不显示.也就是说在弹出悬浮框的同时,不能再弹出对话框,可能的原因: 1.悬浮框的焦点在最前 ...

  2. android悬浮窗口的一些说明

    1.xml文件里的权限申请 <uses-permission android:name="android.permission.SYSTEM_OVERLAY_WINDOW" ...

  3. android悬浮窗口

    悬浮窗原理 做过悬浮窗功能的人都知道, 要想显示悬浮窗, 要有一个服务运行在后台, 通过getSystemService(Context.WINDOW_SERVICE)拿到WindowManager, ...

  4. Android悬浮窗及其拖动事件

    主页面布局很简单,只有一个RelativelyLayout <?xml version="1.0" encoding="utf-8"?> <R ...

  5. Android中悬浮窗口

    调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager的addView方法创建View,这样产生出来的View根据Wind ...

  6. Android中悬浮窗口的实现原理和示例代码

    用了我一个周末的时间,个中愤懑就不说了,就这个问题,我翻遍全球网络没有一篇像样的资料,现在将实现原理简单叙述如下: 调用WindowManager,并设置WindowManager.LayoutPar ...

  7. Android之悬浮窗口实现(WindowManager)

    工作中遇到一些项目需要把窗体显示在最上层,像来电弹窗显示电话号码等信息.拦截短信信息显示给用户或者游戏中实现声音的调节,我们想这些数据放在最上层,activity就满足不了我们的需求了,有些开发者使用 ...

  8. Android 类似360悬浮窗口实现源码

    当我们在手机上安装360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮动窗口可跳转到安全卫士的操作界面,而且该浮动窗口不受其他activity的覆盖影响仍然可见(多米音乐也有相关的和主界面 ...

  9. Android WindowManager和WindowManager.LayoutParams的使用以及实现悬浮窗口的方法

    1.理清概念 我们使用过Dialog和PopupWindow,还有Toast,它们都显示在Activity之上.那么我们首先需要理解的是android中是如何去绘制这些UI的呢?这里我只讲我所理解的, ...

随机推荐

  1. [Clr via C#读书笔记]Cp9参数

    Cp9参数 可选参数和命名参数 参数设置了默认值(设置要从右到左,有默认值的参数必须放在没有默认值的参数的后面,默认值必须是常量),就可以使用可选参数和命名参数了.向方法传递实参的时候,编译器按照从左 ...

  2. 基于物品的协同过滤算法(ItemCF)

    最近在学习使用阿里云的推荐引擎时,在使用的过程中用到很多推荐算法,所以就研究了一下,这里主要介绍一种推荐算法—基于物品的协同过滤算法.ItemCF算法不是根据物品内容的属性计算物品之间的相似度,而是通 ...

  3. kosaraju求强连通分量

    在了解kosaraju算法之前我们先了解一下什么是强连通分量,在有向图中如果两个定点vi,ui存在一条路劲从vi到达ui且也存在一条路劲从ui到达vi那么由ui和vi这两个点构成的图成为强连通图,简洁 ...

  4. Python3 Tkinter-Scale

    1.创建 from tkinter import * root=Tk() Scale(root).pack() root.mainloop() 2.参数 from tkinter import * r ...

  5. SpringBoot在IDEA下使用JPA

    1依赖 使用IDEA构建基于JPA的项目需要引用JPA.MYSQL依赖 2配置文件修改 2.1连接库 spring.datasource.url=jdbc:mysql://localhost:3306 ...

  6. 2.hbase原理(未完待续)

    hbase简介相关概念hmsterhregionserver表regionhstorememstorestorefilehfileblockcacheWALminorcompactmajorcompa ...

  7. 团队Beta阶段事后分析

    团队Beta阶段事后分析 设想和目标 我们的软件要解决什么问题?是否定义得很清楚?是否对典型用户和典型场景有清晰的描述? 我们的软件要解决用户的休闲娱乐问题,为用户提供好玩的模拟经营类的游戏,游戏主题 ...

  8. C语言特殊符号

    -> ->在C语言中称为间接引用运算符,是二目运算符,优先级同成员运算符“.”.用法:p->a,其中p是指向一个结构体的指针,a是这个结构体类型的一个成员.表达式p->a引用了 ...

  9. 深入了解View的绘制流程

    1.  ViewRoot ViewRoot是连接WindowManager与DecorView的纽带,View的整个绘制流程的三大步(measure.layout.draw)都是通过ViewRoot完 ...

  10. 【MVC4升级到MVC5】ASP.Net MVC 4项目升级MVC 5的方法

    1.备份你的项目 2.从Web API升级到Web API 2,修改global.asax,将 ? 1 WebApiConfig.Register(GlobalConfiguration.Config ...