很感谢原文作者

http://blog.csdn.net/guolin_blog/article/details/8689140

经自己理解

程序执行界面例如以下图:

1.程序入口界面

2.小浮动窗体

watermark/2/text/aHR0cDovL2Jsb2cuY3Nkbi5uZXQvd2Vpd2VpNTkxMA==/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70/gravity/SouthEast" alt="">

3.大浮动窗体

由上图可看出,能够看出我们基本须要:

1.一个主Activity

2.小浮动窗体view界面

3.大浮动窗体view界面

对于浮动窗体的管理我们还须要

4.一个Service(在后台监控管理浮动窗体的状态)

5.窗体管理类(创建/消除浮动窗体)

代码:

package com.ww.activity;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button; import com.ww.service.FloatWindowService; /**
* Activity
* 程序主入口
*
* @author wangwei
* @Email 25501232@qq.com
*
*/
public class MainActivity extends Activity { private Button btnFloatWin; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
} private void init(){ btnFloatWin = (Button) findViewById(R.id.btnFloatWin);
btnFloatWin.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// 启动浮动窗体Service
Intent intent = new Intent(MainActivity.this, FloatWindowService.class);
startService(intent);
finish();
}
}); } @Override
protected void onResume() {
super.onResume(); } /**
* 打印日志
* @param msg
*/
private static void log(String msg) {
Log.i("Test", msg);
} }
package com.ww.view;

import android.content.Context;
import android.graphics.Rect;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView; import com.ww.activity.R;
import com.ww.bean.MyWindowManager; /**
* 小浮动窗体视图
* 小浮动窗体可在屏幕上自由拖动(除状态栏部分)
* @author wangwei
*
*/
public class FloatWindowSmallView extends LinearLayout { // 小浮动窗体(视图)宽、小浮动窗体(视图)高、状态栏高
public static int viewWidth, viewHeigth, statusBarHeight; // android 窗体管理器
private WindowManager windowManager;
// 窗体管理器參数
private WindowManager.LayoutParams mParams; // 移动时相应屏幕的x,y坐标。
private float xInScreen, yInScreen;
// 按下时相应屏幕的x,y坐标;
private float xDownInScreen, yDownInScreen;
// 按下时相应small_window_layout View中的x,y坐标
private float xInView, yInView; TextView percentView; public FloatWindowSmallView(Context context) {
super(context);
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
/*
* 查找res/layout/下的XML文件
* 对于一个没有被加载或者想要动态加载的界面,使用LayoutInflater.inflate()来加载
*/
LayoutInflater.from(context).inflate(R.layout.float_window_small, this);
View view = findViewById(R.id.small_window_layout); /*
* viewWidth 计算方式
* float_window_small ID:small_window_layout的 width heigth 为 60dp 25dp
* 简单计算出像素方法,屏幕密度为160的设备 1dp=1px
* 我当前使用模拟器像素为480*800 屏幕密度为240(屏幕密度计算方式 根号内 长(像素)平方 + 宽(像素)平方 除 屏幕英寸 )
* 240/160=1.5
* 所以相应的像素为 60dp * 1.5 = 90px
*
* viewHeight 计算方式(与上同样)
* 25dp * 1.5 = 37.5px
*/
viewWidth = view.getLayoutParams().width;
viewHeigth = view.getLayoutParams().height; percentView = (TextView) findViewById(R.id.tvPercent);
percentView.setText("XXX");
} /**
* 触摸事件
* 小浮动窗体
* 1.按下时 获取各种x,y坐标
* >> 获取view中的x,y坐标
* 获取按下时在屏幕中的x,y坐标
* 获取移动时在屏幕中的x,y坐标
*
* 2.移动时
* >> 更新view的位置
*
* 3.松开时
* >> 推断按下与移动的x,y坐标是否相等。假设相等表示未移动view位置,打开大的浮动窗体
*
*
*/
@Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 1
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:
// 2
xInScreen = event.getRawX();
// 不能移动到状态栏地方
yInScreen = event.getRawY() - getStatusBarHeight(); updateViewPosition();
break; case MotionEvent.ACTION_UP:
// 3
if(xDownInScreen == xInScreen && yDownInScreen == yInScreen){
openBigWindow();
}
break;
default:
break;
}
return true;
} /**
* 设置viewLayout的參数
*
* @param params
*/
public void setParams(WindowManager.LayoutParams params){
mParams = params;
} /**
* 更新view位置
* 主要用来设置x,y坐标,用来改变view位置
*/
private void updateViewPosition(){
mParams.x = (int) (xInScreen - xInView);
mParams.y = (int) (yInScreen - yInView);
windowManager.updateViewLayout(this, mParams); } /**
* 打开大窗体
* 移动小窗体视图
*/
private void openBigWindow(){
MyWindowManager.createBigWindow(getContext());
MyWindowManager.removeSmallWindow(getContext());
} /**
* 获取状态栏高度
* 这里包括了两种获取方式
* 1.使用反射方式获取内部API
* 2.使用正常API接口获取(推荐)
*
* @return
*/
private int getStatusBarHeight(){
Rect frame = new Rect();
this.getWindowVisibleDisplayFrame(frame);
// 状态栏高度
statusBarHeight = frame.top; /*
* 获取应用的标题栏高度
* 因没有应用界面,所以以下代码会获取失败。在此项目中也无需获取该值
*/
// int contentTop = this.findViewById(Window.ID_ANDROID_CONTENT).getTop();
// int titleBarHeight = contentTop - top;
// log("titleBarHeight>>>"+titleBarHeight); /*
* 使用反射的方法调用内部API获取状态栏高度
* 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().getDimensionPixelOffset(x);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}*/
log("statusBarHeight >> "+statusBarHeight);
return statusBarHeight;
} /**
* 打印日志
* @param msg
*/
private static void log(String msg) {
Log.i("Test", msg);
}
}
<pre name="code" class="java">package com.ww.bean;

