首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速。好,我们现在就来模拟实现一下类似的效果。

1.新建一个项目 , 打开activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.windowmanagerdemo.MainActivity" >
    
    <Button 
        android:id="@+id/btn_floatWindows"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:text="Start Float Window"
        />
</RelativeLayout>
在这里面,只有一个Button ,用来Activity开启悬浮窗服务.

2.打开MainActivity.java
public class MainActivity extends ActionBarActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        Button startFloatWindow=(Button) findViewById(R.id.btn_floatWindows);
        startFloatWindow.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent=new Intent(MainActivity.this, FloatWindowService.class);
                startService(intent);
                finish();
            }
        });
    }

}

里面的代码也很简单,  就是点击Button时跳转到FloatWindowService的服务.

3.接下来新建FloatWindowService.java
public class FloatWindowService extends Service {
    //用于在线程中创建或移除悬浮窗
    private Handler mh=new Handler();
    
    //定时器  定时检测当前应该创建还是移除悬浮窗
    private Timer timer;
    
    
    @Override
    public IBinder onBind(Intent arg0) {
        return null;
    }
    
    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        //开启定时器  每隔0.5秒刷新一次
        if(timer==null){
            timer=new Timer();
            timer.scheduleAtFixedRate(new RefreshTask(), 0, 500);  //做一个定时任务 每隔500毫秒执行一次
        }
        return super.onStartCommand(intent, flags, startId);
    }
    
    @Override
    public void onDestroy() {  //在Service被关闭时  同时关闭定时任务
        super.onDestroy();
        timer.cancel();
        timer=null;
    }
    
    
    class RefreshTask extends TimerTask{
        @Override
        public void run() {
            //当前界面是窗口 且没有悬浮窗显示, 则创建悬浮窗
            if(isHome()&&!MyWindowManager.isWindowShowing()){
                mh.post(new Runnable() {
                    @Override
                    public void run() {
                        MyWindowManager.createSmallWindow(getApplicationContext());
                    }
                });
            }
            //当前界面不是桌面 且有悬浮窗口显示  则隐藏悬浮窗口
            else if(!isHome()&&MyWindowManager.isWindowShowing()){
                mh.post(new Runnable() {
                    @Override
                    public void run() {
                        MyWindowManager.removeBigWindow(getApplicationContext());
                        MyWindowManager.removeSamllWindow(getApplicationContext());
                    }
                });
            }
            //如果当前是桌面  且有悬浮窗口显示 则更新内存数据
            else if(isHome()&&MyWindowManager.isWindowShowing()){
                mh.post(new Runnable() {
                    @Override
                    public void run() {
                        MyWindowManager.updateUserPercent(getApplicationContext());
                    }
                });
            }
        }
        
    }
    //判断当前界面是否是桌面
    private boolean isHome(){
        ActivityManager mActivityManager=(ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
        List<RunningTaskInfo> rti=mActivityManager.getRunningTasks(1);
        return getHomes().contains(rti.get(0).topActivity.getPackageName());
    }
    
    //获取属于桌面的应用包名称
    private List<String> getHomes(){
        List<String> names=new ArrayList<String>();
        PackageManager packManager=this.getPackageManager();
        Intent intent=new Intent(Intent.ACTION_MAIN);
        intent.addCategory(Intent.CATEGORY_HOME);
        List<ResolveInfo> resolveInfo=packManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo ri:resolveInfo) {
            names.add(ri.activityInfo.packageName);
        }
        return names;
    }
}

在这里的onStartCommand()方法中开启一个定时任务,这个定时任务就会每隔500毫秒检查一次悬浮窗的状况
在这里用到了一个MyWindowManager类用于管理悬浮窗.

