Android 实现无需权限的悬浮球效果,可适配至Android 10

您所在的位置:网站首页 怎样去掉苹果的悬浮球 Android 实现无需权限的悬浮球效果,可适配至Android 10

Android 实现无需权限的悬浮球效果,可适配至Android 10

2023-12-24 23:14| 来源: 网络整理| 查看: 265

前言:

最近闲来无事,突然想起搞一下悬浮球,之前的项目的悬浮球一直都需要授权,android6.0以后需要手动授权,悬浮球使用时就非常不便,这里为大家带来一种无需权限的悬浮球实现方式。

无需权限!无需权限! 功能: 自动贴边显示红点隐藏红点自由移动显示悬浮球隐藏悬浮球销毁悬浮球接入简单,可进行自定义拓展

附上demo地址

效果如下:

在这里插入图片描述

以下是主要的代码部分:

MainActivity.class

public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); findViewById(R.id.showbtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { RoundView.getInstance().showRoundView(MainActivity.this); } }); findViewById(R.id.hidebtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { RoundView.getInstance().hideRoundView(MainActivity.this); } }); findViewById(R.id.closebtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { RoundView.getInstance().closeRoundView(MainActivity.this); } }); findViewById(R.id.showMsgbtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { RoundView.getInstance().showRoundMsg(); } }); findViewById(R.id.hideMsgbtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { RoundView.getInstance().hideRoundMsg(); } }); findViewById(R.id.exitbtn).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { android.os.Process.killProcess(android.os.Process.myPid()); } }); } }

RoundView.class