import java.io.BufferedReader;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException; import android.app.ActivityManager;
import android.content.Context;
import android.graphics.PixelFormat;
import android.graphics.Point;
import android.util.Log;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.TextView; import com.ww.activity.R;
import com.ww.view.FloatWindowBigView;
import com.ww.view.FloatWindowSmallView; /**
* 窗体管理
* @author wangwei
*
*/
public class MyWindowManager { // 大浮动窗体
private static FloatWindowSmallView smallWindow;
// 小浮动窗体
private static FloatWindowBigView bigWindow;
// 小浮动窗体參数、大浮动窗体參数
private static LayoutParams smallWindowParams, bigWindowParams;
// 窗体管理
private static WindowManager mWindowManager; private static ActivityManager mActivityManager; /**
* 创建小浮动窗体
* @param context
*/
public static void createSmallWindow(Context context){
WindowManager windowManager = getWindowManager(context);
/*
* 获取屏幕width和height 像素方法
* minSdkVersion 13 以上使用此方法
*/
Point p = new Point();
getWindowManager(context).getDefaultDisplay().getSize(p);
int screenWidth = p.x;
int screenHeigth = p.y; /*
* 还有一种获取屏幕width和height 像素方法(已过时)
*
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeigth = windowManager.getDefaultDisplay().getHeight();*/ if(smallWindow == null){
smallWindow = new FloatWindowSmallView(context);
if(smallWindowParams == null){
smallWindowParams = new LayoutParams();
// 它置于全部应用程序之上。状态栏之下
smallWindowParams.type = LayoutParams.TYPE_PHONE;
// 位图格式
smallWindowParams.format = PixelFormat.RGBA_8888;
/*
* 行为选项,默觉得 none
* 当前窗体能够获得焦点 | 不接受触摸屏事件
* 不接受浮动窗体之外的点击事件
*/
smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL | LayoutParams.FLAG_NOT_FOCUSABLE;
// 浮动窗体停靠
smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
// 浮动窗体宽
smallWindowParams.width = FloatWindowSmallView.viewWidth;
// 浮动窗体高
smallWindowParams.height = FloatWindowSmallView.viewHeigth;
// 浮动窗体x坐标
smallWindowParams.x = screenWidth;
// 浮动窗体y坐标
smallWindowParams.y = screenHeigth / 2;
}
smallWindow.setParams(smallWindowParams);
// 将小浮动窗体及浮动窗体參数加入视窗中
windowManager.addView(smallWindow, smallWindowParams);
} } /**
* 移动小浮动窗体
* @param context
*/
public static void removeSmallWindow(Context context){
if(smallWindow != null){
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(smallWindow);
smallWindow = null;
}
} /**
* 创建大浮动窗体
* @param context
*/
public static void createBigWindow(Context context){
WindowManager windowManager = getWindowManager(context);
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeigth = windowManager.getDefaultDisplay().getHeight();
if(bigWindow == null){
bigWindow = new FloatWindowBigView(context);
if(bigWindowParams == null){
/*
* 參数说明与创建小浮动窗体相似
* 详见createSmallWindow
*/
bigWindowParams = new LayoutParams();
bigWindowParams.x = screenWidth / 2 - FloatWindowBigView.viewWidth / 2;
bigWindowParams.y = screenHeigth / 2 - FloatWindowBigView.viewHeight / 2;
bigWindowParams.type = LayoutParams.TYPE_PHONE;
bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
bigWindowParams.width = FloatWindowBigView.viewWidth;
bigWindowParams.height = FloatWindowBigView.viewHeight;
}
windowManager.addView(bigWindow, bigWindowParams);
}
} /**
* 移动大浮动窗体
* @param context
*/
public static void removeBigWindow(Context context){
if(bigWindow != null){
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(bigWindow);
bigWindow = null;
}
} /**
* 更新内存使用率
* @param context
*/
public static void updateUsedPercent(Context context){
if(smallWindow != null){
TextView percentView = (TextView) smallWindow.findViewById(R.id.tvPercent);
percentView.setText(getUsedPercentValue(context));
}
} /**
* 推断小浮动窗体或大浮动窗体是否显示
* @return
*/
public static boolean isWindowShowing(){
return smallWindow !=null || bigWindow != null;
} /**
* 获取 WindowManager 对象
* @param context
* @return
*/
private static WindowManager getWindowManager(Context context){
if(mWindowManager == null){
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
return mWindowManager;
} /**
* 获取 ActivityManager 对象
* @param context
* @return
*/
private static ActivityManager getActivityManager(Context context){
if(mActivityManager == null){
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
return mActivityManager;
} /**
* 获取 内存 使用率
* @param context
* @return 内存占用百分比
*/
public static String getUsedPercentValue(Context context){
String dir = "/proc/meminfo";
try {
FileReader fr = new FileReader(dir);
BufferedReader br = new BufferedReader(fr, 2048);
String memoryLine = br.readLine();
String subMemoryLine = memoryLine.substring(memoryLine.indexOf("MemTotal"));
br.close();
// 总内存
long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll("\\D+", ""));
// 当前剩余内存
long availableSize = getAvailableMemory(context) / 1024; // log(totalMemorySize + "----" + availableSize);
// 已用内存百分比
int percent = (int)((totalMemorySize - availableSize) / (float)totalMemorySize * 100); return percent + "%";
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return "悬浮窗";
} /**
* 获取 当前剩余内存
*
* 想使用mi.totalMem获取总内存,但报错,不知道为何
*
* @param context
* @return
*/
private static long getAvailableMemory(Context context){
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
getActivityManager(context).getMemoryInfo(mi);
return mi.availMem;
} /**
* 打印日志
* @param msg
*/
private static void log(String msg) {
Log.i("Test", msg);
} }


package com.ww.service;

import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask; import android.app.ActivityManager;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.os.Handler;
import android.os.IBinder;
import android.util.Log; import com.ww.bean.MyWindowManager; /**
* Service
* 浮动窗体Service
* 使用定时任务监控管理浮动窗体的状态
* 大小浮动窗体的创建移动及窗体位置管理
*
* @author wangwei
*
*/
public class FloatWindowService extends Service { private Handler handler = new Handler(); private Timer timer; @Override
public IBinder onBind(Intent intent) {
return null;
} /**
* 启动服务(服务启动时)
* 调度定时任务
*/
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
if(timer == null){
timer = new Timer();
timer.scheduleAtFixedRate(new RefreshTask(), flags, startId);
}
return super.onStartCommand(intent, flags, startId);
} /**
* 服务注销(服务停止时)
* 1.取消定时任务高度
* 2.将timer定时器设置为null
*/
@Override
public void onDestroy() {
super.onDestroy();
timer.cancel();
timer = null;
} /**
*
* 定时任务执行线程
* 有三种情况
* 1.当前在HOME界面,而且浮动窗体(大窗体和小窗体)没有显示
* >> 创建小窗体
*
* 2.当前不在HOME界面。而且浮动窗体(大窗体或小窗体)已显示
* >> 移动小窗体或大窗体
*
* 3.当前在HOME界面,而且浮动窗体(大窗体或小窗体)已显示
* >> 更新窗体位置
*
* @author wangwei
* @date 2014-6-13
*/
class RefreshTask extends TimerTask{ @Override
public void run() {
if(isHome() && !MyWindowManager.isWindowShowing()){
// 1
handler.post(new Runnable() {
@Override
public void run() {
MyWindowManager.createSmallWindow(getApplicationContext());
}
});
}else if(!isHome() && MyWindowManager.isWindowShowing()){
// 2
handler.post(new Runnable() { @Override
public void run() {
MyWindowManager.removeSmallWindow(getApplicationContext());
MyWindowManager.removeBigWindow(getApplicationContext());
}
});
}else if(isHome() && MyWindowManager.isWindowShowing()){
// 3
handler.post(new Runnable() {
@Override
public void run() {
MyWindowManager.updateUsedPercent(getApplicationContext());
}
});
}
} } /**
* 是否在Home界面
* @return
*/
private boolean isHome(){
// Activity管理器
ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
/*
* 获得当前正在执行的任务
* 返回最多任务数
* mActivityManager.getRunningTasks(maxNum);
* 这里1就够了 得到的即为当前正在执行(可见)的任务
*/
List<RunningTaskInfo> listRti = mActivityManager.getRunningTasks(1);
return getHomes().contains(listRti.get(0).topActivity.getPackageName());
} /**
* 得到全部的Home界面
* @return Home应用的包名
*/
private List<String> getHomes(){
List<String> names = new ArrayList<String>();
// 包管理器
PackageManager packageManager = this.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
// 查找出属于桌面应用的列表
List<ResolveInfo> listRi = packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo ri : listRi) {
names.add(ri.activityInfo.packageName);
}
return names;
} /**
* 打印日志
* @param msg
*/
private static void log(String msg) {
Log.i("Test", msg);
} }

