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" />

时间: 2024-07-28 22:01:42

Android 悬浮窗口的相关文章

Android悬浮窗口

FloatService: package com.home.floatwindow; import android.app.Service; import android.content.Context; import android.content.Intent; import android.graphics.PixelFormat; import android.os.IBinder; import android.util.Log; import android.view.Gravit

Android悬浮窗口的实现

效果图:(悬浮框可拖动) 在项目开发中有一个需求:弹出悬浮窗后,响应悬浮窗的事件再弹出对话框,但是对话框怎么也不显示.也就是说在弹出悬浮框的同时,不能再弹出对话框,可能的原因: 1.悬浮框的焦点在最前面,把对话框挡住了,我们看不到. 2.浮动框限制了对话框的弹出. 解决: 弹出对话框的时候把悬浮框关掉,然后对话框处理完了,把对话框关掉,在重新开启一个悬浮框,把需要的值传进去. 就相关知识详解: 当我们在手机上使用360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮动窗口可跳转到安全卫

Android中可自由移动悬浮窗口的实现

大家对悬浮窗概念不会陌生,相信每台电脑桌面的右上角都会有这么一个东西,它总是出现在所有页面的顶端(Top Show).但在Android平台中如何实现这样的效果呢?先来看一看效果图. 看见在Google搜索框上面的那个Icon图片了嘛.下面我就来详细介绍一下在Android平台下悬浮窗口的实现,并让它能够随手指的触摸而移动. 一.实现原理及移动思路 调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager的addView

Android使用WindowManager实现悬浮窗口

原文地址:http://www.3g-edu.org/news/art027.htm 下面就介绍一下如何通过WindowManager来实现这个效果. 通过WindowManager的addView()方法,并设置WindowManager.LayoutParams的相关属性,就可以往WindowManager中加入所需要的View,而根据WindowManager.LayoutParams属性不同,也就能实现不同的效果.比如创建系统顶级窗口,实现悬浮窗口效果.如果需要将View从WindowM

Android 实现顶层窗口、悬浮窗口

1.如图片1所示,在一个Android应用中,除了标题栏和底层的ActionBar的区域,是我们可以操纵的UI区域,那是不是说我们就不能改变除了这两个区域的UI呢?答案是否定的. 比如现在我们希望把一个View放在窗口的最低端显示,通过hierarchyviewer工具我们可以发现最底层的ActionBar是在TestActivity布局的父窗口中设置的,那么我们想通过setContentView(R.layout.activity_main)在activity_main布局文件中设置就无法实现

Android中悬浮窗口

调用WindowManager,并设置WindowManager.LayoutParams的相关属性,通过WindowManager的addView方法创建View,这样产生出来的View根据WindowManager.LayoutParams属性不同,效果也就不同了.比如创建系统顶级窗口,实现悬浮窗口效果!WindowManager的方法很简单,基本用到的就三个addView,removeView,updateViewLayout.而WindowManager.LayoutParams的属性就

android 类似360悬浮窗口实现源码

当我们在手机上安装360安全卫士时,手机屏幕上时刻都会出现一个小浮动窗口,点击该浮动窗口可跳转到安全卫士的操作界面,而且该浮动窗口不受其他activity的覆盖影响仍然可见(多米音乐也有相关的和主界面交互的悬浮小窗口).它能悬浮在手机桌面,且不受Activity界面的影响,说明该悬浮窗口是不隶属于Activity界面的,也就是说,他是隶属于启动它的应用程序所在进程.如360App所在的应用进程,当杀掉它所在的应用进程时,它才会消失.悬浮窗口的实现涉及到WindowManager(基于4.0源码分

【Anroid界面实现】通用的桌面悬浮窗口的实现(一)

转载请注明出处:http://blog.csdn.net/zhaokaiqiang1992 现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作.今天这篇文章,就是介绍如何实现桌面悬浮窗效果的. 首先,看一下效果图. 悬浮窗一共分为两个部分,一个是平常显示的小窗口,另外一个是点击小窗口显示出来的二级悬浮窗口. 首先,先看一下这个项目的目录结构. 最关键的就是红框内的四个类. 首先,FloatWindowService是一个后台的

Android悬浮窗实现 使用WindowManager

本文转载自: http://blog.csdn.net/stevenhu_223/article/details/8504058 悬浮窗口的实现涉及到WindowManager(基于4.0源码分析),它是一个接口,实现类有WindowManagerImpl,CompatModeWrapper(WindowManagerImpl的内部类),LocalWindowManager(Window的内部类),它们之间的关系如下图的类图: WindowManagerImpl: 1.是WindowManage