4.新建一个MyWindowManager.java
public class MyWindowManager {
    //小窗口View的实例
    private static FloatWindowSmallView smallView;
    //大窗口View的实例
    private static FloatWindowBigView bigView;
    //小窗口View的参数
    private static LayoutParams smallViewParams;
    //大窗口View的参数
    private static LayoutParams bigViewParams;
    //用于在屏幕上添加或移除悬浮窗
    private static WindowManager mWindowManager;
    //获取手机可用内存
    private static ActivityManager mActivityManager;
    
    //创建一个小悬浮窗 初始位置为屏幕左边中间
    public static void createSmallWindow(Context context){
        WindowManager windowManager=getWindowManager(context);
        int screenWidth=windowManager.getDefaultDisplay().getWidth();
        int screenHeight=windowManager.getDefaultDisplay().getHeight();
        if(smallView==null){
            smallView=new FloatWindowSmallView(context);
            if(smallViewParams==null){
                smallViewParams=new LayoutParams();
                smallViewParams.type=LayoutParams.TYPE_PHONE;
                smallViewParams.format=PixelFormat.RGBA_8888;
                smallViewParams.flags=LayoutParams.FLAG_NOT_TOUCH_MODAL|LayoutParams.FLAG_NOT_FOCUSABLE;
                smallViewParams.gravity=Gravity.LEFT|Gravity.TOP;
                smallViewParams.width=FloatWindowSmallView.viewWidth;
                smallViewParams.height=FloatWindowSmallView.viewHeight;
                smallViewParams.x=screenWidth;
                smallViewParams.y=screenHeight/2;
            }
            smallView.setParams(smallViewParams);
            windowManager.addView(smallView, smallViewParams);
        }
    }
    //将小窗口从屏幕上移除
    public static void removeSamllWindow(Context context){
        if(smallView!=null){
            WindowManager windowManager=getWindowManager(context);
            windowManager.removeView(smallView);
            smallView=null;
        }
    }
    
    //创建一个大悬浮窗  位于屏幕正中间
    public static void createBigWindow(Context context){
        WindowManager windowManager=getWindowManager(context);
        int screenWidth=windowManager.getDefaultDisplay().getWidth();
        int screenHeight=windowManager.getDefaultDisplay().getHeight();
        if(bigView==null){
            bigView=new FloatWindowBigView(context);
            if(bigViewParams==null){
                bigViewParams = new LayoutParams();
                bigViewParams.x = screenWidth / 2 - FloatWindowBigView.viewWidth / 2;
                bigViewParams.y = screenHeight / 2 - FloatWindowBigView.viewHeight / 2;
                bigViewParams.type = LayoutParams.TYPE_PHONE;
                bigViewParams.format = PixelFormat.RGBA_8888;
                bigViewParams.gravity = Gravity.LEFT | Gravity.TOP;
                bigViewParams.width = FloatWindowBigView.viewWidth;
                bigViewParams.height = FloatWindowBigView.viewHeight;
            }
            windowManager.addView(bigView, bigViewParams);
        }
    }
    
    //将大悬浮窗口从屏幕上移除
    public static void removeBigWindow(Context context){
        if(bigView!=null){
            WindowManager windowManager=getWindowManager(context);
            windowManager.removeView(bigView);
            bigView=null;
        }
    }
    
