序言

前些日子跟朋友聊天,朋友Z果粉,前些天更新了微信,说微信出了个好方便的功能啊,我问是啥功能啊,看看我大Android有没有,他说现在阅读公众号文章如果有人给你发微信你可以把这篇文章当作悬浮窗悬浮起来,方便你聊完天不用找继续阅读,听完是不是觉得这叫啥啊,我大Android微信版不是早就有这个功能了吗,我看文章的时候看到过有这个悬浮按钮,但是我一直没有使用过,试了一下还是挺方便的,就想着自己实现一下这个功能,下面看图,大家都习惯了无图言X

image

原理

看完动图我们来分析一下,如何在每个页面上都存在一个View呢,有些人可能会说,写在base里面,这样每次启动一个新的Activity都要往页面上addView一次,性能不好,再说了,我们作为一个优秀的程序员能干这种重复的事吗,这种方案果断打回去;既然这样的话那我们肯定要在全局加了,那么全局是哪呢?相信了解过Activity源码的朋友肯定知道,全局可以在Window层加啊,这样既能一次性搞定,又不影响性能,说干就干。

实现

1、权限

首先我们要考虑的一个问题就是权限问题,因为要适配Android 7.0 8.0,添加悬浮窗是需要申请权限的,这里参考了Android 悬浮窗权限各机型各系统适配大全这篇文章,适配的比较全,可以直接拿来用。这里需要注意的是,为了适配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,有需要的朋友可以下载下来看看。

参考

公众号

欢迎关注我的个人公众号【IT先森养成记】,专注大前端技术分享,包含Android,Java基础,Kotlin,HTML,CSS,JS等技术;在这里你能得到的不止是技术上的提升,还有一些学习经验以及志同道合的朋友,赶快加入我们,一起学习,一起进化吧!!!

公众号:IT先森养成记

android 微信浮窗实现_Android仿微信文章悬浮窗效果相关推荐

  1. android 微信浮窗实现_Android仿微信视屏悬浮窗效果

    在项目中需要对接入的腾讯云音视频,可以悬浮窗显示,悬浮窗可拖拽,并且在悬浮窗不影响其他的activity的焦点. 这个大神的文章Android基于腾讯云实时音视频仿微信视频通话最小化悬浮,他讲的是视频 ...

  2. android仿微信下拉二楼_Android仿微信下拉列表实现

    本文要实现微信6.1中点击顶部菜单栏的"+"号按钮时,会弹出一个列表框.这里用的了Activity实现,其实最好的方法可以用ActionBar,不过这货好像只支持3.0以后的版本. ...

  3. java实现仿微信app聊天功能_Android仿微信语音聊天功能

    本文实例讲述了Android仿微信语音聊天功能代码.分享给大家供大家参考.具体如下: 项目效果如下: 具体代码如下: AudioManager.java package com.xuliugen.we ...

  4. 安卓开发仿微信图片拖拽_Android仿微信朋友圈图片浏览器(支持图片手势缩放,拖动)...

    [实例简介] Android仿微信朋友圈图片浏览器(支持图片手势缩放,拖动) [实例截图] [核心代码] ImageDemo-2014-02-18 └── ImageDemo-2014-02-18 ├ ...

  5. 安卓开发仿微信图片拖拽_Android 仿微信朋友圈图片拖拽返回

    目前的app的动画效果是越来越炫了,很多主流app的图片预览返回都有类似功能,比较常见的是ios自带相册,微信朋友圈等等.自己项目中也有类似功能,最近整理了一下这个功能的代码,做个笔记记录,有兴趣的朋 ...

  6. android 微信缩小通话界面_Android仿微信多人音视频通话界面

    工作中需要实现一个类似微信多人视频通话功能的界面,分别使用自定义viewgroup和自定义layoutmanager的方式进行了实现.最终工作中采用了layoutmanager,因为可以使用paylo ...

  7. 安卓开发仿微信图片拖拽_Android 仿微信朋友圈发表图片拖拽和删除功能

    朋友圈实现原理 我们使用 Android Device Monitor 来分析朋友圈发布图片的界面实现原理.如果需要分析其他应用的界面实现也是采用这种方法哦. 打开 Android Device Mo ...

  8. 基于微信小程序开发的仿微信demo

    (本文参考自github/liujians,地址:https://github.com/liujians/weApp) 作者声明: 基于微信小程序开发的仿微信demo  整合了ionic的样式库和we ...

  9. Android开发之仿手机卫士悬浮窗效果

    基本的实现原理,这种桌面悬浮窗的效果很类似与Widget,但是它比Widget要灵活的多.主要是通过WindowManager这个类来实现的,调用这个类的addView方法用于添加一个悬浮窗,upda ...

  10. Android 悬浮窗,绝对是目前相关悬浮窗开源库最完美的适配方案

    PerfectFloatWindow 项目地址:Alonsol/PerfectFloatWindow 简介: android 全局悬浮窗,目前已经适配华为,小米,vivo,oppo,一加,三星,魅族, ...

最新文章

  1. python浪漫表白代码
  2. 算法博士平均月入4万,数据可视化技能全球吃香 | 2020年《顶级数据团队建设全景报告》重磅发布...
  3. 游戏光线追踪往事:十年技术轮回
  4. Spring Cloud —— 消息队列与 RocketMQ
  5. 设置ArcGIS的外观改回到出厂
  6. 实现粗糙表面_什么是表面粗糙度,你真懂吗?
  7. WPF ComboBox样式
  8. JAVA中向量类Vector
  9. 为什么接口在设计时所有的方法一般都要抛异常?
  10. MySQL · 特性分析 · 执行计划缓存设计与实现
  11. Android安全补丁程序下载,2017年一月win7安全补丁更新包官方下载-2017Win7安全更新补丁包64位下载-西西软件下载...
  12. java多线程--信号量Semaphore的使用
  13. 微x怎么设置主题_微话题:小区设置公共晾衣架,您怎么看?您有什么建议?...
  14. PTAM + OpenCV3.4.1 + Ubuntu16.04
  15. 程序员学英语Oeasy
  16. Processing编写熊猫
  17. 使用清华镜像快速下载TensorFlow各版本及已安装版本查看
  18. 利用R代码从UCSC XENA下载mRNA, lncRNA, miRNA表达数据并匹配临床信息
  19. ROS单线程与多线程处理
  20. Hive2.x、HiveServer、HiveServer2简述及Beeline使用

热门文章

  1. 计算机上打印机删除不了怎么办,打印任务无法删除怎么办?
  2. JS插件开发之LightBox图片画廊
  3. php查询快递,php查询快递的类
  4. Python selenium 加载并保存QQ群成员,去除其群主、管理员信息的示例代码
  5. 热敏打印计算机,热敏打印头工作原理是什么 热敏打印头原理介绍【详解】
  6. 行业承压虎牙营收持续稳健,电竞浪潮中把握长期价值
  7. centos安装stress安装失败_Linux压力测试软件Stress使用指南
  8. 电感滤波电路的工作原理
  9. RdViewer(远程控制电脑屏幕软件)官方中文版V3.3.1 | rd远程工具下载
  10. Shawn Wildermuth的《Architecting WP7 》系列文章