如须要原码,可在评论下留下邮箱地址

android 浮动窗体学习笔记及个人理解(仿360手机助手)的更多相关文章

  1. Android静默安装实现方案,仿360手机助手秒装和智能安装功能

    转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/47803149 之前有非常多朋友都问过我.在Android系统中如何才干实现静默安装 ...

  2. Java四种引用--《深入理解Java虚拟机》学习笔记及个人理解(四)

    Java四种引用--<深入理解Java虚拟机>学习笔记及个人理解(四) 书上P65. StrongReference(强引用) 类似Object obj = new Object() 这类 ...

  3. Java虚拟机内存溢出异常--《深入理解Java虚拟机》学习笔记及个人理解(三)

    Java虚拟机内存溢出异常--<深入理解Java虚拟机>学习笔记及个人理解(三) 书上P39 1. 堆内存溢出 不断地创建对象, 而且保证创建的这些对象不会被回收即可(让GC Root可达 ...

  4. Android安装器学习笔记(一)

    Android安装器学习笔记(一) 一.Android应用的四种安装方式: 1.通过系统应用PackageInstaller.apk进行安装,安装过程中会让用户确认 2.系统程序安装:在开机的时候自动 ...

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

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

  6. 仿360手机卫士界面效果android版源码

    仿360手机卫士界面效果android版,这个今天一大早在源码天堂的那个网站上看到了一个那个网站最新更新的一个源码,所以就分享给大学习一下吧,布局还挺不错的,而且也很简单的,我就不把我修改的那个分享出 ...

  7. android cocos2d-x for Android安装和学习笔记(请用adt-bundle21.1或以上导入)

    引用:http://weimingtom.iteye.com/blog/1483566 (20121108)注意:这篇文章用cdt编译ndk工程的内容已过时(现在可以用adt-bundle,避免配置繁 ...

  8. CSS学习笔记09 简单理解BFC

    引子 在讲BFC之前,先来看看一个例子 <!DOCTYPE html> <html lang="en"> <head> <meta cha ...

  9. 【学习笔记】深入理解js原型和闭包(0)——目录

    文章转载:https://www.cnblogs.com/wangfupeng1988/p/4001284.html 说明: 本篇文章一共16篇章,外加两篇后补的和一篇自己后来添加的学习笔记,一共19 ...

随机推荐

  1. List<T>排序

    List<Student> studentList = new List<Student>(); Student s = new Student(); s.Name = &qu ...

  2. 「 HDOJ P2227 」 Find the nondecreasing subsequences

    # 题目大意 就是找不下降子序列的个数. # 解题思路 一开始想着先离散化,然后再做个 $dp$,发现用 $dp$ 的话时间复杂度是 $\text{O}(n^2)$ 的,稳稳超时. 这里说说 $dp$ ...

  3. centos7.x设置静态IP

    本教程以centOs7.4为例: 1.点击虚拟机的[编辑]选项,选择[虚拟网络编辑器] 2.选择[VMnet8],然后点击[NAT设置] 3.记录[子网掩码]和[网关IP],后面会用到 4.进入终端, ...

  4. MYSQL数据库攻防与加固

    这是“官方”原本的模样搬过来的..写的很粗略啊.还有篇详细的请查看:MySQL安全加固题目及答案参考解析 启动xserver-mysql,进入xserver-mysql,开始实验,实验步骤如下: 1. ...

  5. 条款27:尽量少做转型动作(Minimize casting)

    NOTE : 1.如果可以,尽量避免转型,特别是在注重效率的代码中避免dynamic_casts. 如果有个设计需要转型动作,试着发展无需转型的替代设计. 2.如果转型是必须要的,试着将它隐藏于某个函 ...

  6. Java:清空文件内容

    文章来源:https://www.cnblogs.com/hello-tl/p/9139432.html import java.io.*; public class FileBasicOperati ...

  7. 跟http相关的

    http http 中     请求: 请求行    请求方式   采用协议   版本号     网址    请求头    客户端可以接受数据类型    可以接受语言     可以接受的压缩格式 请求 ...

  8. HDU 6216 A Cubic number and A Cubic Number(数学/二分查找)

    题意: 给定一个素数p(p <= 1e12),问是否存在一对立方差等于p. 分析: 根据平方差公式: 因为p是一个素数, 所以只能拆分成 1*p, 所以 a-b = 1. 然后代入a = b + ...

  9. 3.3.2 使用 cut 选定字段

        cut 命令是用来剪下文本文件里的数据,文本文件可以是字段类型或是字符类型.后一种数据类型在遇到需要从文件里剪下特定的列时,特别方便.请注意:一个制表字符在此被视为单个字符.          ...

  10. python023 Python3 标准库概览

    Python3 标准库概览 操作系统接口 os模块提供了不少与操作系统相关联的函数. >>> import os >>> os.getcwd() # 返回当前的工作 ...