悬浮窗可以显示在所有应用程序之上,不管在PC机还是Android设备上都有这个,最常见的是360的“加速球”
来看下在Android设备上的效果
程序的目录结构如下图
创建Activity后启动Service就关闭
- package com.example.floatwnd;
-
- import android.app.Activity;
- import android.content.Intent;
- import android.os.Bundle;
-
- import com.example.floatwnd.service.FloatService;
-
- public class MainActivity extends Activity {
-
- @Override
- protected void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- Intent intent = new Intent(MainActivity.this, FloatService.class);
- startService(intent);
- finish();
- }
- }
悬浮窗的主要工作放在Service上处理
- package com.example.floatwnd.service;
-
- import android.app.Service;
- import android.content.Intent;
- import android.graphics.PixelFormat;
- import android.graphics.Rect;
- import android.os.Handler;
- import android.os.IBinder;
- import android.os.Looper;
- import android.os.Message;
- import android.view.Gravity;
- import android.view.LayoutInflater;
- import android.view.MotionEvent;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.View.OnTouchListener;
- import android.view.WindowManager;
- import android.view.WindowManager.LayoutParams;
- import android.widget.Button;
- import android.widget.TextView;
- import android.widget.Toast;
-
- import com.example.floatwnd.R;
- import com.example.floatwnd.utils.SysInfoUtils;
-
- /**
- * 悬浮窗Service 该服务会在后台一直运行一个悬浮的透明的窗体
- *
- * @author Administrator
- *
- */
- public class FloatService extends Service {
-
- private static final int UPDATE_PIC = 0x100;
- private int statusBarHeight;// 状态栏高度
- private View view;// 透明窗体
- private TextView text = null;
- private Button hideBtn = null;
- private Button updateBtn = null;
- private HandlerUI handler = null;
- private Thread updateThread = null;
- private boolean viewAdded = false;// 透明窗体是否已经显示
- private boolean viewHide = false; // 窗口隐藏
- private WindowManager windowManager;
- private WindowManager.LayoutParams layoutParams;
-
- @Override
- public IBinder onBind(Intent arg0) {
- // TODO Auto-generated method stub
- return null;
- }
-
- @Override
- public void onCreate() {
- // TODO Auto-generated method stub
- super.onCreate();
- createFloatView();
- }
-
- @Override
- public void onStart(Intent intent, int startId) {
- // TODO Auto-generated method stub
- super.onStart(intent, startId);
- System.out.println("------------------onStart");
- viewHide = false;
- refresh();
- }
-
- @Override
- public void onDestroy() {
- // TODO Auto-generated method stub
- super.onDestroy();
- removeView();
- }
-
- /**
- * 关闭悬浮窗
- */
- public void removeView() {
- if (viewAdded) {
- windowManager.removeView(view);
- viewAdded = false;
- }
- }
-
- private void createFloatView() {
- handler = new HandlerUI();
- UpdateUI update = new UpdateUI();
- updateThread = new Thread(update);
- updateThread.start(); // 开户线程
-
- view = LayoutInflater.from(this).inflate(R.layout.main, null);
- text = (TextView) view.findViewById(R.id.usage);
- hideBtn = (Button) view.findViewById(R.id.hideBtn);
- updateBtn = (Button) view.findViewById(R.id.updateBtn);
- windowManager = (WindowManager) this.getSystemService(WINDOW_SERVICE);
- /*
- * LayoutParams.TYPE_SYSTEM_ERROR:保证该悬浮窗所有View的最上层
- * LayoutParams.FLAG_NOT_FOCUSABLE:该浮动窗不会获得焦点,但可以获得拖动
- * PixelFormat.TRANSPARENT:悬浮窗透明
- */
- layoutParams = new LayoutParams(LayoutParams.WRAP_CONTENT,
- LayoutParams.WRAP_CONTENT, LayoutParams.TYPE_SYSTEM_ERROR,
- LayoutParams.FLAG_NOT_FOCUSABLE, PixelFormat.TRANSPARENT);
- // layoutParams.gravity = Gravity.RIGHT|Gravity.BOTTOM; //悬浮窗开始在右下角显示
- layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
-
- /**
- * 监听窗体移动事件
- */
- view.setOnTouchListener(new OnTouchListener() {
- float[] temp = new float[] { 0f, 0f };
-
- public boolean onTouch(View v, MotionEvent event) {
- layoutParams.gravity = Gravity.LEFT | Gravity.TOP;
- int eventaction = event.getAction();
- switch (eventaction) {
- case MotionEvent.ACTION_DOWN: // 按下事件,记录按下时手指在悬浮窗的XY坐标值
- temp[0] = event.getX();
- temp[1] = event.getY();
- break;
-
- case MotionEvent.ACTION_MOVE:
- refreshView((int) (event.getRawX() - temp[0]),
- (int) (event.getRawY() - temp[1]));
- break;
-
- }
- return true;
- }
- });
-
- hideBtn.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- viewHide = true;
- removeView();
- System.out.println("----------hideBtn");
- }
- });
-
- updateBtn.setOnClickListener(new OnClickListener() {
-
- @Override
- public void onClick(View v) {
- // TODO Auto-generated method stub
- Toast.makeText(getApplicationContext(), "you click UpdateBtn",
- Toast.LENGTH_SHORT).show();
- System.out.println("mom "
- + SysInfoUtils
- .getUsedPercentValue(getApplicationContext()));
- }
- });
- }
-
- /**
- * 刷新悬浮窗
- *
- * @param x
- * 拖动后的X轴坐标
- * @param y
- * 拖动后的Y轴坐标
- */
- private void refreshView(int x, int y) {
- // 状态栏高度不能立即取,不然得到的值是0
- if (statusBarHeight == 0) {
- View rootView = view.getRootView();
- Rect r = new Rect();
- rootView.getWindowVisibleDisplayFrame(r);
- statusBarHeight = r.top;
- }
-
- layoutParams.x = x;
- // y轴减去状态栏的高度,因为状态栏不是用户可以绘制的区域,不然拖动的时候会有跳动
- layoutParams.y = y - statusBarHeight;// STATUS_HEIGHT;
- refresh();
- }
-
- /**
- * 添加悬浮窗或者更新悬浮窗 如果悬浮窗还没添加则添加 如果已经添加则更新其位置
- */
- private void refresh() {
- // 如果已经添加了就只更新view
- if (viewAdded) {
- windowManager.updateViewLayout(view, layoutParams);
- } else {
- windowManager.addView(view, layoutParams);
- viewAdded = true;
- }
- }
-
- /**
- * 接受消息和处理消息
- *
- * @author Administrator
- *
- */
- class HandlerUI extends Handler {
- public HandlerUI() {
-
- }
-
- public HandlerUI(Looper looper) {
- super(looper);
- }
-
- /**
- * 接收消息
- */
- @Override
- public void handleMessage(Message msg) {
- // TODO Auto-generated method stub
- // 根据收到的消息分别处理
- if (msg.what == UPDATE_PIC) {
- text.setText(SysInfoUtils
- .getUsedPercentValue(getApplicationContext())
- + " t = "
- + SysInfoUtils.getTotalMemory(getApplicationContext())
- + " a = "
- + SysInfoUtils
- .getAvailableMemoryString(getApplicationContext()));
- if (!viewHide)
- refresh();
- } else {
- super.handleMessage(msg);
- }
-
- }
-
- }
-
- /**
- * 更新悬浮窗的信息
- *
- * @author Administrator
- *
- */
- class UpdateUI implements Runnable {
-
- @Override
- public void run() {
- // TODO Auto-generated method stub
- // 如果没有中断就一直运行
- while (!Thread.currentThread().isInterrupted()) {
- Message msg = handler.obtainMessage();
- msg.what = UPDATE_PIC; // 设置消息标识
- handler.sendMessage(msg);
- // 休眠1s
- try {
- Thread.sleep(1000);
- } catch (InterruptedException e) {
- // TODO Auto-generated catch block
- e.printStackTrace();
- }
- }
- }
- }
- }
为悬浮窗创建布局文件
- <?xml version="1.0" encoding="UTF-8"?>
- <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:background="@drawable/background" >
-
- <!-- android:updatePeriodMillis="10000"> -->
-
- <TextView
- android:id="@+id/flowspeed"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_alignParentTop="true"
- android:layout_centerInParent="true"
- android:text="@string/float_text"
- android:textColor="#000000" />
-
- <Button
- android:id="@+id/hideBtn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/flowspeed"
- android:layout_centerInParent="true"
- android:layout_marginTop="15dp"
- android:background="@drawable/hide"
- android:contentDescription="@string/hide"
- android:text="@string/hide" />
-
- <Button
- android:id="@+id/updateBtn"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/hideBtn"
- android:layout_centerInParent="true"
- android:background="@drawable/update"
- android:contentDescription="@string/update"
- android:paddingTop="15dp"
- android:text="@string/update" />
-
- <TextView
- android:id="@+id/usage"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_below="@id/updateBtn"
- android:layout_centerInParent="true"
- android:paddingTop="15dp"
- android:text="@string/float_text"
- android:textColor="#000000" />
-
- </RelativeLayout>
悬浮窗上的控件就跟Activity上一样使用
悬浮窗上显示的一些内存信息,通过工具类:SysInfoUtils.java来读取
完整的Demo可以从以下地址下载:
点击打开链接