当前位置: 首页 > news >正文

【Android】PopupWindow实现长按菜单

在这里插入图片描述

三三要成为安卓糕手
高能预警,又是炸裂的一局

一:PopupWindow实现长按菜单

需求分析:长按消息,显示复制翻译转发

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要两个xml布局

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"xmlns:tools="http://schemas.android.com/tools"android:id="@+id/main"android:layout_width="match_parent"android:layout_height="match_parent"android:orientation="vertical"android:padding="16dp"tools:context=".popupwindow.PopupWindowActivity"><Buttonandroid:id="@+id/btn_show_bottom_menu"android:layout_width="match_parent"android:layout_height="wrap_content"android:text="显示一个底部弹窗"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><LinearLayoutandroid:layout_width="match_parent"android:layout_height="wrap_content"android:orientation="horizontal"android:padding="10dp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toBottomOf="@id/btn_show_bottom_menu"><TextViewandroid:id="@+id/tv_message"android:layout_width="0dp"android:layout_height="wrap_content"android:layout_weight="1"android:background="@drawable/bg_chat_message"android:padding="20dp"android:textColor="@color/my_blue"android:text="hello,my name is lao sun,i'm from Chinasdadasdsadsadasdsadassadsad!"android:textSize="28sp" /><ImageViewandroid:id="@+id/iv_avatar"android:layout_width="60dp"android:layout_height="60dp"android:layout_marginLeft="5dp"android:layout_marginTop="30dp"android:src="@drawable/icon_face1" /></LinearLayout></androidx.constraintlayout.widget.ConstraintLayout>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

需要注意的一个点:因为我们使用的是.9图片,当看不到文字的时候,添加一个内边距能解决

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:app="http://schemas.android.com/apk/res-auto"android:id="@+id/parent"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@drawable/bg_msg_opreation"android:paddingLeft="12dp"android:paddingRight="12dp"android:paddingBottom="16dp"><TextViewandroid:id="@+id/tv_copy"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/black"android:padding="8dp"android:text="复制"android:textColor="@color/white"android:textSize="18sp"app:layout_constraintStart_toStartOf="parent"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_translate"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/black"android:padding="8dp"android:text="翻译"android:textColor="@color/white"android:textSize="18sp"app:layout_constraintLeft_toRightOf="@+id/tv_copy"app:layout_constraintTop_toTopOf="parent" /><TextViewandroid:id="@+id/tv_report"android:layout_width="wrap_content"android:layout_height="wrap_content"android:background="@color/black"android:padding="8dp"android:text="转发"android:textColor="@color/white"android:textSize="18sp"app:layout_constraintLeft_toRightOf="@+id/tv_translate"app:layout_constraintTop_toTopOf="parent" /></androidx.constraintlayout.widget.ConstraintLayout>

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