/** * 悬浮窗管理器 */ public class RoundView { private static RoundView sFloatingLayer; /** * 用于控制在屏幕上添加或移除悬浮窗 */ private static WindowManager mWindowManager; public static boolean isNearLeft = true; //判断悬浮球是否在左边 private static boolean isShow = false; //判断悬浮球是否已经显示 private int mWidth,mHeight; //屏幕的宽高 public static final int WIN_NONE = 0;// 不展示 public static final int WIN_SMALL = 1;// 小浮标 public static final int WIN_BIG = 2;// 展开 public static final int WIN_HIDE = 3;// 靠边隐藏 无操作隐藏 public static int winStatus; public static boolean isMsg=false; //红点消息提示是否显示 /** * 小悬浮窗view的实例 */ private static RoundWindowSmallView smallWindow; /** * 隐藏悬浮窗view的实例 */ private static RoundWindowHideView hideWindow; /** * 大悬浮窗view的实例 */ private static RoundWindowBigView bigWindow; /** * 大小悬浮窗view的参数 */ private static WindowManager.LayoutParams mLayoutParams; public static RoundView getInstance() { if (null == sFloatingLayer) { synchronized (RoundView.class) { if (null == sFloatingLayer) { sFloatingLayer = new RoundView(); } } } return sFloatingLayer; } /** * 显示悬浮窗 * @param context */ public void showRoundView(Context context) { if(mWindowManager==null){ mWindowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); } //每一次显示浮窗前都重新获取一次宽高,避免横竖屏切换后宽高变化 getWidthAndHeight(context); if(!isShow){ //处于非显示状态,可以显示 isShow=true; if(winStatus==WIN_NONE){ //处于未创建状态,请创建 showSmallwin(context); }else { //已创建了,直接显示 switch (winStatus){ case WIN_SMALL: if(smallWindow!=null) smallWindow.setVisibilityState(View.VISIBLE); break; case WIN_BIG: if(bigWindow!=null) bigWindow.setVisibilityState(View.VISIBLE); break; case WIN_HIDE: if(hideWindow!=null) hideWindow.setVisibilityState(View.VISIBLE); break; } } } } /** * 隐藏悬浮窗 * @param context */ public void hideRoundView(Context context) { if(isShow){ //处于显示状态,可以隐藏 isShow=false; switch (winStatus){ case WIN_SMALL: if(smallWindow!=null) smallWindow.setVisibilityState(View.GONE); break; case WIN_BIG: if(bigWindow!=null) bigWindow.setVisibilityState(View.GONE); break; case WIN_HIDE: if(hideWindow!=null) hideWindow.setVisibilityState(View.GONE); break; } } } /** * 销毁悬浮窗 * @param context */ public void closeRoundView(Context context) { isShow=false; isMsg=false; winStatus=WIN_NONE; removeSmallWindow(context); removeBigWindow(context); removeHideWindow(context); } /** * 显示红点提示 */ public void showRoundMsg() { if(!isMsg){ isMsg=true; switch (winStatus){ case WIN_SMALL: if(smallWindow!=null) smallWindow.showRoundMsg(); break; case WIN_BIG: if(bigWindow!=null) bigWindow.showRoundMsg(); break; case WIN_HIDE: if(hideWindow!=null) hideWindow.showRoundMsg(); break; } } } /** * 隐藏红点提示 */ public void hideRoundMsg() { if(isMsg){ isMsg=false; switch (winStatus){ case WIN_SMALL: if(smallWindow!=null) smallWindow.hideRoundMsg(); break; case WIN_BIG: if(bigWindow!=null) bigWindow.hideRoundMsg(); break; case WIN_HIDE: if(hideWindow!=null) hideWindow.hideRoundMsg(); break; } } } /** * 显示小悬浮窗 * @param context context */ public void showSmallwin(final Context context) { new Handler().postDelayed(new Runnable() { @Override public void run() { createSmallWindow(context); removeBigWindow(context); removeHideWindow(context); } }, 500); } /************************************************ 创建窗口 ************************************************************/ /** * 创建一个小悬浮窗。初始位置在屏幕的左上角位置 * * @param context 必须为应用程序的Context */ public void createSmallWindow(Context context) { //每一次创建前都要重新获取宽高,不然横竖屏切换时会出问题 getWidthAndHeight(context); if (smallWindow == null) { smallWindow = new RoundWindowSmallView(context); if (mLayoutParams == null) { mLayoutParams = new WindowManager.LayoutParams(); mLayoutParams.format = PixelFormat.RGBA_8888;// 解决带Alpha的32位png图片失真问题 mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; //显示在左上角 mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; // 在设置宽高 mLayoutParams.x = 0; mLayoutParams.y = context.getResources().getDisplayMetrics().heightPixels / 3 * 2; mLayoutParams.width = WindowManager.LayoutParams.WRAP_CONTENT; mLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT; } } mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_ATTACHED_DIALOG; //设置悬浮窗的层次 if (!isNearLeft) {// 当在屏幕右侧时 重新计算x坐标 int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); smallWindow.measure(w, h); if (Configuration.ORIENTATION_LANDSCAPE == context.getResources().getConfiguration().orientation) {// 横屏 mLayoutParams.x = mWidth - smallWindow.getMeasuredWidth(); } else {// 竖屏 mLayoutParams.x = mWidth - smallWindow.getMeasuredWidth(); } } smallWindow.setParams(mLayoutParams); // 将悬浮球添加到窗体 if (smallWindow.getParent() == null) { mWindowManager.addView(smallWindow, mLayoutParams); } winStatus=WIN_SMALL; smallWindow.timehide();// 小悬浮窗3s后隐藏动画 } /** * 将小悬浮窗从屏幕上移除 * * @param context 必须为应用程序的context */ public void removeSmallWindow(Context context) { if (smallWindow != null) { smallWindow.stopDelayed(); if (context != null) { if (mWindowManager != null) { try { mWindowManager.removeView(smallWindow); } catch (Exception e) { } } smallWindow = null; } else { smallWindow = null; } } } /** * 创建一个隐藏悬浮窗。 * * @param context 必须为应用程序的Context */ public void createHideWindow(Context context) { //每一次创建前都要重新获取宽高,不然横竖屏切换时会出问题 getWidthAndHeight(context); if (hideWindow == null) { int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); smallWindow.measure(w, h);// 测量 int width = smallWindow.getMeasuredWidth();// 获得视图实际宽度(测量宽度) if (isNearLeft) { hideWindow = new RoundWindowHideView(context); } else { hideWindow = new RoundWindowHideView(context); mLayoutParams.x = mLayoutParams.x + width / 2; } } mWindowManager.addView(hideWindow, mLayoutParams); winStatus=WIN_HIDE; } /** * 将隐藏悬浮窗从屏幕上移除 * * @param context 必须为应用程序的context */ public void removeHideWindow(Context context) { if (hideWindow != null) { if (context != null) { if (mWindowManager != null) { mWindowManager.removeView(hideWindow); } } hideWindow = null; } } /** * 创建一个大悬浮窗。 * * @param context 必须为应用程序的Context */ public void createBigWindow(Context context) { //每一次创建前都要重新获取宽高,不然横竖屏切换时会出问题 getWidthAndHeight(context); int w = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); int h = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED); if (bigWindow == null) { if (smallWindow != null) { smallWindow.measure(w, h); } else if (hideWindow != null) { hideWindow.measure(w, h); } bigWindow = new RoundWindowBigView(context); } bigWindow.measure(w,h); if (mLayoutParams.x>(mWidth-bigWindow.getMeasuredWidth())){ // 在右边拖动的时候需要减去虚拟按钮 mLayoutParams.x = mWidth-bigWindow.getMeasuredWidth(); } mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_SUB_PANEL+1; //设置悬浮窗的层次 数据越大则越下面 mWindowManager.addView(bigWindow, mLayoutParams); winStatus=WIN_BIG; } /** * 将大悬浮窗从屏幕上移除 * * @param context 必须为应用程序的context */ public void removeBigWindow(Context context) { if (bigWindow != null) { if (context != null) { if (mWindowManager != null) { mWindowManager.removeView(bigWindow); } } bigWindow = null; } } /** * 获取全屏状态下的宽高 * @param context */ public void getWidthAndHeight(Context context){ mWidth=context.getResources().getDisplayMetrics().widthPixels; mHeight = context.getResources().getDisplayMetrics().heightPixels; //有的手机是有虚拟导航键的,当横屏且全屏时,悬浮球无法靠到最右边,所以要用包含虚拟导航键的屏幕宽度 Point point = new Point(); if (mWindowManager != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { mWindowManager.getDefaultDisplay().getRealSize(point); mWidth=point.x; //mHeight =point.y; } } } }

RoundWindowBigView.class

/** * 悬浮窗展开状态 */ public class RoundWindowBigView extends LinearLayout { private Context context; private ImageView iv_content; /** * 红点消息提示 */ private View msg; public RoundWindowBigView(Context context) { super(context); this.context = context; if (RoundView.isNearLeft) { LayoutInflater.from(context).inflate(FileUtil.getResIdFromFileName(context, "layout", "pop_left"), this); } else { LayoutInflater.from(context).inflate(FileUtil.getResIdFromFileName(context, "layout", "pop_right"), this); } iv_content = (ImageView) findViewById( FileUtil.getResIdFromFileName(context, "id", "iv_content")); msg=findViewById(FileUtil.getResIdFromFileName(context, "id", "round_msg")); if(RoundView.isMsg){ msg.setVisibility(VISIBLE); }else { msg.setVisibility(GONE); } setupViews(); } private void setupViews() { iv_content.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { new Handler().post(new Runnable() { @Override public void run() { // 只能先创建在移除,不然有问题 RoundView.getInstance().createSmallWindow(context); RoundView.getInstance().removeBigWindow(context); } }); } }); } public void setVisibilityState(int state){ this.setVisibility(state); } /** * 红点消息显示 */ public void showRoundMsg(){ msg.setVisibility(VISIBLE); } /** * 红点消息隐藏 */ public void hideRoundMsg(){ msg.setVisibility(GONE); } }

RoundWindowHideView.class

/** * 悬浮窗贴边状态 */ public class RoundWindowHideView extends LinearLayout { private ImageView hideview; private Context context; /** * 红点消息提示 */ private View msg; public RoundWindowHideView(Context context) { super(context); this.context = context; if (RoundView.isNearLeft) { LayoutInflater.from(context).inflate(FileUtil.getResIdFromFileName(context, "layout", "layout_hide_float_left"), this); } else { LayoutInflater.from(context).inflate(FileUtil.getResIdFromFileName(context, "layout", "layout_hide_float_right"), this); } hideview = (ImageView) findViewById( FileUtil.getResIdFromFileName(context, "id", "hide_float_iv")); msg=findViewById(FileUtil.getResIdFromFileName(context, "id", "round_msg")); if(RoundView.isMsg){ msg.setVisibility(VISIBLE); }else { msg.setVisibility(GONE); } setupViews(); } private void setupViews() { hideview.setOnClickListener(new OnClickListener() { @Override public void onClick(View arg0) { // 只能先创建在移除,不然有问题 RoundView.getInstance().createSmallWindow(context); RoundView.getInstance().removeHideWindow(context); } }); } public void setVisibilityState(int state){ this.setVisibility(state); } /** * 红点消息显示 */ public void showRoundMsg(){ msg.setVisibility(VISIBLE); } /** * 红点消息隐藏 */ public void hideRoundMsg(){ msg.setVisibility(GONE); } }

RoundWindowSmallView.class

/** * 悬浮窗常规状态 */ public class RoundWindowSmallView extends LinearLayout { private static final String TAG = "RoundView"; private Context context; private WindowManager windowManager; private WindowManager.LayoutParams mParams; private float mPrevX; private float mPrevY; private int mWidth,mHeight; private Timer mAnimationTimer; private AnimationTimerTask mAnimationTask; private Handler mHandler = new Handler(); private int mAnimationPeriodTime = 16; private long mLastTouchDownTime; //按下的时间 private final int TOUCH_TIME_THRESHOLD = 150; //少于150毫秒算点击事件 private long lastClickTime; /** * 小悬浮窗对象 */ private View view; /** * 红点消息提示 */ private View msgLeft,msgRight; public RoundWindowSmallView(final Context context) { super(context); this.context=context; windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE); mWidth=getContext().getResources().getDisplayMetrics().widthPixels; mHeight = getContext().getResources().getDisplayMetrics().heightPixels; Point point = new Point(); if (windowManager != null) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { windowManager.getDefaultDisplay().getRealSize(point); mWidth=point.x; //mHeight =point.y; } } // 获取布局文件 LayoutInflater.from(context).inflate(FileUtil.getResIdFromFileName(context, "layout", "round_view"), this); view = findViewById(FileUtil.getResIdFromFileName(context, "id", "rl_content")); msgLeft=findViewById(FileUtil.getResIdFromFileName(context, "id", "round_msg_left")); msgRight=findViewById(FileUtil.getResIdFromFileName(context, "id", "round_msg_right")); if(RoundView.isMsg){ if(RoundView.isNearLeft){ msgLeft.setVisibility(VISIBLE); }else { msgRight.setVisibility(VISIBLE); } }else { msgLeft.setVisibility(GONE); msgRight.setVisibility(GONE); } this.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View view, MotionEvent motionEvent) { switch (motionEvent.getActionMasked()) { case MotionEvent.ACTION_DOWN: //按下 mLastTouchDownTime = System.currentTimeMillis(); handler.removeMessages(1); mPrevX = motionEvent.getRawX(); mPrevY = motionEvent.getRawY(); break; case MotionEvent.ACTION_MOVE: float deltaX = motionEvent.getRawX() - mPrevX; float deltaY = motionEvent.getRawY() - mPrevY; mParams.x += deltaX; mParams.y += deltaY; mPrevX = motionEvent.getRawX(); mPrevY = motionEvent.getRawY(); //判断是否越界 if (mParams.x mWidth - view.getWidth()) mParams.x = mWidth - view.getWidth(); if (mParams.y mHeight - view.getHeight()) mParams.y = mHeight - view.getHeight(); try { windowManager.updateViewLayout(view, mParams); } catch (Exception e) { Log.d(TAG, e.toString()); } break; case MotionEvent.ACTION_CANCEL: case MotionEvent.ACTION_UP: //松开 if (isOnClickEvent()) { //添加点击事件 if (isFastDoubleClick()) { //防止连续点击,如果连续点击这里什么也不做 } else { //Toast.makeText(context, "你点击了悬浮球", Toast.LENGTH_SHORT).show(); new Handler().post(new Runnable() { @Override public void run() { RoundView.getInstance().createBigWindow(context); RoundView.getInstance().removeSmallWindow(context); } }); return false; } } //贴边 mAnimationTimer = new Timer(); mAnimationTask = new AnimationTimerTask(); mAnimationTimer.schedule(mAnimationTask, 0,mAnimationPeriodTime); timehide(); //3s钟后隐藏 break; } return false; } }); } /** * 将小悬浮窗的参数传入,用于更新小悬浮窗的位置 * * @param params 小悬浮窗的参数 */ public void setParams(WindowManager.LayoutParams params) { mParams = params; } //防止连续点击 private boolean isFastDoubleClick() { long time = System.currentTimeMillis(); if (time - lastClickTime return System.currentTimeMillis() - mLastTouchDownTime if (mParams.x > mWidth / 2) { RoundView.isNearLeft = false; mDestX = mWidth - view.getWidth(); mStepX = (mWidth - mParams.x) / 10; } else { RoundView.isNearLeft = true; mDestX = 0; mStepX = -((mParams.x) / 10); } } @Override public void run() { if (Math.abs(mDestX - mParams.x) mParams.x += mStepX; } try { mHandler.post(new Runnable() { @Override public void run() { updateViewPosition(); } }); } catch (Exception e) { Log.d(TAG, e.toString()); } if (mParams.x == mDestX) { mAnimationTask.cancel(); mAnimationTimer.cancel(); } } } public void setVisibilityState(int state){ if(state==View.VISIBLE){ timehide(); }else if(state==View.GONE){ handler.removeMessages(1); } this.setVisibility(state); } public void updateViewPosition(){ windowManager.updateViewLayout(this, mParams); } /** * 显示小浮标是否添加3秒隐藏 */ public void timehide() { Message msg = new Message(); msg.what = 1; handler.sendMessageDelayed(msg, 3000); } /** * 小浮标停止隐藏 */ public void stopDelayed() { if (handler != null) { handler.removeMessages(1); } } /** * handler处理:隐藏半球 */ private Handler handler = new Handler() { @Override public void handleMessage(Message msg) { super.handleMessage(msg); switch (msg.what) { case 1: hidePop(); break; default: break; } } }; /** * 退出动画 * */ public void hidePop(){ Animation mAnimation = null; if (RoundView.isNearLeft) { mAnimation = AnimationUtils.loadAnimation(context, FileUtil.getResIdFromFileName(context, "anim", "slide_out_left")); } else { mAnimation = AnimationUtils.loadAnimation(context, FileUtil.getResIdFromFileName(context, "anim", "slide_out_right")); } view.startAnimation(mAnimation); mAnimation.setAnimationListener(new Animation.AnimationListener() { @Override public void onAnimationStart(Animation animation) { } @Override public void onAnimationRepeat(Animation animation) { } @Override public void onAnimationEnd(Animation animation) { //动画执行完毕后切换视图 // 只能先创建在移除,不然有问题 RoundView.getInstance().createHideWindow(context); RoundView.getInstance().removeSmallWindow(context); } }); } /** * 红点消息显示 */ public void showRoundMsg(){ if(RoundView.isNearLeft){ msgLeft.setVisibility(VISIBLE); }else { msgRight.setVisibility(VISIBLE); } } /** * 红点消息隐藏 */ public void hideRoundMsg(){ msgLeft.setVisibility(GONE); msgRight.setVisibility(GONE); } }


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3