Android常用控件之悬浮窗
16lz
2021-12-04
悬浮窗可以显示在所有应用程序之上,不管在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 {@Overrideprotected 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;@Overridepublic IBinder onBind(Intent arg0) {// TODO Auto-generated method stubreturn null;}@Overridepublic void onCreate() {// TODO Auto-generated method stubsuper.onCreate();createFloatView();}@Overridepublic void onStart(Intent intent, int startId) {// TODO Auto-generated method stubsuper.onStart(intent, startId);System.out.println("------------------onStart");viewHide = false;refresh();}@Overridepublic void onDestroy() {// TODO Auto-generated method stubsuper.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() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubviewHide = true;removeView();System.out.println("----------hideBtn");}});updateBtn.setOnClickListener(new OnClickListener() {@Overridepublic void onClick(View v) {// TODO Auto-generated method stubToast.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) {// 状态栏高度不能立即取,不然得到的值是0if (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() {// 如果已经添加了就只更新viewif (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);}/** * 接收消息 */@Overridepublic 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 {@Overridepublic void run() {// TODO Auto-generated method stub// 如果没有中断就一直运行while (!Thread.currentThread().isInterrupted()) {Message msg = handler.obtainMessage();msg.what = UPDATE_PIC; // 设置消息标识handler.sendMessage(msg);// 休眠1stry {Thread.sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.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可以从以下地址下载:
点击打开链接
更多相关文章
- Android常用控件之悬浮窗
- android > layout > SeekBar (拉动条)
- Android(安卓)图片拖动 放大
- Android的window类的常用方法
- Android之SeekBar
- 进度条及拖动条背景颜色设置(progressDrawable)
- Android(安卓)使用WindowManager打造通用悬浮菜单,兼容Android(安
- [Android(安卓)Studio] Android(安卓)Studio如何提示函数用法
- 让Android自带的Gallery实现多点缩放,拖动和边界回弹效果,效果流畅