可拖动、可靠边的 popupWindow 实现

0 背景

  开发要实现一个可以拖动的圆角小窗,要求松手时,哪边近些靠哪边。并且还规定了拖动范围。样式如下:
在这里插入图片描述

1 实现

首先把 PopupWindow 的布局文件 pop.xml 实现

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="88dp"
    android:layout_height="132dp"
    android:background="@drawable/radius_12"
    android:id="@+id/mini_popup"
    android:visibility="visible">

    <com.google.android.material.imageview.ShapeableImageView
        android:id="@+id/iv_live_cover"
        android:layout_width="88dp"
        android:scaleType="fitXY"
        android:layout_height="132dp"
        android:background="@color/purple_200"
        app:shapeAppearanceOverlay="@style/MiniDialogRoundedImageStyle" />

    <ImageView
        android:id="@+id/iv_close"
        android:layout_width="16dp"
        android:layout_height="16dp"
        android:layout_alignParentRight="true"
        android:layout_marginTop="4dp"
        android:layout_marginRight="4dp"
        android:src="@color/teal_200" />
</RelativeLayout>

布局中圆角和 PopupWindow 的动画 style.xml

    <!-- 圆角图片 -->
    <style name="MiniDialogRoundedImageStyle">
        <item name="cornerFamily">rounded</item>
        <item name="cornerSize">12dp</item>
    </style>

    <!-- PopupWindow 的动画效果 -->
    <style name="PopupWindowAnimation">
        <item name="android:windowEnterAnimation">@anim/live_popup_window_in_anim</item>
    </style>

radius_12.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <corners android:radius="12dp"/>
    <solid android:color="@color/white"/>
</shape>

MyPopupWindow.java

package com.example.myapplication.popupwindow;

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.content.res.Resources;
import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.PopupWindow;


import com.bumptech.glide.Glide;
import com.example.myapplication.R;

public class MyPopupWindow extends PopupWindow {
    private Context mContext;
    private View mRootView;

    // 背景
    private ImageView mBackground;

    // 关闭弹窗
    private ImageView mIvClose;


    // 弹窗的移动范围
    private int mMinX;
    private int mMinY;
    private int mMaxX;
    private int mMaxY;

    // 屏幕宽高
    private int mScreenWidth;

    public MyPopupWindow(Context context) {
        super(context);
        mContext = context;
        mRootView = View.inflate(mContext, R.layout.pop, null);

        mScreenWidth = getScreenWidth(mContext);
        mMinX = dp2px(12);
        mMaxX = mScreenWidth - dp2px(12) - dp2px(88);
        mMinY = dp2px(12);
        mMaxY = dp2px(500);

        // 为了保证整体是圆角形状
        mRootView.findViewById(R.id.mini_popup).setClipToOutline(true);
        initView();
    }

    private void initView() {
        setContentView(mRootView);
        mBackground = mRootView.findViewById(R.id.iv_live_cover);
        mIvClose = mRootView.findViewById(R.id.iv_close);

        mIvClose.setOnClickListener(view -> this.dismiss());
        // 小窗的宽高
        setHeight(dp2px(132));
        setWidth(dp2px(88));
        this.setTouchInterceptor(new View.OnTouchListener() {
            int orgX, orgY;
            int offsetX, offsetY;

            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                switch (motionEvent.getAction()) {
                    case MotionEvent.ACTION_DOWN:
                        orgX = (int) motionEvent.getX();
                        orgY = (int) motionEvent.getY();
                        break;
                    case MotionEvent.ACTION_MOVE:
                        offsetX = (int) motionEvent.getRawX() - orgX;
                        offsetY = (int) motionEvent.getRawY() - orgY;
                        // 限制 x 坐标
                        offsetX = Math.max(offsetX, mMinX);
                        offsetX = Math.min(offsetX, mMaxX);
                        // 限制 y 坐标
                        offsetY = Math.max(offsetY, mMinY);
                        offsetY = Math.min(offsetY, mMaxY);
                        update(offsetX, offsetY, -1, -1, true);
                        break;
                    case MotionEvent.ACTION_UP:
                        // 小窗靠边
                        if (offsetX < mScreenWidth / 2) {
                            offsetX = mMinX;
                        } else {
                            offsetX = mMaxX;
                        }
                        update(offsetX, offsetY, -1, -1, true);
                        break;
                }
                // 避免 view 中的其他点击事件被吞掉
                return false;
            }
        });
        // 设置小窗背景
        this.setBackgroundDrawable(mContext.getResources().getDrawable(R.drawable.abc_vector_test));
        // 出现的动画
        this.setAnimationStyle(R.style.PopupWindowAnimation);
    }

    public void show(View anchor) {
        this.showAtLocation(anchor, Gravity.NO_GRAVITY, mMaxX, mMaxY);
    }

    @SuppressLint("CheckResult")
    public void setBackground(String url) {
        if (url != null && !TextUtils.isEmpty(url))
            Glide.with(mContext).load(url).into(mBackground);
    }

    public int dp2px(float dpValue) {
        return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
    }
    public int getScreenWidth(Context context) {
        DisplayMetrics localDisplayMetrics = new DisplayMetrics();
        ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(localDisplayMetrics);
        return localDisplayMetrics.widthPixels;
    }
}

