Android 模仿苹果虚拟悬浮按钮 仿苹果悬浮窗下载 您所在的位置:网站首页 仿ipone11桌面 Android 模仿苹果虚拟悬浮按钮 仿苹果悬浮窗下载

Android 模仿苹果虚拟悬浮按钮 仿苹果悬浮窗下载

2023-06-25 14:06| 来源: 网络整理| 查看: 265

说明:本人写博客一来是为了方便日后查看项目,二来是希望能够和广大的程序猿相互交流学习,文章布局简单,如有嫌弃,请绕行,如有错误,请指出,谢谢。

实验环境:安卓6.0 魅族手机

悬浮窗主要有以下几个功能:

1、跟随手指的滑动而滑动(也可以用鼠标滑动) 2、在手指弹起的时候,悬浮窗会自动停靠在左右两侧 3、点击悬浮窗按钮可以返回到桌面

MainActivity中添加6.0访问权限

6.0权限问题:Google在6.0时加入权限管理机制,6.0之后,android需要动态获取权限,要使用权限,不仅要在manifest文件中定义,还要在代码中动态获取。

manifest中添加权限声明

MainActivity中代码如下:

public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (Settings.canDrawOverlays(MainActivity.this)) { Intent intent = new Intent(MainActivity.this, FloatViewService.class); Toast.makeText(MainActivity.this, "已开启悬浮窗", Toast.LENGTH_SHORT).show(); startService(intent); finish(); } else { //若没有权限,提示获取. Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION); intent.setData(Uri.parse("package:" + getPackageName())); Toast.makeText(MainActivity.this, "需要取得权限以使用悬浮窗", Toast.LENGTH_SHORT).show(); startActivity(intent); finish(); } } }

代码说明:如果手机已授予该app使用悬浮窗的功能,界面会自动开启悬浮窗,MainActivity被finish,否则直接跳转到本手机开启悬浮窗权限的界面,亲测有效,比如魅族手机开启权限的界面如下图所示:

Android 模仿苹果虚拟悬浮按钮 仿苹果悬浮窗下载_android

问题:只有在第一次安装app的时候才会跳转到打开权限的界面,之后打开app则不会跳转,这部分不太理解,有知悉的评论区见。

悬浮窗界面的绘制

Android的窗口是基于WindowManager实现的,它面向的对象一端是屏幕,另一端就是View,比如我们之前使用的setContentView(R.layout.activity_main), 就是将view显示在屏幕上,代码的底层都是经过WindowManager实现的,整个系统只有一个WindowManager。

Service实现后台运行

当app没有被关闭时,悬浮窗同样可以运行,这时候就需要Service来实现后台运行。这里可自行百度Service具体实现的过程,本篇不做解释。

跟随手指的滑动而滑动

说明:需要监听手势,所以设置了setOnTouchListener,识别按下、移动、弹起三个动作,移动的过程需要动态获取触摸的坐标,所以首先要在按下的过程中获取按下的坐标,rawX = event.getRawX(); rawY = event.getRawY(),在移动的过程中进行刷新, wmParams.x = wmParams.x - distanceX;wmParams.y = wmParams.y - distanceY。

// 设置监听浮动窗口的触摸移动 go_mainhome.setOnTouchListener(new View.OnTouchListener() { private float rawX; private float rawY; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // Log.i("qqq", "onTouch------------------------------ACTION_DOWN: "); mFloatLayout.setAlpha(1.0f);//设置其透明度 myCountDownTimer.cancel();//取消计时 rawX = event.getRawX(); rawY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: // Log.i("qqq", "onTouch------------------------------ACTION_MOVE: "); // getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标 int distanceX = (int) (event.getRawX() - rawX); int distanceY = (int) (event.getRawY() - rawY); //mFloatView.getMeasuredWidth()和mFloatView.getMeasuredHeight()都是100 wmParams.x = wmParams.x - distanceX; wmParams.y = wmParams.y - distanceY; // 刷新 mWindowManager.updateViewLayout(mFloatLayout, wmParams); rawX = event.getRawX(); rawY = event.getRawY(); break; case MotionEvent.ACTION_UP: myCountDownTimer.start();//重新开始计时 if (wmParams.x < screenWidth / 2) { //在屏幕右侧 wmParams.x = 0; wmParams.y = wmParams.y - 0; } else { //在屏幕左侧 wmParams.x = screenWidth; wmParams.y = wmParams.y - 0; } mWindowManager.updateViewLayout(mFloatLayout, wmParams); break; } return false;//此处必须返回false,否则OnClickListener获取不到监听 } });

获取屏幕大小 尝试了好几种获取屏幕大小的代码,此方法亲测有效。

Display display = mWindowManager.getDefaultDisplay(); Point point = new Point(); display.getRealSize(point); screenWidth = point.x; screenHeight = point.y; Log.i("qqq", "screenWidth------: " + screenWidth + "\n" + "screenHeight---" + screenHeight);停靠功能

说明:当手指滑动到屏幕中央右侧时,比如在图中的A点(x,y),最终悬浮窗将会停靠在图中的B点,A点向右平移到B点,纵坐标不变,横坐标为0,在屏幕左侧同理,可详见代码case MotionEvent.ACTION_UP部分,前提是需要设置 wmParams.gravity = Gravity.RIGHT | Gravity.BOTTOM,可以滑动最下面看详细代码。

Android 模仿苹果虚拟悬浮按钮 仿苹果悬浮窗下载_ide_02

点击悬浮窗按钮可以返回到桌面

其实就是对按钮设置了监听setOnClickListener,点击之后跳转到桌面的main。

Service中的代码package com.lightingcontour.toucher; import android.annotation.SuppressLint; import android.app.Service; import android.content.Intent; import android.content.res.Resources; import android.graphics.PixelFormat; import android.graphics.Point; import android.os.CountDownTimer; import android.os.IBinder; import android.util.DisplayMetrics; import android.util.Log; import android.view.Display; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.Toast; public class FloatViewService extends Service { private static final String TAG = "FloatViewService"; // 定义浮动窗口布局 private LinearLayout mFloatLayout; private WindowManager.LayoutParams wmParams; // 创建浮动窗口设置布局参数的对象 private WindowManager mWindowManager; private ImageButton go_mainhome; private ImageButton go_back; // private LinearLayout toucher_layout; private int screenHeight; private int screenWidth; private MyCountDownTimer myCountDownTimer; @Override public void onCreate() { super.onCreate(); Log.i(TAG, "onCreate"); createFloatView(); myCountDownTimer = new MyCountDownTimer(2500, 1000); //设置计时2.5s myCountDownTimer.start(); } @SuppressWarnings("static-access") @SuppressLint("InflateParams") private void createFloatView() { wmParams = new WindowManager.LayoutParams(); // 通过getApplication获取的是WindowManagerImpl.CompatModeWrapper mWindowManager = (WindowManager) getApplication().getSystemService(getApplication().WINDOW_SERVICE); Display display = mWindowManager.getDefaultDisplay(); Point point = new Point(); display.getRealSize(point); screenWidth = point.x; screenHeight = point.y; Log.i("qqq", "screenWidth------: " + screenWidth + "\n" + "screenHeight---" + screenHeight); // 设置window type wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; // 设置图片格式,效果为背景透明 wmParams.format = PixelFormat.RGBA_8888; // 设置浮动窗口不可聚焦(实现操作除浮动窗口外的其他可见窗口的操作) wmParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 调整悬浮窗显示的停靠位置为右侧底部 wmParams.gravity = Gravity.RIGHT | Gravity.BOTTOM; // 以屏幕左上角为原点,设置x、y初始值,相对于gravity wmParams.x = 0; wmParams.y = 0; // 设置悬浮窗口长宽数据 wmParams.width = WindowManager.LayoutParams.WRAP_CONTENT; wmParams.height = WindowManager.LayoutParams.WRAP_CONTENT; LayoutInflater inflater = LayoutInflater.from(getApplication()); // 获取浮动窗口视图所在布局 mFloatLayout = (LinearLayout) inflater.inflate(R.layout.toucherlayout, null); // 添加mFloatLayout mWindowManager.addView(mFloatLayout, wmParams); // 浮动窗口按钮 go_mainhome = (ImageButton) mFloatLayout.findViewById(R.id.go_mainhome); go_back = (ImageButton) mFloatLayout.findViewById(R.id.go_back); //UNSPECIFIED是未指定模式 mFloatLayouasure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED), View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED)); // 设置监听浮动窗口的触摸移动 go_mainhome.setOnTouchListener(new View.OnTouchListener() { private float rawX; private float rawY; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // Log.i("qqq", "onTouch------------------------------ACTION_DOWN: "); mFloatLayout.setAlpha(1.0f);//设置其透明度 myCountDownTimer.cancel();//取消计时 rawX = event.getRawX(); rawY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: // Log.i("qqq", "onTouch------------------------------ACTION_MOVE: "); // getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标 int distanceX = (int) (event.getRawX() - rawX); int distanceY = (int) (event.getRawY() - rawY); //mFloatView.getMeasuredWidth()和mFloatView.getMeasuredHeight()都是100 wmParams.x = wmParams.x - distanceX; wmParams.y = wmParams.y - distanceY; // 刷新 mWindowManager.updateViewLayout(mFloatLayout, wmParams); rawX = event.getRawX(); rawY = event.getRawY(); break; case MotionEvent.ACTION_UP: myCountDownTimer.start();//重新开始计时 if (wmParams.x < screenWidth / 2) { //在屏幕右侧 wmParams.x = 0; wmParams.y = wmParams.y - 0; } else { wmParams.x = screenWidth; wmParams.y = wmParams.y - 0; } mWindowManager.updateViewLayout(mFloatLayout, wmParams); break; } return false;//此处必须返回false,否则OnClickListener获取不到监听 } }); // 设置监听浮动窗口的触摸移动 go_back.setOnTouchListener(new View.OnTouchListener() { private float rawX; private float rawY; @SuppressLint("ClickableViewAccessibility") @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: // Log.i("qqq", "onTouch------------------------------ACTION_DOWN: "); mFloatLayout.setAlpha(1.0f);//设置其透明度 myCountDownTimer.cancel();//取消计时 rawX = event.getRawX(); rawY = event.getRawY(); break; case MotionEvent.ACTION_MOVE: // Log.i("qqq", "onTouch------------------------------ACTION_MOVE: "); // getRawX是触摸位置相对于屏幕的坐标,getX是相对于按钮的坐标 int distanceX = (int) (event.getRawX() - rawX); int distanceY = (int) (event.getRawY() - rawY); //mFloatView.getMeasuredWidth()和mFloatView.getMeasuredHeight()都是100 wmParams.x = wmParams.x - distanceX; wmParams.y = wmParams.y - distanceY; // 刷新 mWindowManager.updateViewLayout(mFloatLayout, wmParams); rawX = event.getRawX(); rawY = event.getRawY(); break; case MotionEvent.ACTION_UP: myCountDownTimer.start();//重新开始计时 if (wmParams.x < screenWidth / 2) { //在屏幕右侧 wmParams.x = 0; wmParams.y = wmParams.y - 0; } else { wmParams.x = screenWidth; wmParams.y = wmParams.y - 0; } mWindowManager.updateViewLayout(mFloatLayout, wmParams); break; } return false;//此处必须返回false,否则OnClickListener获取不到监听 } }); go_mainhome.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatViewService.this, "返回到桌面", Toast.LENGTH_SHORT).show(); Intent intent = new Intent(); // 为Intent设置Action、Category属性 intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setAction(Intent.ACTION_MAIN);// "android.intent.action.MAIN" intent.addCategory(Intent.CATEGORY_HOME); //"android.intent.category.HOME"CATEGORY_HOME 目标Activity是HOME Activity,即手机开机启动后显示的Activity,或按下HOME键后显示的Activity startActivity(intent); } }); go_back.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { Toast.makeText(FloatViewService.this, "返回", Toast.LENGTH_SHORT).show(); } }); } @Override public void onDestroy() { super.onDestroy(); if (mFloatLayout != null) { // 移除悬浮窗口 mWindowManager.removeView(mFloatLayout); } } @Override public IBinder onBind(Intent intent) { return null; } public class MyCountDownTimer extends CountDownTimer { public MyCountDownTimer(long millisInFuture, long countDownInterval) { super(millisInFuture, countDownInterval); } @Override public void onTick(long millisUntilFinished) { } @Override public void onFinish() { mFloatLayout.setAlpha(0.4f); } } }manifest中的代码 布局中的代码



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有