仿360安全卫士悬浮窗
虽然360安全卫士很流氓,但是我相信安装的人不在少数,它有一个让人很忧伤的功能,就是即时你关闭了360安全卫士,你手机的左边距或者右边距会有一个虫虫一样的东西,美其名曰:帮助用户清理内存的,今天我们就来讲一下360悬浮窗怎么玩,还是先贴图:
虽然丑了点,但主要在功能,想做漂亮,你们自己找美工P图吧! 说道悬浮窗,大家可能想到很多方法可以实现,但是怎样才能让它在退出Activity之后依然存在呢,首先还是讲思路,我们需要解决三大问题: 一、我们必须要想到一个载体,类似状态栏中的通知,是不是想到了,这个载体就是service,service的生命周期保证了悬浮窗不会随着Activity退出而被干掉。 二、我们必须要保证悬浮窗能够显示在launch上,这里我给大家推荐一个好东西,WindowManager,他能帮助你很好的解决这个问题。 三、如何获取内存使用信息,这一点我已经在上一章讲过,就不多说了,自己去看:内存获取 问题解决了,我们就开始贴代码部分吧(直接复制可用):
public class FwService extends Service {
private static ActivityManager mActivityManager;
//定义浮动窗口布局
LinearLayout mFloatLayout;
WindowManager.LayoutParams wmParams;
//创建浮动窗口设置布局参数的对象
static WindowManager mWindowManager;
Button mFloatView;
private static final String TAG = "FwService";
@Override
public void onCreate() {
// TODO Auto-generated method stub
super.onCreate();
Log.i(TAG, "oncreat");
createFloatView();
//Toast.makeText(FxService.this, "create FxService", Toast.LENGTH_LONG);
}
@Override
public IBinder onBind(Intent intent) {
// TODO Auto-generated method stub
return null;
}
private int width, height;
private void createFloatView() {
wmParams = new LayoutParams();
mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE);
//获取屏幕的高度
DisplayMetrics dm = new DisplayMetrics();
mWindowManager.getDefaultDisplay().getMetrics(dm);
width = dm.widthPixels;
height = dm.heightPixels;
//设置window type
wmParams.type = LayoutParams.TYPE_PHONE;
//设置图片格式,效果为背景透明
wmParams.format = PixelFormat.RGBA_8888;
//设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作)
wmParams.flags = LayoutParams.FLAG_NOT_FOCUSABLE | LayoutParams.FLAG_NOT_TOUCH_MODAL;
//调整悬浮窗显示的停靠位置为右侧侧置顶,方便实现触摸滑动
wmParams.gravity = Gravity.LEFT | Gravity.TOP;
// 以屏幕左上角为原点,设置x、y初始值
wmParams.x = width;
wmParams.y = height/2;
//设置悬浮窗口长宽数据
wmParams.width = LinearLayout.LayoutParams.WRAP_CONTENT;
wmParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
LayoutInflater inflater = LayoutInflater.from(getApplication());
//获取浮动窗口视图所在布局
mFloatLayout = (LinearLayout) inflater.inflate(R.layout.float_layout, null);
//添加mFloatLayout
mWindowManager.addView(mFloatLayout, wmParams);
mFloatView = (Button) mFloatLayout.findViewById(R.id.float_id);
// int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
// int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
// //设置layout大小
// mFloatLayout.measure(w, h);
Log.i(TAG, "Width/2--->" + mFloatView.getMeasuredWidth() / 2);
Log.i(TAG, "Height/2--->" + mFloatView.getMeasuredHeight() / 2);
mFloatView.setText(getUsedPercentValue(getApplicationContext()));
//设置监听浮动窗口的触摸移动
mFloatView.setOnTouchListener(new OnTouchListener() {
float dx, dy, mx, my;
float moveX, moveY;
//这里用于up和move不冲突
boolean isMove;
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
//getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//记住手按下的位置
dx = event.getRawX();
dy = event.getRawY();
// mx = v.getWidth()/2;
// my = v.getHeight()/2;
//计算手相对控件本身按下的位置
mx = event.getX();
my = event.getY();
isMove = false;//这里需要设置默认值为false,避免up部分出bug
return false;
case MotionEvent.ACTION_MOVE:
//计算手移动的距离
int x = Math.abs((int) (event.getRawX() - dx));
int y = Math.abs((int) (event.getRawY() - dy));
//如果x和y距离都小于5,说明用户并没打算移动,只是手触摸时产生的move
if (x < 5 || y < 5) {
isMove = false;
return false;
} else {
isMove = true;
}
//计算控件移动的距离
x = (int) (event.getRawX() - mx);
y = (int) (event.getRawY() - my);
wmParams.x = x;
//25为状态栏的高度
wmParams.y = y;
//刷新
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
return true;
case MotionEvent.ACTION_UP:
//这里要注意边界问题
float finalX = event.getRawX();
float finalY = event.getRawY();
//控制上边距
if (finalY < v.getHeight()) {
moveX = 0;
moveY = finalX - my;
}
//下边距
if (finalY > height - v.getHeight()) {
moveX = finalX - mx;
moveY = finalY - v.getHeight();
}
//判断控件改停留在左边距还是右边距
if (finalX - v.getWidth() / 2 < width / 2) {
moveX = 0;
moveY = finalY - my;
} else if (finalX - v.getWidth() / 2 > width / 2) {
moveX = width - v.getWidth();
moveY = finalY - my;
}
wmParams.x = (int) moveX;
wmParams.y = (int) moveY;
if (isMove) {
mWindowManager.updateViewLayout(mFloatLayout, wmParams);
}
return isMove;//false为down,true为move
default:
break;
}
//这里return ture,说明本次事件已经被处理,不会传给父亲
return false;
}
});
mFloatView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
Toast.makeText(FwService.this, "onClick", Toast.LENGTH_SHORT).show();
}
});
}
/**
* 计算已使用内存的百分比,并返回。
*
* @param context
* 可传入应用程序上下文。
* @return 已使用内存的百分比,以字符串形式返回。
*/
public static String getUsedPercentValue(Context context) {
//内存信息文件(CPU信息文件:/proc/cpuinfo)这两个文件是linux系统用来存储内存和CPU信息的
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();
//获取总的内存,这里需要注意的是replaceAll支持正则表达式"\\D"代表所有的字母字符,只保留数字部分
long totalMemorySize = Integer.parseInt(subMemoryLine.replaceAll("\\D+", ""));
//获取当前可用内存
long availableSize = getAvailableMemory(context) / 1024;
int percent = (int) ((totalMemorySize - availableSize) / (float) totalMemorySize * 100);
return percent + "%";
} catch (IOException e) {
e.printStackTrace();
}
return "悬浮窗";
}
/**
* 获取当前可用内存,返回数据以字节为单位。
*
* @param context
* 可传入应用程序上下文。
* @return 当前可用内存。
*/
private static long getAvailableMemory(Context context) {
ActivityManager.MemoryInfo mi = new ActivityManager.MemoryInfo();
getActivityManager(context).getMemoryInfo(mi);
long currentMemory = mi.availMem;
return currentMemory;
}
/**
* 如果ActivityManager还未创建,则创建一个新的ActivityManager返回。否则返回当前已创建的ActivityManager。
*
* @param context
* 可传入应用程序上下文。
* @return ActivityManager的实例,用于获取手机可用内存。
*/
private static ActivityManager getActivityManager(Context context) {
if (mActivityManager == null) {
mActivityManager = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
}
return mActivityManager;
}
@Override
public void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if (mFloatLayout != null) {
mWindowManager.removeView(mFloatLayout);
}
}
}
至于如何调用,那就再简单不过了:
Intent intent = new Intent(MainActivity.this, FwService.class);
startService(intent);
讲到这里就基本讲完了,主要还是思路,下一章我将为大家讲述仿360悬浮窗的进阶篇,代码部分会更优化。我的博客只要是功能模块的,都会给源码,看完博客内容的当然是免费,如果想奉献点积分给我,也可以去我的CSDN下载栏去下载