AndroidDemo - FloatWindowDemo
安卓悬浮窗Demo
<?xml verl sion="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="200dip"
android:layout_height="130dip"
android:id="@+id/big_window_layout"
android:background="@drawable/bg_blue"
android:orientation="vertical" >
<TextView
android:id="@+id/base"
android:layout_width="0dip"
android:layout_height="0dip"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
/>
<TextView
android:id="@+id/text1"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignRight="@+id/text2"
android:textColor="#00FFFF" />
<Button
android:id="@+id/btn_big_1"
android:layout_width="100dip"
android:layout_height="40dip"
android:layout_below="@id/base"
android:background="@android:color/transparent"
android:layout_centerHorizontal="true"
android:text="close" />
<TextView
android:id="@+id/text2"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/text1"
android:textColor="#00FFFF" />
<Button
android:id="@+id/btn_big_2"
android:layout_width="100dip"
android:layout_height="40dip"
android:background="@android:color/transparent"
android:layout_alignLeft="@+id/btn_big_1"
android:layout_alignTop="@+id/text2"
android:text="back" />
</RelativeLayout>
/****************************************************************************************************************************************/
以下为小窗口
<?xml version="1.0" encoding="UTF-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/small_window_layout"
android:layout_width="60dip"
android:layout_height="25dip"
android:background="@drawable/bg_small"
>
<TextView
android:id="@+id/percent"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center"
android:textColor="#ffffff"
/>
</LinearLayout>
放置一个TextView,用来显示内存使用百分率。
package com.rust.floatwindowdemo;
import java.lang.reflect.Field;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
public class WindowSmall extends LinearLayout{
private WindowManager windowManager;
private WindowManager.LayoutParams mParams;
private static int barHeight;
public static int viewWidth;
public static int viewHeight;
private float firstX;
private float firstY;
private float currentX;
private float currentY;
private float viewX;
private float viewY;
public WindowSmall(Context context) {
super(context);
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater.from(context).inflate(R.layout.window_small, this);
View view = findViewById(R.id.small_window_layout);
viewWidth = view.getLayoutParams().width;
viewHeight = view.getLayoutParams().height;
TextView memoryUsePercent = (TextView) findViewById(R.id.percent);
memoryUsePercent.setText(FloatWindowManager.getUsePercent(context));
}
public void setParams(WindowManager.LayoutParams params){
mParams = params;
}
@Override
public boolean onTouchEvent(MotionEvent event){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
viewX = event.getX();
viewY = event.getY();
firstX = event.getRawX();
firstY = event.getRawY() - getBarHeight();
currentX = event.getRawX();
currentY = event.getRawY() - getBarHeight();
break;
case MotionEvent.ACTION_MOVE:
currentX = event.getRawX();
currentY = event.getRawY() - getBarHeight();
freshSmallWindow();
break;
case MotionEvent.ACTION_UP:
if(firstX == currentX && firstY == currentY){
startBigWindow(getContext());
}
break;
}
return true;
}
/**
*
* @return 返回状态栏高度
*/
private int getBarHeight(){
if(barHeight == 0){
try{
Class<?> c = Class.forName("com.android.internal.R$dimen");
Object o = c.newInstance();
Field field = c.getField("status_bar_height");
int x = (Integer) field.get(o);
barHeight = getResources().getDimensionPixelSize(x);
}catch(Exception e){
e.printStackTrace();
}
}
return barHeight;
}
private void freshSmallWindow(){
mParams.x = (int) (currentX - viewX);
mParams.y = (int) (currentY - viewY);
windowManager.updateViewLayout(this, mParams);
}
private void startBigWindow(Context context){
FloatWindowManager.removeSmallWindow(getContext());
FloatWindowManager.createBigWindow(getContext());
}
}
package com.rust.floatwindowdemo;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;
public class WindowBig extends RelativeLayout{
private WindowManager windowManager;
private WindowManager.LayoutParams mParams;
private String strViewX;
private String strfirstX;
public static int bigViewWidth;
public static int bigViewHeight;
private static int screenWidth;
private static int screenHeight;
private float firstX;
private float firstY;
private float currentX;
private float currentY;
private float viewX;
private float viewY;
private int deltaX;
private int deltaY;
public WindowBig(final Context context) {
super(context);
windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
LayoutInflater.from(context).inflate(R.layout.window_big, this);
screenWidth = windowManager.getDefaultDisplay().getWidth();
screenHeight = windowManager.getDefaultDisplay().getHeight();
View view = findViewById(R.id.big_window_layout);
Button btn1 = (Button) findViewById(R.id.btn_big_1);//close all
Button btn2 = (Button) findViewById(R.id.btn_big_2);//close big window
bigViewWidth = view.getLayoutParams().width;
bigViewHeight = view.getLayoutParams().height;
deltaX = screenWidth/2 - bigViewWidth/2;
btn1.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
FloatWindowManager.removeBigWindow(context);
FloatWindowManager.removeSmallWindow(context);
Toast.makeText(context, "bye bye", Toast.LENGTH_SHORT).show();;
Intent intent = new Intent(getContext(),FloatWindowService.class);
context.stopService(intent);
}
});
btn2.setOnClickListener(new OnClickListener(){
@Override
public void onClick(View v) {
FloatWindowManager.removeBigWindow(context);
FloatWindowManager.createSmallWindow(context);
}
});
TextView tv1 = (TextView)findViewById(R.id.text1);
}
@Override
public boolean onTouchEvent(MotionEvent event){
switch(event.getAction()){
case MotionEvent.ACTION_DOWN:
viewX = event.getX();
viewY = event.getY();
firstX = event.getRawX();
firstY = event.getRawY();
currentX = event.getRawX();
currentY = event.getRawY();
// showXY();
break;
case MotionEvent.ACTION_MOVE:
currentX = event.getRawX();
currentY = event.getRawY();
break;
case MotionEvent.ACTION_UP:
if(viewX < 0 || viewX > bigViewWidth
|| viewY < 0 || viewY > bigViewHeight){
FloatWindowManager.removeBigWindow(getContext());
FloatWindowManager.createSmallWindow(getContext());
}
break;
}
return true;
}
private void showXY(){
strViewX = Float.toString(bigViewHeight);
TextView text1 = (TextView)findViewById(R.id.text1);
text1.setText(strViewX);
strfirstX = Float.toString(firstY);
TextView text2 = (TextView)findViewById(R.id.text2);
text2.setText(strfirstX);
}
}
onTouchEvent中判断手指点击屏幕的位置。当点击到大窗体之外的部分,会显示小窗口。
showXY()用来显示点击坐标。仅用于测试。
package com.rust.floatwindowdemo;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// setContentView(R.layout.activity_main);
Intent intent = new Intent(MainActivity.this , FloatWindowService.class);
startService(intent);
finish();
// Button startButton = (Button) findViewById(R.id.btn_1);
// startButton.setOnClickListener(new OnClickListener(){
//
// @Override
// public void onClick(View v) {
// Intent intent = new Intent(MainActivity.this , FloatWindowService.class);
// startService(intent);
//
// finish();
// }
//
// });
}
/****************************************************************************************************************************************/
package com.rust.floatwindowdemo;
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;
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) {
// 开启定时器,每隔0.5秒刷新一次
if (timer == null) {
timer = new Timer();
timer.scheduleAtFixedRate(new SettingTask(), 0, 500);
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onDestroy() {
super.onDestroy();
// Service被终止的同时也停止定时器继续运行
timer.cancel();
timer = null;
}
class SettingTask extends TimerTask {
@Override
public void run() {
// 当前界面是桌面,且没有悬浮窗显示,则创建悬浮窗。
if (OnTop() && !FloatWindowManager.isWindowShowing()) {
handler.post(new Runnable() {
@Override
public void run() {
FloatWindowManager.createSmallWindow(getApplicationContext());
}
});
}
// 当前界面不是桌面,且有悬浮窗显示,则移除悬浮窗。
else if (!OnTop() && FloatWindowManager.isWindowShowing()) {
handler.post(new Runnable() {
@Override
public void run() {
FloatWindowManager.removeSmallWindow(getApplicationContext());
FloatWindowManager.removeBigWindow(getApplicationContext());
}
});
}
// 当前界面是桌面,且有悬浮窗显示,则更新内存数据。
else if (OnTop() && FloatWindowManager.isWindowShowing()) {
handler.post(new Runnable() {
@Override
public void run() {
FloatWindowManager.updateUsedPercent(getApplicationContext());
}
});
}
}
}
/**
* 判断当前界面是否是桌面
*/
private boolean OnTop() {
ActivityManager mActivityManager = (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
List<RunningTaskInfo> rti = mActivityManager.getRunningTasks(1);
return getNames().contains(rti.get(0).topActivity.getPackageName());
}
/**
* 获得属于桌面的应用的应用包名称
*
* @return 返回包含所有包名的字符串列表
*/
private List<String> getNames() {
List<String> names = new ArrayList<String>();
PackageManager packageManager = this.getPackageManager();
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
List<ResolveInfo> resolveInfo = packageManager.queryIntentActivities(intent,
PackageManager.MATCH_DEFAULT_ONLY);
for (ResolveInfo ri : resolveInfo) {
names.add(ri.activityInfo.packageName);
}
return names;
}
}
package com.rust.floatwindowdemo;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import android.app.ActivityManager;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.TextView;
public class FloatWindowManager {
/**
* 小悬浮窗View的实例
*/
private static WindowSmall smallWindow;
/**
* 大悬浮窗View的实例
*/
private static WindowBig bigWindow;
/**
* 小悬浮窗View的参数
*/
private static LayoutParams smallWindowParams;
/**
* 大悬浮窗View的参数
*/
private static LayoutParams bigWindowParams;
/**
* 用于控制在屏幕上添加或移除悬浮窗
*/
private static WindowManager mWindowManager;
/**
* 用于获取手机可用内存
*/
private static ActivityManager mActivityManager;
/**
* 创建一个小悬浮窗。初始位置为屏幕的右部中间位置。
*
* @param context
* 必须为应用程序的Context.
*/
public static void createSmallWindow(Context context) {
WindowManager windowManager = getWindowManager(context);
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeight = windowManager.getDefaultDisplay().getHeight();
if (smallWindow == null) {
smallWindow = new WindowSmall(context);
if (smallWindowParams == null) {
smallWindowParams = new LayoutParams();
smallWindowParams.type = LayoutParams.TYPE_PHONE;
smallWindowParams.format = PixelFormat.RGBA_8888;
smallWindowParams.flags = LayoutParams.FLAG_NOT_TOUCH_MODAL
| LayoutParams.FLAG_NOT_FOCUSABLE;
smallWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
smallWindowParams.width = WindowSmall.viewWidth;
smallWindowParams.height = WindowSmall.viewHeight;
smallWindowParams.x = screenWidth;
smallWindowParams.y = screenHeight / 2;
}
smallWindow.setParams(smallWindowParams);
windowManager.addView(smallWindow, smallWindowParams);
}
}
/**
* 将小悬浮窗从屏幕上移除。
*
* @param context
* 必须为应用程序的Context.
*/
public static void removeSmallWindow(Context context) {
if (smallWindow != null) {
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(smallWindow);
smallWindow = null;
}
}
/**
* 创建一个大悬浮窗。位置为屏幕正中间。
*
* @param context
* 必须为应用程序的Context.
*/
public static void createBigWindow(Context context) {
WindowManager windowManager = getWindowManager(context);
int screenWidth = windowManager.getDefaultDisplay().getWidth();
int screenHeight = windowManager.getDefaultDisplay().getHeight();
if (bigWindow == null) {
bigWindow = new WindowBig(context);
if (bigWindowParams == null) {
bigWindowParams = new LayoutParams();
bigWindowParams.x = screenWidth / 2 - WindowBig.bigViewWidth / 2;
bigWindowParams.y = screenHeight / 2 - WindowBig.bigViewHeight / 2;
bigWindowParams.type = LayoutParams.TYPE_PHONE;
bigWindowParams.format = PixelFormat.RGBA_8888;
bigWindowParams.gravity = Gravity.LEFT | Gravity.TOP;
bigWindowParams.width = WindowBig.bigViewWidth;
bigWindowParams.height = WindowBig.bigViewHeight;
}
windowManager.addView(bigWindow, bigWindowParams);
}
}
/**
* 将大悬浮窗从屏幕上移除。
*
* @param context
* 必须为应用程序的Context.
*/
public static void removeBigWindow(Context context) {
if (bigWindow != null) {
WindowManager windowManager = getWindowManager(context);
windowManager.removeView(bigWindow);
bigWindow = null;
}
}
/**
* 更新小悬浮窗的TextView上的数据,显示内存使用的百分比。
*
* @param context
* 可传入应用程序上下文。
*/
public static void updateUsedPercent(Context context) {
if (smallWindow != null) {
TextView percentView = (TextView) smallWindow.findViewById(R.id.percent);
percentView.setText(getUsePercent(context));
}
}
/**
* 是否有悬浮窗(包括小悬浮窗和大悬浮窗)显示在屏幕上。
*
* @return 有悬浮窗显示在桌面上返回true,没有的话返回false。
*/
public static boolean isWindowShowing() {
return smallWindow != null || bigWindow != null;
}
/**
* 如果WindowManager还未创建,则创建一个新的WindowManager返回。否则返回当前已创建的WindowManager。
*
* @param context
* 必须为应用程序的Context.
* @return WindowManager的实例,用于控制在屏幕上添加或移除悬浮窗。
*/
private static WindowManager getWindowManager(Context context) {
if (mWindowManager == null) {
mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
}
return mWindowManager;
}
/**
* 如果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;
}
/**
* 计算已使用内存的百分比,并返回。
*
* @param context
* 可传入应用程序上下文。
* @return 已使用内存的百分比,以字符串形式返回。
*/
public static String getUsePercent(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;
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);
return mi.availMem;
}
}
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.rust.floatwindowdemo"
android:versionCode="1"
android:versionName="1.0" >
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
<uses-permission android:name="android.permission.GET_TASKS"/>
<uses-sdk
android:minSdkVersion="19"
android:targetSdkVersion="19" />
<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".FloatWindowService">
</service>
</application>
</manifest>
/****************************************************************************************************************************************/
AndroidDemo - FloatWindowDemo的更多相关文章
- TensorFlow编译androiddemo
首先是把tensorflow克隆到本地一份. git clone --recurse-submodules https://github.com/tensorflow/tensorflow.git 既 ...
- [Android-Demo]Android View的拖动
↑ 请善用目录 Demo下载地址:http://download.csdn.net/detail/u011634756/5959637 (免积分哦~) ------------------------ ...
- 讯飞语音识别Android-Demo
import java.io.UnsupportedEncodingException; import android.app.Activity; import android.os.Bundle; ...
- 浅谈 Fragment 生命周期
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Fragment 文中如有纰漏,欢迎大家留言指出. Fragment 是在 Android 3.0 中 ...
- Gradle 实现 Android 多渠道定制化打包
Gradle 实现 Android 多渠道定制化打包 版权声明:本文为博主原创文章,未经博主允许不得转载. 最近在项目中遇到需要实现 Apk 多渠道.定制化打包, Google .百度查找了一些资料, ...
- Android 剪贴板详解
版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Clipboard 如本文有助于你理解 Android 剪贴板,不妨给我一个 Star.对于码农而言, ...
- Android 7.1 - App Shortcuts
Android 7.1 - App Shortcuts 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Shortcuts 文中如有纰漏,欢迎大家留言 ...
- 浅谈 LayoutInflater
浅谈 LayoutInflater 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/View 文中如有纰漏,欢迎大家留言指出. 在 Android 的 ...
- Android Notification 详解(一)——基本操作
Android Notification 详解(一)--基本操作 版权声明:本文为博主原创文章,未经博主允许不得转载. 微博:厉圣杰 源码:AndroidDemo/Notification 文中如有纰 ...
随机推荐
- 使用 libdvm.so 内部函数dvm* 加载 dex
首先要清楚,odex只是对代码段(我将dex文件与elf文件类比,大家都将执行文件分成不同的段)作优化,而其它用于类反射信息的段都应用原来的dex,所以odex文件内部还包含了一个dex. 打开一个d ...
- Mybatis中如何查询时间段内的数据
最后一个是正确的,前边的三个是可能遇到的坑,给大家展示一下,如果不需要的,可以直接跳到最后看: 有时候我们需要查询一张表内一段时间内操作的数据,大家很容易就想到了 between ? and ? 这个 ...
- 使用r.js来打包模块化的javascript文件
前面的话 r.js(下载)是requireJS的优化(Optimizer)工具,可以实现前端文件的压缩与合并,在requireJS异步按需加载的基础上进一步提供前端优化,减小前端文件大小.减少对服务器 ...
- unity3D:游戏分解之角色移动和相机跟随
游戏中,我们经常会有这样的操作,点击场景中某个位置,角色自动移动到那个位置,同时角色一直是朝向那个位置移动的,而且相机也会一直跟着角色移动.有些游戏,鼠标滑动屏幕,相机就会围绕角色旋转. ...
- java基础(四章)
一. switch结构(开关语句)的语法 switch(表达式 ){ ------- [dream1]类型为int.char case 常量1 : ---------[ ...
- 基于redis实现tomcat8及以上版本的tomcat集群的session持久化实现(tomcat-redis-session-manager二次开发)
前言: 本项目是基于jcoleman的tomcat-redis-session-manager二次开发版本 1.修改了小部分实现逻辑 2.去除对juni.jar包的依赖 3.去除无效代码和老版本tom ...
- SQL Server Alwayson读写分离配置
标签:MSSQL/只读路由 概述 Alwayson相对于数据库镜像最大的优势就是可读副本,带来可读副本的同时还添加了一个新的功能就是配置只读路由实现读写分离:当然这里的读写分离稍微夸张了一点,只能称之 ...
- 用php+mysql+ajax实现淘宝客服或阿里旺旺聊天功能 之 前台页面
首先来看一下我已经实现的效果图: 消费者页面:(本篇随笔) (1)会显示店主的头像 (2)当前用户发送信息显示在右侧,接受的信息,显示在左侧 店主或客服页面:(下一篇随笔) (1)在左侧有一个列表 , ...
- Spring Security3详细配置
Spring Security3详细配置 表名:RESOURCE 解释:资源表备注: 资源表 RESOURCE(资源表) 是否主键 字段名 字段描述 数据类型 长度 可空 约束 缺省值 备注 是 ID ...
- javascript事件委托机制详解
以个人前端工作面试经历来看,javascript事件委托是问的最多的一类题目之一,熟悉事件委托能够了解你对于javascript的掌握程度. 面试官可能问一下问题,现在有5个li待办事件,需要实现当点 ...