序言
前些日子跟朋友聊天,朋友Z果粉,前些天更新了微信,说微信出了个好方便的功能啊,我问是啥功能啊,看看我大Android有没有,他说现在阅读公众号文章如果有人给你发微信你可以把这篇文章当作悬浮窗悬浮起来,方便你聊完天不用找继续阅读,听完是不是觉得这叫啥啊,我大Android微信版不是早就有这个功能了吗,我看文章的时候看到过有这个悬浮按钮,但是我一直没有使用过,试了一下还是挺方便的,就想着自己实现一下这个功能,下面看图,大家都习惯了无图言X
看完动图我们来分析一下,如何在每个页面上都存在一个View呢,有些人可能会说,写在base里面,这样每次启动一个新的Activity都要往页面上addView一次,性能不好,再说了,我们作为一个优秀的程序员能干这种重复的事吗,这种方案果断打回去;既然这样的话那我们肯定要在全局加了,那么全局是哪呢?相信了解过Activity源码的朋友肯定知道,全局可以在Window层加啊,这样既能一次性搞定,又不影响性能,说干就干。
实现
1、权限
首先我们要考虑的一个问题就是权限问题,因为要适配Android 7.0 8.0,添加悬浮窗是需要申请权限的,适配的比较全,可以直接拿来用。这里需要注意的是,为了适配Android 8.0,Window的类型需要配置一下:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { //Android 8.0 mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { //其他版本 mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE; }
2、添加ViewGroup到Window
判断好权限之后,直接添加就可以了
@SuppressLint("CheckResult") private void showWindow(Context context) { mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE); mView = LayoutInflater.from(context).inflate(R.layout.article_window, null); ImageView ivImage = mView.findViewById(R.id.aw_iv_image); String imageUrl = SPUtil.getStringDefault(ARTICLE_IMAGE_URL, ""); RequestOptions requestOptions = RequestOptions.circleCropTransform(); requestOptions.placeholder(R.mipmap.ic_launcher_round).error(R.mipmap.ic_launcher_round); Glide.with(context).load(imageUrl).apply(requestOptions).into(ivImage); initListener(context); mLayoutParams = new WindowManager.LayoutParams(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY; } else { mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT; } mLayoutParams.format = PixelFormat.RGBA_8888; //窗口透明 mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP; //窗口位置 mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mLayoutParams.width = 200; mLayoutParams.height = 200; mLayoutParams.x = mWindowManager.getDefaultDisplay().getWidth() - 200; mLayoutParams.y = 0; mWindowManager.addView(mView, mLayoutParams); }
3、View的拖拽实现
借助WindowManager.LayoutParams来实现,mLayoutParams.x和mLayoutParams.y分别表示mView左上角的横纵坐标,所以我们只需要改动这两个值就行了,当ACTION_UP时,计算当前mView的中心点相对窗口的位置,然后将mView动态滑动到窗口左边或者右边:
//设置触摸滑动事件 mView.setOnTouchListener(new View.OnTouchListener() { int startX, startY; //起始点 boolean isMove; //是否在移动 long startTime; int finalMoveX; //最后通过动画将mView的X轴坐标移动到finalMoveX @Override public boolean onTouch(View v, MotionEvent event) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: startX = (int) event.getX(); startY = (int) event.getY(); startTime = System.currentTimeMillis(); isMove = false; return false; case MotionEvent.ACTION_MOVE: mLayoutParams.x = (int) (event.getRawX() - startX); mLayoutParams.y = (int) (event.getRawY() - startY); updateViewLayout(); //更新mView 的位置 return true; case MotionEvent.ACTION_UP: long curTime = System.currentTimeMillis(); isMove = curTime - startTime > 100; //判断mView是在Window中的位置,以中间为界 if (mLayoutParams.x + mView.getMeasuredWidth() / 2 >= mWindowManager.getDefaultDisplay().getWidth() / 2) { finalMoveX = mWindowManager.getDefaultDisplay().getWidth() - mView.getMeasuredWidth(); } else { finalMoveX = 0; } //使用动画移动mView ValueAnimator animator = ValueAnimator.ofInt(mLayoutParams.x, finalMoveX).setDuration(Math.abs(mLayoutParams.x - finalMoveX)); animator.addUpdateListener((ValueAnimator animation) -> { mLayoutParams.x = (int) animation.getAnimatedValue(); updateViewLayout(); }); animator.start(); return isMove; } return false; } });
4、注意
为了让Window与Activity脱离,这里我们采用Service来做,通过Service来添加和移除View;在权限申请成功之后我们需要通知Service(其实是Activity,可能会有保存数据等操作)作相应改变(提供一个接口给Service),然后在Service中使用广播来通知Activity;最后一个需要注意的地方就是我们需要判断应用程序是否在前台还是后台来添加或移除Window,这里通过使用ActivityLifecycleCallbacks来监听Activity在前台的数量来判断应用程序是在前台还是后台
class ApplicationLifecycle : Application.ActivityLifecycleCallbacks { private var started: Int = 0 override fun onActivityPaused(activity: Activity?) { } override fun onActivityResumed(activity: Activity?) { } override fun onActivityStarted(activity: Activity?) { started++ if (started == 1) { Log.e("TAG", "应用在前台了!!!") } } override fun onActivityDestroyed(activity: Activity?) { } override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) { } override fun onActivityStopped(activity: Activity?) { started-- if (started == 0) { Log.e("TAG", "应用在后台了!!!") } } override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) { } }
本文代码已传至Github,有需要的朋友可以下载下来看看。
总结
以上所述是小编给大家介绍的Android仿微信文章悬浮窗效果,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对小牛知识库网站的支持!
本文向大家介绍Android仿微信视屏悬浮窗效果,包括了Android仿微信视屏悬浮窗效果的使用技巧和注意事项,需要的朋友参考一下 在项目中需要对接入的腾讯云音视频,可以悬浮窗显示,悬浮窗可拖拽,并且在悬浮窗不影响其他的activity的焦点。 这个大神的文章Android基于腾讯云实时音视频仿微信视频通话最小化悬浮,他讲的是视频通话时,将远端视频以悬浮窗形式展示,根据他的代码我进行了部分简化 1
本文向大家介绍Android实现桌面悬浮窗、蒙板效果实例代码,包括了Android实现桌面悬浮窗、蒙板效果实例代码的使用技巧和注意事项,需要的朋友参考一下 现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作。 今天这篇文章,就是介绍如何实现桌面悬浮窗效果的。 首先,看一下效果图。 悬浮窗一共分为两个部分,一个是平常显示的小窗口,
本文向大家介绍Android实现带磁性的悬浮窗体效果,包括了Android实现带磁性的悬浮窗体效果的使用技巧和注意事项,需要的朋友参考一下 本文实例讲述了Android实现带磁性的悬浮窗体效果。分享给大家供大家参考,具体如下: 带磁性的悬浮窗体,类似于360绿色小人 主要实现的是: 1.悬浮所有窗体之上 2.有吸引力,吸附于屏幕边上 3.有点击效果 下面我就实现上面三点,简单封装了个FloatVi
本文向大家介绍Android实现顶部悬浮效果,包括了Android实现顶部悬浮效果的使用技巧和注意事项,需要的朋友参考一下 本文实例为大家分享了Android实现顶部悬浮效果的具体代码,供大家参考,具体内容如下 效果图 布局 java 注意事项 1.清单文件需要配置 2.gradle配置需要24以上 3.recyclerView的下滑和swipRefreshLayout的下拉刷新会冲突,所以需要
本文向大家介绍微信小程序中悬浮窗功能的实现代码,包括了微信小程序中悬浮窗功能的实现代码的使用技巧和注意事项,需要的朋友参考一下 问题场景 所谓悬浮窗就是图中微信图标的按钮,采用fixed定位,可拖动和点击。 这算是一个比较常见的实现场景了。 为什么要用cover-view做悬浮窗?原生组件出来背锅了~ 最初我做悬浮窗用的不是cover-view,而是view。 这是简化的代码结构: 为什么要用co
本文向大家介绍Android实现类似qq微信消息悬浮窗通知功能,包括了Android实现类似qq微信消息悬浮窗通知功能的使用技巧和注意事项,需要的朋友参考一下 实现方法:(需要开启悬浮窗通知权限、允许应用在其他应用上显示) 一.利用headsup 悬挂式Notification,他是5.0中新增的,也就是API中的Headsup的Notification,可以在不打断用户操作的时候,给用户通知 二