最后在 MainActivity 中使用

mTextView = findViewById(R.id.myView);
if (mMyPopupWindow == null) {
    mMyPopupWindow = new MyPopupWindow(MainActivity.this);
}
mTextView.post(() -> {
    mMyPopupWindow.show(mTextView);
});

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/165333.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

2024年全网最全的Jmeter+ant+jenkins实现持续集成教程

jmeterantjenkins持续集成 一、下载并配置jmeter 首先下载jmeter工具&#xff0c;并配置好环境变量&#xff1b;参考&#xff1a;https://www.cnblogs.com/YouJeffrey/p/16029894.html jmeter默认保存的是.jtl格式的文件&#xff0c;要设置一下bin/jmeter.properties,文件内容…

当小白遇到电脑程序不完全退出怎么办?

方法一&#xff1a;使用软件默认的退出方式 此处拿百度网盘举例&#xff1a; 用户登录网盘后&#xff1a; 如果直接点击右上角红框中的x 程序不会完全退出 百度网盘点击“x”后仍然在后台继续运行,可以在电脑桌面的右下角看到图标仍然在&#xff0c;证明程序肯定还在运行 部…

有成效的工作

从开始上班起&#xff0c;听到过工作是做不完得。 大概的意思&#xff0c;现在的工作做完了&#xff0c;就会分配新的工作。所以总也做不完。 如果是做不完的&#xff0c;那么是不是在一个岗位上就一直干着呢。既然这个很难成立。那其实工作是可以干得完的。 一个岗位的终结&am…

qemu + busybox + 内核实验环境搭建(2023-11)

主要是参考网上的例子&#xff0c;网上的一些例子可能用的busybox 老旧&#xff0c;编译各种问题&#xff0c;以及rootfs hda的方式或者ramfs的方式。可能有些概念还是不清楚&#xff0c;以下是最终完成测试成功的案例。 下载kernel https://cdn.kernel.org/pub/linux/kernel…

动态改标题