    //更新小悬浮窗口TextView上的数据
    public static void updateUserPercent(Context context){
        if(smallView!=null){
            TextView percentView=(TextView) smallView.findViewById(R.id.percent);
            percentView.setText(getUserdPercentValue(context));
        }
    }
    
    
    //如果windowManager还未创建,则创建一个新的WindowManager返回  否则返回已经创建的WindowManager
    private static WindowManager getWindowManager(Context context){
        if(mWindowManager==null){
            mWindowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        }
        return mWindowManager;
    }
    //如果ActivityManager还未创建,则创建一个新的ActivityManager返回 否则返回已经创建了的ActivityManager
    private static ActivityManager getActivityManager(Context context){
        if(mActivityManager==null){
            mActivityManager=(ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
        }
        return mActivityManager;
    }
    
    //获取当前可用内存
    private static long getAvaliableMemory(Context context){
        ActivityManager.MemoryInfo mi=new MemoryInfo();
        getActivityManager(context).getMemoryInfo(mi);
        return mi.availMem;
    }
    
    //计算以使用的内存百分比
    public static String getUserdPercentValue(Context context){
        String dir="/proc/meminfo";
        try {
            FileReader fr=new FileReader(dir);
            BufferedReader br=new BufferedReader(fr);
            String memoryLine=br.readLine();
            String subMemoryLine=memoryLine.substring(memoryLine.indexOf("MemTotal:"));
            br.close();
            long totalMemorySize=Integer.parseInt(subMemoryLine.replaceAll("\\D+", ""));
            long availableSize=getAvaliableMemory(context)/1024;
            int precent=(int) ((totalMemorySize - availableSize) / (float) totalMemorySize * 100);  
            return precent+"%";
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return "悬浮窗";
    }
    
    //是否有悬浮窗口
    public static boolean isWindowShowing(){
        return smallView!=null||bigView!=null;
    }
}
这个方法中包含了很多对悬浮窗的管理方法 ,有添加悬浮窗,删除悬浮窗,更新悬浮窗.
但这个方法里面用到了两个悬浮窗的对象.

5.新建FloatWindowSmallView.java   小悬浮窗的类
public class FloatWindowSmallView extends LinearLayout {
    //记录小窗口的宽度
    public static int viewWidth;
    //记录小窗口的高度
    public static int viewHeight;
    //记录系统状态栏高度
    private static int statusBarHeight;
    //用于更新小悬浮窗位置
    private WindowManager windowManager;
    //小悬浮窗的参数
    private WindowManager.LayoutParams mParams;
    //记录当前手指在屏幕上的横坐标值
    private float xInScreen;
    //记录当前手指在屏幕上的纵坐标值
    private float yInScreen;
    //记录手指在屏幕上按下的横坐标
    private float xDownInScreen;
    //记录手指在屏幕上按下的纵坐标
    private float yDownInScreen;
    //记录手指按下时小悬浮窗的横坐标
    private float xInView;
    //记录手指按下时小悬浮窗的纵坐标
    private float yInView;
    
    
    public FloatWindowSmallView(Context context) {
        super(context);
        windowManager=(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater.from(context).inflate(R.layout.float_windows_small, this);
        View view=findViewById(R.id.small_layout);
        viewHeight=view.getLayoutParams().height;
        viewWidth=view.getLayoutParams().width;
        TextView parcentView=(TextView) findViewById(R.id.percent);
        parcentView.setText(MyWindowManager.getUserdPercentValue(context));
    }
    
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
            //手指按下时记录必要的数据  纵坐标的值都需要减去状态栏的高度
            xInView=event.getX();
            yInView=event.getY();  //触摸点相对于控件的左上角位置
            xDownInScreen=event.getRawX();   //触摸点相对于这个屏幕左上角位置
            yDownInScreen=event.getRawY()-getStatusBarHeight();
            xInScreen=event.getRawX();
            yInScreen=event.getRawY()-getStatusBarHeight();
            break;
        case MotionEvent.ACTION_MOVE:
            xInScreen=event.getRawX();
            yInScreen=event.getRawY()-getStatusBarHeight();
            //手指移动时更新悬浮窗的位置
            updateViewPosition();
            break;
        case MotionEvent.ACTION_UP:
            //如果手指离开屏幕  xDownInScreen和xInScreen相同 且 yDownInScreen和yInScreen相等,则视为触发了单机事件
            if(xDownInScreen==xInScreen&&yDownInScreen==yInScreen){
                openBigWindow();
            }
            break;
        default:
            break;
        }
        return true;
    }
    
    //将小悬浮窗的参数传入     用于更新小悬浮窗的位置
    public void setParams(WindowManager.LayoutParams params){
        mParams=params;
    }
    
    //打开大悬浮窗 关闭小悬浮窗
    private void openBigWindow(){
        MyWindowManager.createBigWindow(getContext());
        MyWindowManager.removeSamllWindow(getContext());
    }
    //更新小悬浮窗在屏幕中的位置
    private void updateViewPosition(){
        //在移动的过程中  需要减去触摸点相对于控件的位置   否则就会出现移动时 窗口跟着左上角走, 而不是正中间
        mParams.x=(int) (xInScreen-xInView);
        mParams.y=(int) (yInScreen-yInView);
        windowManager.updateViewLayout(this, mParams);
    }
    
    
    //获取状态栏的高度
    private int getStatusBarHeight(){
        if(statusBarHeight==0){
            try {
                Class<?> c=Class.forName("com.android.internal.R$dimen");
                Object o=c.newInstance();
                Field field=c.getField("status_bar_height");
                int x=field.getInt(o);
                statusBarHeight=getResources().getDimensionPixelSize(x);
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }
}
在小悬浮窗中动态的显示当前手机所占用的内存,并且可以让用户拖拽窗口,同时实现可以点击效果,当用户点击小悬浮窗时,开启一个大悬浮窗.

6.新建FloatWindowBigView.java  大悬浮窗的类
public class FloatWindowBigView extends LinearLayout {
    //记录大悬浮窗口的宽度
    public static int viewWidth;
    //记录最大悬浮窗的高度
    public static int viewHeight;
    
    
    public FloatWindowBigView(final Context context) {
        super(context);
        LayoutInflater.from(context).inflate(R.layout.float_windows_big,this);
        View view=findViewById(R.id.big_layout);
        viewWidth=view.getLayoutParams().width;
        viewHeight=view.getLayoutParams().height;
        Button close=(Button) findViewById(R.id.close);
        Button back=(Button) findViewById(R.id.back);
        close.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                MyWindowManager.removeBigWindow(context);
                MyWindowManager.removeSamllWindow(context);
                Intent intent=new Intent(getContext(), FloatWindowService.class);
                context.stopService(intent);
            }
        });
        back.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                MyWindowManager.removeBigWindow(context);
                MyWindowManager.createSmallWindow(context);
            }
        });
    }
}

