我的编程空间,编程开发者的网络收藏夹
学习永远不晚

Android仿IOS系统悬浮窗效果

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

Android仿IOS系统悬浮窗效果

在一些场合里,我们使用悬浮窗会有很大的便利,比如IOS系统的悬浮窗,360或者其他手机卫士的悬浮窗等等。

本篇博客,我们创造出两个悬浮窗,通过点击小悬浮窗打开或者关闭大悬浮窗(一个播放控制器)。

代码如下:

在这之前,我们需要在manifest中申请权限:


<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />

并且,悬浮窗这个权限我们需要手动在手机找到应用权限管理,允许这个权限才行

小悬浮窗的界面代码float_normal_view.xml:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="65dp"
    android:layout_height="65dp"
    android:id="@+id/ll_float_normal"
    android:background="@drawable/float_bg"
    android:gravity="center"
    android:orientation="vertical">

    <ImageView
        android:id="@+id/iv_show_control_view"
        android:layout_gravity="center"
        android:background="@drawable/white_ring"
        android:layout_width="35dp"
        android:orientation="vertical"
        android:layout_height="35dp" >

    </ImageView>

</LinearLayout>

大悬浮窗的界面代码float_control_view:


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/ll_float_control"
    android:layout_width="300dp"
    android:layout_height="100dp"
    android:background="@drawable/float_bg"
    android:gravity="center_horizontal|bottom"
    android:orientation="vertical">

    <SeekBar
        android:id="@+id/timeline"
        android:paddingTop="3dp"
        android:paddingBottom="3dp"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:focusable="true"
        android:maxHeight="5.0dip"
        android:minHeight="5.0dip"
        android:paddingLeft="16.0dip"
        android:paddingRight="16.0dip"
        android:progressDrawable="@drawable/po_seekbar"
        android:thumb="@drawable/seekbar_thumb" />

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom"
        android:paddingBottom="10dp"
        android:paddingEnd="20dp"
        android:paddingStart="20dp"
        android:paddingTop="10dp">

        <ImageButton
            android:id="@+id/ibt_rewind"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:layout_marginEnd="20dp"
            android:layout_marginRight="20dp"
            android:layout_toLeftOf="@+id/ibt_play"
            android:layout_toStartOf="@+id/ibt_play"
            android:background="@drawable/rewind" />

        <ImageButton
            android:id="@+id/ibt_play"
            android:layout_width="50dp"
            android:layout_height="50dp"
            android:layout_centerHorizontal="true"
            android:background="@drawable/pause" />

        <ImageButton
            android:id="@+id/ibt_forward"
            android:layout_width="40dp"
            android:layout_height="40dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp"
            android:layout_marginStart="20dp"
            android:layout_toEndOf="@+id/ibt_play"
            android:layout_toRightOf="@+id/ibt_play"
            android:background="@drawable/forward" />

    </RelativeLayout>


</LinearLayout>

入口activity(FloatActivity ):


public class FloatActivity extends Activity {

    MyWindowManager myWindowManager;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        myWindowManager = MyWindowManager.getInstance();
        myWindowManager.createNormalView(this.getApplicationContext());
    }
}

悬浮窗管理器MyWindowManager:



public class MyWindowManager {

    private FloatNormalView normalView;
    private FloatControlView controlView;

    private static MyWindowManager instance;
    private WindowManager windowManager;

    private MyWindowManager() {
    }

    public static MyWindowManager getInstance() {
        if (instance == null)
            instance = new MyWindowManager();
        return instance;
    }

    private WindowManager getWindowManager(Context context) {
        if (windowManager == null)
            windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        return windowManager;
    }

    
    public boolean isNormalViewExists() {
        return normalView != null;
    }

    
    public boolean isControlViewExists() {
        return controlView != null;
    }

    
    public void createNormalView(Context context) {
        if (normalView == null) {
            normalView = new FloatNormalView(context);
        }
    }


    
    public void removeNormalView(Context context) {
        if (normalView != null) {
            windowManager.removeView(normalView);
            normalView = null;
        }
    }

    
    public void createControlView(Context context) {
        if (controlView == null)
            controlView = new FloatControlView(context);
    }

    
    public void removeControlView(Context context) {
        if (controlView != null) {
            WindowManager windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            windowManager.removeView(controlView);
            controlView = null;
        }
    }
}

小悬浮窗FloatNormalView:



public class FloatNormalView extends LinearLayout {

    
    public static int viewWidth;

    
    public static int viewHeight;

    
    private static int statusBarHeight;

    
    private WindowManager windowManager;

    
    private WindowManager.LayoutParams mParams;

    
    private float xInScreen;

    
    private float yInScreen;

    
    private float xDownInScreen;

    
    private float yDownInScreen;

    
    private float xInView;

    
    private float yInView;

