Android仿微信文章悬浮窗效果

简介:

序言

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

640?wx_fmt=gif&tp=webp&wxfrom=5&wx_lazy=

原理

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

实现

1、权限

首先我们要考虑的一个问题就是权限问题,因为要适配Android 7.0 8.0,添加悬浮窗是需要申请权限的,这里参考了Android 悬浮窗权限各机型各系统适配大全这篇文章,适配的比较全,可以直接拿来用。这里需要注意的是,为了适配Android 8.0Window的类型需要配置一下:

1if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
2    //Android 8.0
3    mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
4else {
5    //其他版本
6    mLayoutParams.type = WindowManager.LayoutParams.TYPE_PHONE;
7}

2、添加ViewGroup到Window

判断好权限之后,直接添加就可以了

 1@SuppressLint("CheckResult")
2private void showWindow(Context context{
3    mWindowManager = (WindowManager) context.getSystemService(WINDOW_SERVICE);
4    mView = LayoutInflater.from(context).inflate(R.layout.article_window, null);
5
6    ImageView ivImage = mView.findViewById(R.id.aw_iv_image);
7    String imageUrl = SPUtil.getStringDefault(ARTICLE_IMAGE_URL, "");
8    RequestOptions requestOptions = RequestOptions.circleCropTransform();
9    requestOptions.placeholder(R.mipmap.ic_launcher_round).error(R.mipmap.ic_launcher_round);
10    Glide.with(context).load(imageUrl).apply(requestOptions).into(ivImage);
11
12    initListener(context);
13
14    mLayoutParams = new WindowManager.LayoutParams();
15    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
16        mLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
17    } else {
18        mLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_ALERT;
19    }
20    mLayoutParams.format = PixelFormat.RGBA_8888;   //窗口透明
21    mLayoutParams.gravity = Gravity.LEFT | Gravity.TOP;  //窗口位置
22    mLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;
23    mLayoutParams.width = 200;
24    mLayoutParams.height = 200;
25    mLayoutParams.x = mWindowManager.getDefaultDisplay().getWidth() - 200;
26    mLayoutParams.y = 0;
27    mWindowManager.addView(mView, mLayoutParams);
28}

3、View的拖拽实现

借助WindowManager.LayoutParams来实现,mLayoutParams.xmLayoutParams.y分别表示mView左上角的横纵坐标,所以我们只需要改动这两个值就行了,当ACTION_UP时,计算当前mView的中心点相对窗口的位置,然后将mView动态滑动到窗口左边或者右边:

 1//设置触摸滑动事件
