android 360浮动窗口,android 浮动窗口学习笔记及个人了解(仿360手机助手)

本文档详细介绍了如何在Android平台上实现浮动窗口,包括小窗口和大窗口的创建、移动以及状态管理。通过Service在后台监控浮动窗口,并提供触摸事件处理以实现窗口的拖动。同时,还涉及到了窗口尺寸的计算、布局参数设置以及窗口位置的更新。此外,代码示例展示了如何在不同场景下动态创建和移除浮动窗口。
摘要由CSDN通过智能技术生成

android 浮动窗口学习笔记及个人理解(仿360手机助手)

非常感谢原文作者

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

经自己理解

程序运行界面如下图:

1.程序入口界面

103026121.jpg

2.小浮动窗口

103026122.jpg

3.大浮动窗口

103026123.jpg

由上图可看出,可以看出我们基本需要:

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);

}

}

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 listRti = mActivityManager.getRunningTasks(1);

return getHomes().contains(listRti.get(0).topActivity.getPackageName());

}

/**

* 得到所有的Home界面

* @return Home应用的包名

*/

private List getHomes(){

List names = new ArrayList();

// 包管理器

PackageManager packageManager = this.getPackageManager();

Intent intent = new Intent(Intent.ACTION_MAIN);

intent.addCategory(Intent.CATEGORY_HOME);

// 查找出属于桌面应用的列表

List 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);

}

}

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值