大悬浮窗 里面有两个按钮  一个关闭按钮,按下后关闭服务,定时任务取消,去除大悬浮和小悬浮窗口.

最后,还需要在AndroidManifest.xml文件中加入权限.
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

<uses-permission android:name="android.permission.GET_TASKS"/>

以及注册悬浮窗的Service

<service android:name=".FloatWindowService"></service>   





























android桌面悬浮窗实现的更多相关文章

  1. Android 桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    首先是一个小的悬浮窗显示的是当前使用了百分之多少的内存,点击一下小悬浮窗,就会弹出一个大的悬浮窗,可以一键加速.好,我们现在就来模拟实现一下类似的效果. 先谈一下基本的实现原理,这种桌面悬浮窗的效果很 ...

  2. Android桌面悬浮窗效果实现,仿360手机卫士悬浮窗效果

    大家好,今天给大家带来一个仿360手机卫士悬浮窗效果的教程,在开始之前请允许我说几句不相干的废话. 不知不觉我发现自己接触Android已有近三个年头了,期间各种的成长少不了各位高手的帮助,总是有很多 ...

  3. android桌面悬浮窗仿QQ手机管家加速效果

    主要还是用到了WindowManager对桌面悬浮进行管理. 需要一个火箭的悬浮窗 一个发射台悬浮窗  ,判断火箭是否放到了发射台,如果放上了,则使用AsyTask 慢慢将火箭的图片往上移.结束后., ...

  4. Android仿腾讯手机管家实现桌面悬浮窗小火箭发射的动画效果

    功能分析: 1.小火箭游离在activity之外,不依附于任何activity,不管activity是否开启,不影响小火箭的代码逻辑,所以小火箭的代码逻辑是要写在服务中: 2.小火箭挂载在手机窗体之上 ...

  5. Android -- 桌面悬浮,QQ管家火箭实现

    续上一篇博客<Android -- 桌面悬浮,仿360>,传送门:http://www.cnblogs.com/yydcdut/p/3909888.html,在此代码上继续添加实现. 比起 ...

  6. 简易的可拖动的桌面悬浮窗效果Demo

    首先,我们需要知道,悬浮窗分为两种:Activity级别的悬浮窗,系统级别的悬浮窗 Activity级别的悬浮窗跟随所属Activity的生命周期而变化,而系统级别的悬浮窗则可以脱离Activity而 ...

  7. Android -- 桌面悬浮,仿360

    实现原理                                                                               这种桌面悬浮窗的效果很类似与Wid ...

  8. Android WindowManager悬浮窗:不需要申请权限实现悬浮

     Android WindowManager悬浮窗:不需要申请权限实现悬浮 附录文章1介绍了Android平台上的悬浮窗WindowManager,WindowManager悬浮窗可以悬浮在And ...

  9. Android 之 悬浮窗

    昨天研究Android的悬浮窗,遇到一个问题,研究了一天,总算找到结症了,原因非常坑人..... 问题是这样的,我想要将悬浮窗展现在桌面或其他应用之上,我的开发机子用的是MIUI,结果发现在机子上无论 ...

