Android 自定义PopupWindow,实现下拉框

1、效果图

2、前言

1、页面由 MagicIndicator + ViewPager2 + Fragment 实现;

2、下拉框是基于WindowManager实现;

3、我使用PopupWindow实现下拉框时,发现一个问题,PopupWindow 在窗口显示的情况下,无法直接从外部修改布局,必须先dismiss

PopupWindow源码:              

public void setContentView(View contentView) {if (isShowing()) {return;}... ...
}public void showAsDropDown(View anchor, int xoff, int yoff, int gravity) {if (isShowing() || !hasContentView()) {return;}... ...
}

4、如果先dismiss再添加,属于重新创建布局,切换生硬,会出现闪烁,影响用户体验,就像这样;

那就没办法了,自己实现;

观摩PopupWindow源码发现它是基于windowManager实现的,照葫芦画瓢,自定义一个

3、自定义下拉框

AffiliatedBottomWindow

package com.example.myapplication.common;import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.PixelFormat;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;import androidx.annotation.NonNull;import com.example.myapplication.util.BarUtils;
import com.example.myapplication.util.CustomizeUtils;
import com.example.myapplication.util.ScreenUtils;/*** 挂靠在某个view下面的悬浮窗* <p>* 和PopupWindow一样都是基于WindowManager实现的;* <p>** 和PopupWindow区别:* PopupWindow 在窗口显示的情况下,无法直接从外部修改布局,必须先dismiss,* 再重新创建,切换会出现闪烁,用户体验差;* <p>* PopupWindow源码:* public void setContentView(View contentView) {*      if (isShowing()) {*          return;*      }*      ... ...* }** <p>* 为了避免这种闪烁,基于WindowManager写了这个 AffiliatedBottomWindow 底部挂靠悬浮框;* <p>*** 注意:单例,使用完成后一定要清空,不然无法创建实例 if (instance == null) {}*     public void clear() {*         instance = null;*     }*/
public class AffiliatedBottomWindow {private final Context context;private static CustomizeUtils.AntiShake antiShake;private WindowManager windowManager;@SuppressLint("StaticFieldLeak")private static AffiliatedBottomWindow instance;// 根view@SuppressLint("StaticFieldLeak")private static ViewGroup rootView;// 根view中的子viewprivate ViewGroup rootChildView;// true:使用 根view中的子view 作为内容布局的容器private boolean useRootChildView = false;// 显示/隐藏 过渡动画时长public static int animatorDuration = 200;private WindowManager.LayoutParams windowLayoutParams;private AffiliatedBottomWindow(Context context) {this.context = context;createWindowManager();antiShake = new CustomizeUtils.AntiShake(500);}public WindowManager getWindowManager() {return windowManager;}public void setWindowManager(WindowManager windowManager) {this.windowManager = windowManager;}public AffiliatedBottomWindow getInstance() {return instance;}public WindowManager.LayoutParams getWindowLayoutParams() {return windowLayoutParams;}public void setWindowLayoutParams(WindowManager.LayoutParams windowLayoutParams) {this.windowLayoutParams = windowLayoutParams;}public ViewGroup getRootView() {return rootView;}public void setRootView(ViewGroup rootView) {AffiliatedBottomWindow.rootView = rootView;// 初始化隐藏布局AffiliatedBottomWindow.rootView.setVisibility(View.GONE);}public boolean isUseRootChildView() {return useRootChildView;}public void setUseRootChildView(boolean useRootChildView) {this.useRootChildView = useRootChildView;}// 显示 和 隐藏 过渡透明度动画public static void alphaAnimation(boolean show) {if (antiShake.isFastClick()) {ValueAnimator animator;if (show) {if (rootView.getVisibility() == View.VISIBLE) {return;}rootView.setAlpha(0);rootView.setVisibility(View.VISIBLE);//显示animator = ValueAnimator.ofFloat(0f, 1f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(@NonNull ValueAnimator animation) {float progress = (float) animation.getAnimatedValue();rootView.setAlpha(progress);}});animator.setDuration(animatorDuration);animator.start();} else {if (rootView.getVisibility() == View.GONE) {return;}//隐藏animator = ValueAnimator.ofFloat(1f, 0f);animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(@NonNull ValueAnimator animation) {float progress = (float) animation.getAnimatedValue();rootView.setAlpha(progress);}});animator.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {super.onAnimationEnd(animation);rootView.setVisibility(View.GONE);}});animator.setDuration(animatorDuration);animator.start();}}}/*** 内容布局插入到 根View中** @param context* @param rootView       根布局* @param affiliatedView 挂靠的View,悬浮框会出现在这个View下面*/public static AffiliatedBottomWindow createInstance(Context context,ViewGroup rootView,View affiliatedView) {if (instance == null) {instance = new AffiliatedBottomWindow(context);// 先设置根viewinstance.setRootView(rootView);// 再设置挂靠view,顺序不能乱instance.setAffiliatedView(affiliatedView);}return instance;}/*** 内容布局插入到 根View中的 子View中** @param context* @param rootView          根布局* @param affiliatedView    挂靠的View,悬浮框会出现在这个View下面* @param rootChildView     根view中的子view* @param useChildContainer 是否将 根view中的子view 作为内容布局的容器*/public static AffiliatedBottomWindow createInstance(Context context,ViewGroup rootView,View affiliatedView,ViewGroup rootChildView,boolean useChildContainer) {if (instance == null) {instance = new AffiliatedBottomWindow(context);instance.setUseRootChildView(useChildContainer);instance.setRootChildView(rootChildView);// 先设置根viewinstance.setRootView(rootView);// 再设置挂靠view,顺序不能乱instance.setAffiliatedView(affiliatedView);}return instance;}public void setAffiliatedView(View affiliatedView) {// 设置悬浮框宽/高windowLayoutParams.width = affiliatedView.getWidth();// 剩余空间高度windowLayoutParams.height = ScreenUtils.getScreenHeight(context) - affiliatedView.getBottom();// 设置悬浮框位置windowLayoutParams.gravity = Gravity.TOP;// 减去状态栏高度,沉浸式布局,// 如果不是沉浸式布局,扩展重写此方法windowLayoutParams.y = affiliatedView.getBottom() - BarUtils.getStatusBarHeight(context);// 显示,当前根视图隐藏了,所以不显示windowManager.addView(rootView, windowLayoutParams);}public void setRootChildView(ViewGroup rootChildView) {this.rootChildView = rootChildView;}private void createWindowManager() {if (windowManager != null) {return;}// 创建 windowManager对象windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);// 创建布局参数windowLayoutParams = new WindowManager.LayoutParams();// 设置窗口类型windowLayoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION;// 设置悬浮框不可触摸,默认接收事件,会导致底层view,接收不到事件windowLayoutParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL| WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE;// 背景颜色,设置为透明windowLayoutParams.format = PixelFormat.TRANSPARENT;}// 插入布局public void insertViewLayout(View view) {if (useRootChildView) {if (rootChildView.getChildCount() == 0) {rootChildView.addView(view);} else {rootChildView.removeAllViews();rootChildView.addView(view);}} else {if (rootView.getChildCount() == 0) {rootView.addView(view);} else {rootView.removeAllViews();rootView.addView(view);}}alphaAnimation(true);}// 隐藏窗口public static void dismiss() {alphaAnimation(false);}// 单例,使用完成后一定要清空// 不然 无法创建实例 if (instance == null) {}public void clear() {instance = null;}
}