    public FloatNormalView(Context context) {
        super(context);
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
        View view = findViewById(R.id.ll_float_normal);
        viewWidth = view.getLayoutParams().width;
        viewHeight = view.getLayoutParams().height;
        initLayoutParams();
    }

    
    private void initLayoutParams() {
        //屏幕宽高
        int screenWidth = windowManager.getDefaultDisplay().getWidth();
        int screenHeight = windowManager.getDefaultDisplay().getHeight();

        mParams = new WindowManager.LayoutParams();
        //总是出现在应用程序窗口之上。
        mParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
        // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        //悬浮窗默认显示的位置
        mParams.gravity = Gravity.START | Gravity.TOP;
        //指定位置
        mParams.x = screenWidth - viewWidth * 2;
        mParams.y = screenHeight / 2 + viewHeight * 2;
        //悬浮窗的宽高
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.format = PixelFormat.TRANSPARENT;
        windowManager.addView(this, mParams);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
                xInView = event.getX();
                yInView = event.getY();
                xDownInScreen = event.getRawX();
                yDownInScreen = event.getRawY() - getStatusBarHeight();
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                break;
            case MotionEvent.ACTION_MOVE:
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                // 手指移动的时候更新小悬浮窗的位置
                updateViewPosition();
                break;
            case MotionEvent.ACTION_UP:
                // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
                if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
                    openOrCloseControlView();
                }
                break;
            default:
                break;
        }
        return true;
    }

    
    public void setParams(WindowManager.LayoutParams params) {
        mParams = params;
    }

    
    private void updateViewPosition() {
        mParams.x = (int) (xInScreen - xInView);
        mParams.y = (int) (yInScreen - yInView);
        windowManager.updateViewLayout(this, mParams);
    }

    
    private void openOrCloseControlView() {
        MyWindowManager manager = MyWindowManager.getInstance();
        if (!manager.isControlViewExists())
            manager.createControlView(getContext());
        else
            manager.removeControlView(getContext());
    }

    
    private int getStatusBarHeight() {
        if (statusBarHeight == 0) {
            try {
                Class<?> c = Class.forName("com.android.internal.R$dimen");
                Object o = c.newInstance();
                Field field = c.getField("status_bar_height");
                int x = (Integer) field.get(o);
                statusBarHeight = getResources().getDimensionPixelSize(x);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }
}

大悬浮窗FloatControlView:



public class FloatNormalView extends LinearLayout {

    
    public static int viewWidth;

    
    public static int viewHeight;

    
    private static int statusBarHeight;

    
    private WindowManager windowManager;

    
    private WindowManager.LayoutParams mParams;

    
    private float xInScreen;

    
    private float yInScreen;

    
    private float xDownInScreen;

    
    private float yDownInScreen;

    
    private float xInView;

    
    private float yInView;

    public FloatNormalView(Context context) {
        super(context);
        windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        LayoutInflater.from(context).inflate(R.layout.float_normal_view, this);
        View view = findViewById(R.id.ll_float_normal);
        viewWidth = view.getLayoutParams().width;
        viewHeight = view.getLayoutParams().height;
        initLayoutParams();
    }

    
    private void initLayoutParams() {
        //屏幕宽高
        int screenWidth = windowManager.getDefaultDisplay().getWidth();
        int screenHeight = windowManager.getDefaultDisplay().getHeight();

        mParams = new WindowManager.LayoutParams();
        //总是出现在应用程序窗口之上。
        mParams.type = WindowManager.LayoutParams.TYPE_PHONE;

        // FLAG_NOT_TOUCH_MODAL不阻塞事件传递到后面的窗口
        // FLAG_NOT_FOCUSABLE 悬浮窗口较小时,后面的应用图标由不可长按变为可长按,不设置这个flag的话,home页的划屏会有问题
        mParams.flags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL;

        //悬浮窗默认显示的位置
        mParams.gravity = Gravity.START | Gravity.TOP;
        //指定位置
        mParams.x = screenWidth - viewWidth * 2;
        mParams.y = screenHeight / 2 + viewHeight * 2;
        //悬浮窗的宽高
        mParams.width = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
        mParams.format = PixelFormat.TRANSPARENT;
        windowManager.addView(this, mParams);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                // 手指按下时记录必要数据,纵坐标的值都需要减去状态栏高度
                xInView = event.getX();
                yInView = event.getY();
                xDownInScreen = event.getRawX();
                yDownInScreen = event.getRawY() - getStatusBarHeight();
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                break;
            case MotionEvent.ACTION_MOVE:
                xInScreen = event.getRawX();
                yInScreen = event.getRawY() - getStatusBarHeight();
                // 手指移动的时候更新小悬浮窗的位置
                updateViewPosition();
                break;
            case MotionEvent.ACTION_UP:
                // 如果手指离开屏幕时,xDownInScreen和xInScreen相等,且yDownInScreen和yInScreen相等,则视为触发了单击事件。
                if (xDownInScreen == xInScreen && yDownInScreen == yInScreen) {
                    openOrCloseControlView();
                }
                break;
            default:
                break;
        }
        return true;
    }

    
    public void setParams(WindowManager.LayoutParams params) {
        mParams = params;
    }

    
    private void updateViewPosition() {
        mParams.x = (int) (xInScreen - xInView);
        mParams.y = (int) (yInScreen - yInView);
        windowManager.updateViewLayout(this, mParams);
    }

    
    private void openOrCloseControlView() {
        MyWindowManager manager = MyWindowManager.getInstance();
        if (!manager.isControlViewExists())
            manager.createControlView(getContext());
        else
            manager.removeControlView(getContext());
    }

    
    private int getStatusBarHeight() {
        if (statusBarHeight == 0) {
            try {
                Class<?> c = Class.forName("com.android.internal.R$dimen");
                Object o = c.newInstance();
                Field field = c.getField("status_bar_height");
                int x = (Integer) field.get(o);
                statusBarHeight = getResources().getDimensionPixelSize(x);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return statusBarHeight;
    }
}

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程网。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

Android仿IOS系统悬浮窗效果

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

iOS实现应用悬浮窗效果

本文实例为大家分享了iOS实现应用悬浮窗效果的具体代码,供大家参考,具体内容如下 需求 在一个app应用的最顶部添加一个悬浮窗,就像ios系统AssistiveTouch 可以左右滑动,但是最终会停在左边或右边。 实现思路 在应用的视图的最
2022-05-24

Android悬浮窗效果怎么实现

要实现Android的悬浮窗效果,可以采用以下几种方法:使用系统提供的WindowManager类来创建一个悬浮窗口。可以通过以下步骤实现:在AndroidManifest.xml文件中添加SYSTEM_ALERT_WINDOW权限。创建
2023-10-22

Android仿知乎悬浮功能按钮FloatingActionButton效果

前段时间在看属性动画,恰巧这个按钮的效果可以用属性动画实现,所以就来实践实践。效果基本出来了,大家可以自己去完善。 首先看一下效果图:我们看到点击FloatingActionButton后会展开一些item,然后会有一个蒙板效果,这都是这个
2022-06-06

Android实现带磁性的悬浮窗体效果

本文实例讲述了Android实现带磁性的悬浮窗体效果。分享给大家供大家参考,具体如下: 带磁性的悬浮窗体,类似于360绿色小人 主要实现的是: 1.悬浮所有窗体之上 2.有吸引力,吸附于屏幕边上 3.有点击效果 下面我就实现上面三点,简单封
2022-06-06

Kotlin如何实现Android系统悬浮窗

本篇内容介绍了“Kotlin如何实现Android系统悬浮窗”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!Android 弹窗浅谈我们知道
2023-06-22

Android UI仿QQ好友列表分组悬浮效果

本文实例为大家分享了Android UI仿QQ好友列表分组悬浮效果的具体代码,供大家参考,具体内容如下楼主是在平板上測试的。图片略微有点大,大家看看效果就好 接下来贴源代码: PinnedHeaderExpandableListView.j
2022-06-06

Android实现桌面悬浮窗、蒙板效果实例代码

现在很多安全类的软件,比如360手机助手,百度手机助手等等,都有一个悬浮窗,可以飘浮在桌面上,方便用户使用一些常用的操作。 今天这篇文章,就是介绍如何实现桌面悬浮窗效果的。 首先,看一下效果图。悬浮窗一共分为两个部分,一个是平常显示的小窗口
2022-06-06

Android仿美团网、大众点评购买框悬浮效果修改版

我之前写了一篇关于美团网,大众点评的购买框效果的文章Android对ScrollView滚动监听,实现美团、大众点评的购买悬浮效果,我自己感觉效果并不是很好,如果快速滑动界面,显示悬浮框的时候会出现一卡的现象,有些朋友说有时候会出现两个布局
2022-06-06

模仿美团点评的Android应用中价格和购买栏悬浮固定的效果

随着移动互联网的快速发展,它已经和我们的生活息息相关了,在公交地铁里面都能看到很多人的人低头看着自己的手机屏幕,从此“低头族”一词就产生了,作为一名移动行业的开发人员,我自己也是一名“低头族”,上下班时间在公交地铁上看看新闻来打发下时间,有
2022-06-06

怎么在Android中实现一个仿微软系统加载动画效果

怎么在Android中实现一个仿微软系统加载动画效果?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。实现步骤:初始化五个圆球分别设置中心点,方便画圆利用ValueAnimat
2023-06-14

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录