2mView.setOnTouchListener(new View.OnTouchListener() {
3    int startX, startY;  //起始点
4    boolean isMove;  //是否在移动
5    long startTime;
6    int finalMoveX;  //最后通过动画将mView的X轴坐标移动到finalMoveX
7
8    @Override
9    public boolean onTouch(View v, MotionEvent event
{
10        switch (event.getAction()) {
11            case MotionEvent.ACTION_DOWN:
12                startX = (intevent.getX();
13                startY = (intevent.getY();
14                startTime = System.currentTimeMillis();
15                isMove = false;
16                return false;
17            case MotionEvent.ACTION_MOVE:
18                mLayoutParams.x = (int) (event.getRawX() - startX);
19                mLayoutParams.y = (int) (event.getRawY() - startY);
20                updateViewLayout();   //更新mView 的位置
21                return true;
22            case MotionEvent.ACTION_UP:
23                long curTime = System.currentTimeMillis();
24                isMove = curTime - startTime > 100;
25
26                //判断mView是在Window中的位置,以中间为界
27                if (mLayoutParams.x + mView.getMeasuredWidth() / 2 >= mWindowManager.getDefaultDisplay().getWidth() / 2) {
28                    finalMoveX = mWindowManager.getDefaultDisplay().getWidth() - mView.getMeasuredWidth();
29                } else {
30                    finalMoveX = 0;
31                }
32
33                //使用动画移动mView
34                ValueAnimator animator = ValueAnimator.ofInt(mLayoutParams.x, finalMoveX).setDuration(Math.abs(mLayoutParams.x - finalMoveX));
35                animator.addUpdateListener((ValueAnimator animation) -> {
36                    mLayoutParams.x = (int) animation.getAnimatedValue();
37                    updateViewLayout();
38                });
39                animator.start();
40
41                return isMove;
42        }
43        return false;
44    }
45});

4、注意

为了让WindowActivity脱离,这里我们采用Service来做,通过Service来添加和移除View;在权限申请成功之后我们需要通知Service(其实是Activity,可能会有保存数据等操作)作相应改变(提供一个接口给Service),然后在Service中使用广播来通知Activity;最后一个需要注意的地方就是我们需要判断应用程序是否在前台还是后台来添加或移除Window,这里通过使用ActivityLifecycleCallbacks来监听Activity在前台的数量来判断应用程序是在前台还是后台

 1class ApplicationLifecycle : Application.ActivityLifecycleCallbacks {
2
3    private var started: Int = 0
4
5    override fun onActivityPaused(activity: Activity?) {
6    }
7
8    override fun onActivityResumed(activity: Activity?) {
9    }
10
11    override fun onActivityStarted(activity: Activity?) {
12        started++
13        if (started == 1) {
14            Log.e("TAG""应用在前台了!!!")
15        }
16    }
17
18    override fun onActivityDestroyed(activity: Activity?) {
19    }
20
21    override fun onActivitySaveInstanceState(activity: Activity?, outState: Bundle?) {
22    }
23
24    override fun onActivityStopped(activity: Activity?) {
25        started--
26        if (started == 0) {
27            Log.e("TAG""应用在后台了!!!")
28        }
29    }
30
31    override fun onActivityCreated(activity: Activity?, savedInstanceState: Bundle?) {
32    }
33}



原文发布时间为:2018-10-10

本文作者:24K纯帅豆

本文来自云栖社区合作伙伴“IT先森养成记”,了解相关信息可以关注“IT先森养成记”。

相关文章
如何将Markdown文章轻松地搬运到微信公众号并完美地呈现代码内容
相信有很多童鞋跟我一样,热衷于用Markdown来编写文章。由于其简单的语法和清晰的渲染效果,受到广大码农朋友们的推崇。但是,当我们想维护起自己的公众号时,公众号编辑器往往让我们费劲了脑汁。本人尝试了各种工具,比如:秀米一些在线提供多种不同样式的编辑器。虽然这些编辑器都能够完成编辑任务,但是效果并不理想。与我们所追求的简洁、清晰风格总是格格不入,尤其是对于代码的展示非常的不友好。所以,这里给大家推荐一个本站的在线工具,可以帮助大家快速地把Markdown文章转换成微信公众号支持的漂亮格式。
276 0
如何将Markdown文章轻松地搬运到微信公众号并完美地呈现代码内容
|
运维 JavaScript 应用服务中间件
怎么微信WeixinJSBridge.invoke支付成功居然不跳转?还把我页面给关了!这篇文章就告诉你What should I do!
怎么微信WeixinJSBridge.invoke支付成功居然不跳转?还把我页面给关了!这篇文章就告诉你What should I do!
862 0
怎么微信WeixinJSBridge.invoke支付成功居然不跳转?还把我页面给关了!这篇文章就告诉你What should I do!
|
2天前
|
数据采集 存储 人工智能
【Python+微信】【企业微信开发入坑指北】4. 企业微信接入GPT,只需一个URL,自动获取文章总结
【Python+微信】【企业微信开发入坑指北】4. 企业微信接入GPT,只需一个URL,自动获取文章总结
13 0
|
1月前
|
数据采集 存储 关系型数据库
Python爬虫-使用代理获取微信公众号文章
使用代理爬取微信公众号文章
54 0
|
1月前
|
小程序 JavaScript 前端开发
【原力计划小程序】1、一篇文章深入了解小程序的学习路线(以项目驱动的方式带你学习微信小程序)
【原力计划小程序】1、一篇文章深入了解小程序的学习路线(以项目驱动的方式带你学习微信小程序)
53 1
|
5月前
|
小程序
微信小程序文章详情页跳转案例
微信小程序文章详情页跳转案例
44 0
|
5月前
如何在电脑上保存微信公众号文章封面图片?
如何在电脑上保存微信公众号文章封面图片?
44 0
|
9月前
layui框架实战案例(4):因内容安全策略导致弹出层模态框无法正常显示微信公众号文章使用window.open的解决方案
layui框架实战案例(4):因内容安全策略导致弹出层模态框无法正常显示微信公众号文章使用window.open的解决方案
39 1
|
9月前
|
JavaScript 前端开发 开发者
写了一个油猴脚本:获取微信公众号文章封面
写了一个油猴脚本:获取微信公众号文章封面
|
9月前
|
存储 监控 程序员
【教程&工具】微信同步文章到Bear
【教程&工具】微信同步文章到Bear