<el-dialog :title"showTitle" :visible"showDialog" close"close"> </el-dialog>使用计算属性 computed: {showTitle() {//这里根据点击的是否有具体点击的那个id来判断return this.form.id ? "编辑部门" : "新增部…

警惕.360勒索病毒,您需要知道的预防和恢复方法。

引言&#xff1a; 网络威胁的演变无常&#xff0c;.360勒索病毒作为一种新兴的勒索软件&#xff0c;以其狡猾性备受关注。本文将深入介绍.360勒索病毒的特点&#xff0c;提供解决方案以恢复被其加密的数据&#xff0c;并分享一系列强化网络安全的预防措施。如果您在面对被勒索…

【洛谷 B2002】Hello,World!(顺序结构)

Hello,World! 题目描述 编写一个能够输出 Hello,World! 的程序。 提示&#xff1a; 使用英文标点符号&#xff1b;Hello,World! 逗号后面没有空格。H 和 W 为大写字母。 输入格式 输出格式 样例 #1 样例输入 #1 无样例输出 #1 Hello,World!思路 #include 是一个预处…

Shell条件测试练习

1、取出/etc/passwd文件的第6行&#xff1b; [rootshell ~]# head -6 /etc/passwd | tail -1 sync:x:5:0:sync:/sbin:/bin/sync [rootshell ~]# sed -n 6p /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync [rootshell ~]# awk NR6 /etc/passwd sync:x:5:0:sync:/sbin:/bin/sync 2…

数据结构之链表练习与习题详细解析

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言程序设计————KTV C语言小游戏 C语言进阶 C语言刷题 数据结构初阶 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂。 目录 1.前言 2.习题解…

一个C语言程序的分析:运行速度和文件大小以及变量初始值

环境 Ubuntu 22.04gcc 11.4.0Window 11Microsoft Visual Studio Community 2022 (64-bit) - Current Version 17.6.2 运行速度 一个C程序 test1.c 如下&#xff1a; int array[30000][30000];int main() {for (int i 0; i < 30000; i)for (int j 0; j < 30000; j) …

Windows安装Vmware 虚拟机

目录 一、Vmware 虚拟机介绍 二、Vmware 虚拟机的三种网络模式 2.1桥接模式 2.2仅主机模式 2.3NAT 网络地址转换模式 三、Vmware 虚拟机的安装 一、Vmware 虚拟机介绍 VMware Workstation Pro 是一款可以在个人电脑的操作系统上创建一个完全与主机操作系统隔离的 虚拟机&…

windows pgsql 数据库 数据目录更改

一.先停止postgres服务 cmd命令 services.msc找到服务停止 二.修改注册表 cmd命令 regedit找到路径 \HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\postgresql-x64-13 将“&#xff0d;D”后的目录名修改为新的数据目录位置即可&#xff0c;如果目录路径中含有…

MyISAM和innoDB两种引擎的对比

innoDB 3.23就有了innoDB引擎&#xff0c;5.5成为了默认引擎&#xff0c;支持外键 是一种事务型引擎&#xff0c;可以保证完整提交和回滚 更新、删除比较多的场景&#xff0c;推荐使用innoDB 不过innoDB对内存要求高&#xff0c;因为索引和数据存到一个表了&#xff1b;写操作…

记录:RK3568显示异常。

最近调一个RK3568的新板子&#xff0c;板子其它接口功能都调试ok。可唯独在适配显示时发现&#xff0c;HDMI和MIPI显示均出现异常。当系统启动要进入桌面时候内核就开始报错。 因为这套源码之前在其它的板子上适配过&#xff0c;所以第一反应就是硬件问题或者是那个电压没配置…

[C/C++]数据结构 LeetCode:用栈实现队列

题目描述: 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xff1a; 实现 MyQueue 类&#xff1a; void push(int x) 将元素 x 推到队列的末尾int pop() 从队列的开头移除并返回元素int peek() 返…

7个最佳开源免费库存/仓库管理系统(WMS)

库存/仓库管理软件是一种用于帮助企业管理库存、仓储位置和交付过程的软件系统。这种类型的软件对于拥有大量库存和多个仓库的企业非常有用。 库存/仓库管理软件的作用包括以下几个方面&#xff1a; &#xff08;1&#xff09;减少库存节约成本 通过跟踪库存水平和存储位置&…

defer和async

如果两个属性浏览器都不兼容&#xff0c;推荐把<script>标签放到底部 一般情况下&#xff0c;浏览器在解析html源文件时&#xff0c;如果遇到外部的<script>标签&#xff0c;解析过程就会先暂停&#xff0c;这时会对script进行加载&#xff0c;执行两个过程&…

4种经典的限流算法与集群限流

0、基础知识 1000毫秒内&#xff0c;允许2个请求&#xff0c;其他请求全部拒绝。 不拒绝就可能往db打请求&#xff0c;把db干爆~ interval 1000 rate 2&#xff1b; 一、固定窗口限流 固定窗口限流算法&#xff08;Fixed Window Rate Limiting Algorithm&#xff09;是…

矩阵运算_矩阵的协方差矩阵/两个矩阵的协方差矩阵_求解详细步骤示例

1. 协方差矩阵定义 在统计学中&#xff0c;方差是用来度量单个随机变量的离散程度&#xff0c;而协方差则一般用来刻画两个随机变量的相似程度。 参考&#xff1a; 带你了解什么是Covariance Matrix协方差矩阵 - 知乎 2. 协方差矩阵计算过程 将输入数据A进行中心化处理得到A…

element-plus 表格-合并单元格

利用表格:span-method"" 方法实现合并单元格 合并前 合并后 重点代码generateIndexGroups &#xff0c;找到合并的单元格的index号 代码实现如下 <template><h2>实现表格的合并</h2><div><!-- :span-method"arraySpanMethod&quo…
最新文章