随机推荐

  1. zabbix服务端安装配置

    1.安装好httpd,mysql,php yum install httpd php mysql mysql-devel php-xmlwriter php-gd php-mbstring php-b ...

  2. CentOS 6.5通过yum安装 MySQL-5.5

    1.安装mysql-5.5的yum源 rpm -ivh http://repo.mysql.com/yum/mysql-5.5-community/el/6/x86_64/mysql-communit ...

  3. 复用传统C/S架构系统,升级成‘伪’B/S架构设计

    应用场景:已经部署了传统系统又想要移动方式的场景.安全性考虑要求高的场景(核心资源要求在企业内部的场景). 我们 做了如下的系统设计: 核心是我们利用了WS做了内外穿透的设计.

  4. 最小化 Java 镜像的常用技巧

    背景 随着容器技术的普及,越来越多的应用被容器化.人们使用容器的频率越来越高,但常常忽略一个基本但又非常重要的问题 - 容器镜像的体积.本文将介绍精简容器镜像的必要性并以基于 spring boot ...

  5. Python学习笔记:Matplotlib(数据可视化)

    Matplotlib是一个可以将数据绘制为图形表示的Python三方库,包括线性图(折线图,函数图).柱形图.饼图等基础而直观的图形,在平常的开发当中需要绘图时就非常有用了. 安装:pip insta ...

  6. 最短路径之迪杰斯特拉算法(Java)

    1)Dijkstra算法适用于求图中两节点之间最短路径 2)Dijkstra算法设计比较巧妙的是:在求源节点到终结点自底向上的过程中,源节点到某一节点之间最短路径的确定上(这也是我之前苦于没有解决的地 ...

  7. openwrt(三) 固件的烧录

    导航: 方法1: tftp: 方法2: 在线升级 方法3: BIOS烧录 方法1:TFTP 这应该是最万能的一种方法了.TFTP是一种依靠网口传送数据的一种通信协议,没错,只是传输数据,并不是烧录,所 ...

  8. POJ 3484 二分

    Showstopper Description Data-mining huge data sets can be a painful and long lasting process if we a ...

  9. Highest Tower 18中南多校第一场H题

    一.题意 给出N个方块,要求给出一个方案,使得1. 所有方块都被使用到(题目数据保证这点) 2.所有方块垒成一个塔,且上面的方块宽度小于下面的方块 3.每个方块只能用一次,可以横着或者竖着. n范围5 ...

  10. Git Pro Book

    目录 2nd Edition (2014) Switch to 1st Edition Download Ebook The entire Pro Git book, written by Scott ...