4、源码  

demo东西比较多,是从自己项目里摘录出来的,扩展了MagicIndicator

核心类:AffiliatedBottomWindow

https://github.com/LanSeLianMa/CustomizeBottomWindow

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试经典 150 题 4 —(数组 / 字符串)— 80. 删除有序数组中的重复项 II

80. 删除有序数组中的重复项 II 方法一 class Solution { public:int removeDuplicates(vector<int>& nums) {int len 0;for(auto num : nums)if(len < 2 || nums[len-2] ! num)nums[len] num;return len;} };方法二 class Solution { public:int removeDupli…

Rabbitmq安装-docker版

1.简介 2.安装消息队列 下载地址https://www.rabbitmq.com/download.html 使用docker方式安装 需要先下载docker&#xff0c;参考文章https://blog.csdn.net/weixin_43917045/article/details/104747341?csdn_share_tail%7B%22type%22%3A%22blog%22%2C%22rType%22%3A%22arti…

操作系统备考学习 day7 (2.3.4 ~ 2.3.5)

操作系统备考学习 day7 第二章 进程与线程2.3 同步与互斥2.3.4 信号量 用信号量实现进程互斥、同步、前驱关系信号量机制实现进程互斥信号量机制实现进程同步信号量机制实现前驱关系 2.3.5 经典同步问题生产者-消费者问题多生产者和多消费者模型抽烟者问题读者-写者问题哲学家进…

使用运放产生各种波形

目录复制 文章目录 RC正弦振荡电路文氏电桥振荡电路移项式正弦波振荡电路 集成函数发生器运算放大器驱动电容性负载峰值检波多通道运放未使用的运放接法 RC正弦振荡电路 文氏电桥振荡电路 这个振荡器起振条件RF > 2R1,起振后又希望RF 2R1产生矛盾怎么办&#xff1f; 将RF换…

centos7终端无图形界面安装tbb

1、官网下载tbb&#xff1a; https://www.intel.com/content/www/us/en/developer/articles/tool/oneapi-standalone-components.html#onetbb 2、终端执行&#xff1a; ./l_tbb_oneapi_p_2021.10.0.49543_offline.sh -a --cli3、cd /opt/intel/oneapi 4、source setvars.sh …

LeetCode 1251. 平均售价

题目链接&#xff1a;1251. 平均售价 题目描述 表&#xff1a;Prices Column NameTypeproduct_idintstart_datedateend_datedatepriceint (product_id&#xff0c;start_date&#xff0c;end_date) 是 prices 表的主键&#xff08;具有唯一值的列的组合&#xff09;。 price…

【LeetCode高频SQL50题-基础版】打卡第3天:第16~20题

文章目录 【LeetCode高频SQL50题-基础版】打卡第3天&#xff1a;第16~20题⛅前言 平均售价&#x1f512;题目&#x1f511;题解 项目员工I&#x1f512;题目&#x1f511;题解 各赛事的用户注册率&#x1f512;题目&#x1f511;题解 查询结果的质量和占比&#x1f512;题目&am…

VS2022配置Opencv

配置环境变量 配置路径 由于新版本VS属性管理器没有Microsoft.cpp.x64.user文件&#xff0c;可以选择直接在Debug x64进行配置 配置包含目录和库目录 配置链接器

红队专题-从零开始VC++远程控制软件RAT-C/S-[2]界面编写及上线

红队专题 招募六边形战士队员1.课前回顾unicode编码 字符串 2.界面编程(下)对话框重载消息函数更改对话框同步更改 3.服务端上线&#xff0c;下线&#xff0c;以及客户端的资源销毁(上)添加socket 变量添加 socket 消息填补config信息创建线程函数 并运行添加Addhost添加 getIt…

Notepad++提取含有特定字符串的行

ctrl M快捷键&#xff0c;进入"标记" 页面 标记所在行–循环查找-- 正则表达式 – 输入关键字 – 全部标记 – Copy Marked Text 关键字格式如下&#xff1a; .*关键字.*ctrl v&#xff0c;粘贴即可。

数据结构与算法(八):排序算法

参考引用 Hello 算法 Github&#xff1a;hello-algo 1. 选择排序 选择排序的工作原理非常直接&#xff1a;开启一个循环&#xff0c;每轮从未排序区间选择最小的元素&#xff0c;将其放到已排序区间的末尾&#xff0c;设数组的长度为 n 初始状态下&#xff0c;所有元素未排序&…

湖南互联网医院|湖南互联网医院牌照办理流程及材料

互联网医牌照&#xff0c;一个让医疗行业焕发数字化新生的通行证。随着时代的进步和技术的发展&#xff0c;互联网已经深入各个行业&#xff0c;医疗领域也不例外。而互联网医牌照的办理流程、内容以及所需材料&#xff0c;则是诸多医疗机构所关注的核心内容。 第一种是实体医…

轻量限制流量?阿里云轻量应用服务器月流量包收费说明

阿里云轻量应用服务器部分套餐限制月流量&#xff0c;轻量应用服务器按照套餐售卖&#xff0c;有的套餐限制月流量&#xff0c;有的不限制流量。像阿里云轻量2核2G3M带宽轻量服务器一年108元和轻量2核4G4M带宽一年297.98元12个月&#xff0c;这两款是不限制月流量的。阿里云百科…

Java学习笔记(一)

目录 一、Java概述 &#xff08;一&#xff09;Java技术体系平台 &#xff08;二&#xff09;Java重要特点 &#xff08;三&#xff09;Java运行机制及运行过程 &#xff08;四&#xff09;JDK &#xff08;五&#xff09;JRE 二、Java的快速入门 &#xff08;一&#…

墨西哥专线空加派一条龙服务性价比高吗?

随着全球贸易的不断发展&#xff0c;越来越多的企业开始关注跨境物流&#xff0c;以满足国际市场的需求。墨西哥专线空加派一条龙服务作为一种便捷、高效的物流解决方案&#xff0c;越来越受到企业的青睐。那么&#xff0c;墨西哥专线空加派一条龙服务的性价比高吗?本文将从服…

企业门户的必备选择,WorkPlus的定制化解决方案

在当今数字化时代&#xff0c;企业门户成为了企业内外沟通与协作的重要基础设施。WorkPlus作为领先的品牌&#xff0c;为企业提供了一站式的企业门户解决方案&#xff0c;旨在提升企业形象、改善内外部沟通与协作效率。本文将深入探讨WorkPlus如何通过定制化的设计&#xff0c;…

fastadmin 后台列表数据多表查询筛选

记录一下fastadmin后台列表数据连表查询筛选提示&#xff1a; 1&#xff1a;下拉框方式搜索 示例&#xff1a; 在对应js文件中添加如下代码&#xff1a; 代码&#xff1a; {field: s.area_id,title: __(所属片区),visible: false,//不显示在列表中operate: ,searchList: $.…

关于Jupyter notebook 创建python3 时进去不能重命名问题及不能编程问题

首先写这篇博客时&#xff0c;已经被这个问题折磨了三天&#xff0c;看了很多博客&#xff0c;其实解决这个问题的关键就是要么没有下pyzmq或者等级太高&#xff0c;要么等级太低&#xff0c;首先我会按照我思路来。 问题如图&#xff1a; 1.自动换行 2.不能重命名 我的解决办…

MOM与MES管理系统有哪些本质上的区别

随着企业业务的不断发展&#xff0c;许多制造企业开始面临车间管理失控、生产不透明等问题。这时候&#xff0c;很多企业选择上线MES生产管理系统来提高生产管理水平。然而&#xff0c;随着企业业务的不断拓展&#xff0c;MES系统也逐渐暴露出其局限性。于是&#xff0c;MOM平台…

水库安全监测方案(实时数据采集、高速数据传输)

​ 一、引言 水库的安全监测对于防止水灾和保障人民生命财产安全至关重要。为了提高水库安全监测的效率和准确性&#xff0c;本文将介绍一种使用星创易联DTU200和SG800 5g工业路由器部署的水库安全监测方案。 二、方案概述 本方案主要通过使用星创易联DTU200和SG800 5g工业路…