public class PopupWindowActivity extends AppCompatActivity {private static final String TAG = "PopupWindowActivity";private PopupWindow popupWindow;private PopupWindow msgOpetationPopupWindow;private TextView tvMessage;private View msgOperationParent;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_popup_window);ConstraintLayout parent = findViewById(R.id.main);findViewById(R.id.btn_show_bottom_menu).setOnClickListener(view ->{//显示在哪个根布局,位置,xy坐标popupWindow.showAtLocation(parent, Gravity.BOTTOM,0,0);setBackgroundAlpha(0.3f);});/*** 处理msgOperationPopupWindow弹窗的显示逻辑* 估算高度*/
//        tvMessage = findViewById(R.id.tv_message);
//        tvMessage.setOnLongClickListener(new View.OnLongClickListener() {
//            @Override
//            public boolean onLongClick(View v) {
//                showMsgLongMenuBySimple();
//                return false;
//            }
//        });/*** 处理msgOperationPopupWindow弹窗的显示逻辑* 精确高度*/tvMessage = findViewById(R.id.tv_message);tvMessage.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {//先让PopupWindow显示一次msgOpetationPopupWindow.showAsDropDown(tvMessage,0,0);//当msgOperationParent控件完全绘制完毕后,执行润里面的代码msgOperationParent.post(new Runnable() {@Overridepublic void run() {if(msgOpetationPopupWindow.isShowing()){msgOpetationPopupWindow.dismiss();int tvMessageWidth = tvMessage.getWidth()/3;int tvMessageHeight = tvMessage.getHeight();
//                            int popupWindowHeight = msgOpetationPopupWindow.getHeight();int msgOperationParentHeight = msgOperationParent.getHeight();int x = tvMessageWidth;int y = -(tvMessageHeight + msgOperationParentHeight);Log.i(TAG, "onLongClick: popupWindowHeight = " + msgOperationParentHeight);msgOpetationPopupWindow.showAsDropDown(tvMessage,x,y);}}});return false;}});createBottomPopupWindow();createMsgPopupWindow();}/*** 估算高度展示PopupWindow*/private void showMsgLongMenuBySimple() {int width = tvMessage.getWidth()/2;//把PopupWindow显示在某个控件的底部,xy偏移量int height = tvMessage.getHeight();int autoHeight = 180;int popupWindowHeight = msgOpetationPopupWindow.getHeight();Log.i(TAG, "onLongClick: PopupWindowHeight = " + popupWindowHeight);msgOpetationPopupWindow.showAsDropDown(tvMessage,width,-(height + autoHeight));}private void createBottomPopupWindow(){View view = getLayoutInflater().inflate(R.layout.layout_bottom_menu_popupwindow, null);popupWindow = new PopupWindow(view,ViewGroup.LayoutParams.MATCH_PARENT,ViewGroup.LayoutParams.WRAP_CONTENT);popupWindow.setOutsideTouchable(true);//如果弹窗消失,就把透明度修改回来popupWindow.setOnDismissListener(new PopupWindow.OnDismissListener() {@Overridepublic void onDismiss() {setBackgroundAlpha(1.0f);}});}/*** 设置透明度* @param f*/private void setBackgroundAlpha(float f){WindowManager.LayoutParams layoutParams = getWindow().getAttributes();layoutParams.alpha = f;getWindow().setAttributes(layoutParams);}/*** 创建消息弹窗*/private void createMsgPopupWindow(){View view = getLayoutInflater().inflate(R.layout.layout_msg_operation,null);msgOpetationPopupWindow = new PopupWindow(view,ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);msgOpetationPopupWindow.setOutsideTouchable(true);msgOperationParent = view.findViewById(R.id.parent);}
}

二:粗略定义弹窗位置

1:创建PopupWindow

上一章已经细讲过,这里尽量不啰嗦

简述:把复制粘贴的布局转为视图,转载到PopupWindow上创建,设置外部可触摸后关闭,最后在主函数中调用这个方法

    /*** 创建消息弹窗*/private void createMsgPopupWindow(){View view = getLayoutInflater().inflate(R.layout.layout_msg_operation,null);msgOpetationPopupWindow = new PopupWindow(view,ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);msgOpetationPopupWindow.setOutsideTouchable(true);//这行代码后面会解释msgOperationParent = view.findViewById(R.id.parent);}

2:弹窗显示位置关联控件

        /*** 处理msgOperationPopupWindow弹窗的显示逻辑*/TextView tvMessage = findViewById(R.id.tv_message);tvMessage.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {int width = tvMessage.getWidth()/2;//把PopupWindow显示在某个控件的底部,xy偏移量int height = tvMessage.getHeight();int autoHeight = 180;//                int popupWindowHeight = msgOpetationPopupWindow.getHeight();
//                Log.i(TAG, "onLongClick: PopupWindowHeight = " + popupWindowHeight);msgOpetationPopupWindow.showAsDropDown(tvMessage,width,-(height + autoHeight));return false;}});

(1)showAsDropDown

将PopupWindow显示在某个控件的底部

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这里我们让复制粘贴显示在消息框下面,再去设置偏移量

x轴偏移消息框宽度的一半,y轴偏移消息框高度+估算 (使用估算这种方法一般可以覆盖90%的场景,精确等会展示)

提问:这里msgOpetationPopupWindow.getHeight()能获取到弹窗的高度吗?

不能,因为它没有被真实的显示,所以获取不到

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(2)方法效果展示

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传
外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

(3)setOnLongClickListener

设置长按这个操作的监听器,new View.OnLongClickListener()

三:精确估计弹窗位置

对弹窗显示的位置要求比较精细的情况下,估算法就不管用了。

因为PopupWindow没有真正的加入到当前的页面,如何获取它的高度??

方案:在弹窗显示之前先让它显示一遍,第一次显示后就可以获取到它的高度了

1:获取PopupWindow高度

声明为成员变量,方便下面访问它的高度和宽度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

        /*** 处理msgOperationPopupWindow弹窗的显示逻辑* 精确高度*/tvMessage = findViewById(R.id.tv_message);tvMessage.setOnLongClickListener(new View.OnLongClickListener() {@Overridepublic boolean onLongClick(View v) {//先让PopupWindow显示一次msgOpetationPopupWindow.showAsDropDown(tvMessage,0,0);//当msgOperationParent控件完全绘制完毕后,执行润里面的代码msgOperationParent.post(new Runnable() {@Overridepublic void run() {if(msgOpetationPopupWindow.isShowing()){msgOpetationPopupWindow.dismiss();int tvMessageWidth = tvMessage.getWidth()/3;int tvMessageHeight = tvMessage.getHeight();
//                            int popupWindowHeight = msgOpetationPopupWindow.getHeight();int msgOperationParentHeight = msgOperationParent.getHeight();int x = tvMessageWidth;int y = -(tvMessageHeight + msgOperationParentHeight);Log.i(TAG, "onLongClick: popupWindowHeight = " + msgOperationParentHeight);msgOpetationPopupWindow.showAsDropDown(tvMessage,x,y);}}});return false;}});

2:post方法的作用

为毛弹窗高度还是-2,因为第一次显示后,立刻就关闭弹窗,此时弹窗视图来不及被创建,需要等第一次的PopupWindow真的显示出来了,才能获取到它的高度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

解决方案:我们去通过监听它的根布局加载成功后,通过根布局获取弹窗的高度

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

这句代码的作用就展示出来了兄弟,声明为成员变量,便于使用

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

调post方法,+改名

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

获取PopupWindow根布局的高度,其实就是PopupWindow本身的高度

3:isShowing

弹窗是否已经显示

4:dismiss

关闭弹窗,你看这不就用上了这个方法,需要我们手动关闭

四:收获

总结:对弹窗偏移量的精准度要求不高,就选第一种估算法;高的话就选第二种

遇到问题了,重要的是有解决方案,也就是解决问题的思路,至于解决问题的工具先学都来的及

http://www.dtcms.com/a/308546.html

相关文章:

  • 难以逾越的夏天
  • 小架构step系列31:处理异常
  • documentPictureInPicture API 教程
  • IK 字段级别词典的升级之路
  • 14day-ai入门-人工智能基础学习-OpenCV-图像预处理4
  • 2683. 相邻值的按位异或
  • GXHT30温湿度传感器可兼容SHT30
  • NMOS防反接电路分析
  • [特殊字符] 数字孪生 + 数据可视化:实战经验分享,让物理世界数据 “会说话”
  • ubuntu18.04 部署nfs服务
  • 第15届蓝桥杯C++青少组中级组选拔赛(STEMA)2024年3月10日真题
  • Java与MySQL AES加密解密实战指南
  • pytest vs unittest: 区别与优缺点比较
  • #C语言——学习攻略:深挖指针路线(五)--回调函数,qsort函数,qsort函数的模拟实现
  • ACOSRAR改进连续蚁群算法用于优化复杂环境下无人机路径规划,Matlab代码实现
  • 中烟创新参编的《软件和信息技术服务行业企业环境社会治理信息披露指南》标准正式发布
  • 树形DP-核心基础
  • 《质光相济:Three.js中3D视觉的底层交互逻辑》
  • 直击WAIC | 百度袁佛玉:加速具身智能技术及产品研发,助力场景应用多样化落地
  • 虚幻基础:模型穿模
  • 产品型号:PCD231B101产品类型:信号隔离转换模块
  • Redis学习14-认识哨兵机制
  • cesium视锥体
  • 【C#】基于SharpCompress实现压缩包解压功能
  • TDengine 中 TDgp 中添加算法模型(预测分析)
  • Spring Security之初体验
  • 智慧社区项目开发(四)——前后端登录认证相关功能实现解析
  • QT Word模板 + QuaZIP + LibreOffice,跨平台方案实现导出.docx文件后再转为.pdf文件
  • 安全月报 | 傲盾DDoS攻击防御2025年7月简报
  • 功能强大编辑器