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.rhkb.cn/news/152091.html

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

相关文章

(c语言进阶)指针的进阶

一.字符指针 1.一般应用 &#xff08;1&#xff09;%c的应用 &#xff08;2&#xff09;%s的应用 字符指针没有权限通过解引用去改变指针指向的值 2.笔试题 题目&#xff1a;判断输出结果 int main() { const char* p1 "abcdef"; const char* p2 "…

无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动,并且客户端端口与服务器端口相同

“/”应用程序中的服务器错误。 无法向会话状态服务器发出会话状态请求。请确保 ASP.NET State Service (ASP.NET 状态服务)已启动&#xff0c;并且客户端端口与服务器端口相同。如果服务器位于远程计算机上&#xff0c;请检查 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Se…

【云备份】

文章目录 [toc] 1 :peach:云备份的认识:peach:1.1 :apple:功能了解:apple:1.2 :apple:实现目标:apple:1.3 :apple:服务端程序负责功能:apple:1.4 :apple:服务端功能模块划分:apple:1.5 :apple:客户端程序负责功能:apple:1.6 :apple:客户端功能模块划分:apple: 2 :peach:环境搭建…

[VC++]圆形进度条

[VC]圆形进度条 源码开发环境&#xff1a;VC6.0 WIN10 64位下编译通过利用绘制饼图的原理&#xff0c;来制作的圆形进度条&#xff0c;可以显示百分比。软件运行截图如下&#xff1a; 附件源码下载(点击下载&#xff09;

基于ensp的园区网络搭建综合实验

目录 &#x1f552; 1. 技术介绍&#x1f552; 2. 需求分析&#x1f558; 2.1 项目背景和需求&#x1f558; 2.2 项目需求分析 &#x1f552; 3. 网络结构设计&#x1f552; 4. 网络拓扑规划&#x1f552; 5. 网络设备基本配置&#x1f558; 5.1 规划VLAN&#x1f558; 5.2 MST…

【RK3588】YOLO V5在瑞芯微板子上部署问题记录汇总

YOLO V5训练模型部署到瑞芯微的板子上面&#xff0c;官方是有给出案例和转过详情的。并且也提供了Python版本的推理代码&#xff0c;以及C语言的代码。 但是&#xff0c;对于转换过程中的细节&#xff0c;哪些需要改&#xff1f;怎么改&#xff1f;如何改&#xff0c;和为什么…

Java中树形菜单的实现方式(超全详解!)

前言 这篇文中&#xff0c;我一共会用两种方式来实现目录树的数据结构&#xff0c;两种写法逻辑是一样的&#xff0c;只是一种适合新手理解&#xff0c;一种看着简单明了但是对于小白不是很好理解。在这里我会很详细的讲解每一步代码&#xff0c;主要是方便新人看懂&#xff0…

typescript: Builder Pattern

/*** file: CarBuilderts.ts* TypeScript 实体类 Model* Builder Pattern* 生成器是一种创建型设计模式&#xff0c; 使你能够分步骤创建复杂对象。* https://stackoverflow.com/questions/12827266/get-and-set-in-typescript* https://github.com/Microsoft/TypeScript/wiki/…

4.Docker 搭建 redis6

1.下载redis docker pull redis:6.2.62.创建需要挂载的宿主机文件夹 mkdir -p /data/redis/conf mkdir -p /data/redis/data3.配置redis 切换到/data/redis/conf文件夹下&#xff0c;创建redis.conf,复制redis.conf配置文件内容到redis.conf文件中&#xff0c;然后按下键盘 …

黑豹程序员-架构师学习路线图-百科:AJAX

文章目录 1、什么是AJAX2、发展历史3、工作原理4、一句话概括 1、什么是AJAX Ajax即Asynchronous&#xff08;呃森可乐思&#xff09; Javascript And XML&#xff08;异步JavaScript和XML&#xff09; 在 2005年被Jesse James Garrett&#xff08;杰西詹姆斯加勒特&#xff09…

GD32F103x 定时器

1. 定时器的基本介绍 STM32的定时器主要分为三种&#xff1a;高级定时器、通用定时器、基本定时器。 即&#xff1a;高级定时器具有捕获/比较通道和互补输出&#xff0c;死区时间&#xff0c;通用定时器只有捕获/比较通道&#xff0c;基本定时器没有以上两者。 1. 基本定时…

网络安全:六种常见的网络攻击手段

1、什么是VPN服务&#xff1f; 虚拟专用网络&#xff08;或VPN&#xff09;是您的设备与另一台计算机之间通过互联网的安全连接。VPN服务可用于在离开办公室时安全地访问工作计算机系统。但它们也常用于规避政府审查制度&#xff0c;或者在电影流媒体网站上阻止位置封锁&#…

【React】深入理解React组件状态State

目录 一、何为State二、如何定义State三、如何判断是否为State四、如何正确使用State1、用setState修改State2、State的更新是异步的①、代码示例 3、State更新会被合并①、组件状态例子②、当只需要修改状态title时&#xff0c;只需要将修改后的title传给setState③、React会合…

Windows安装Node.js

1、Node.js介绍 ①、Node.js简介 Node.js是一个开源的、跨平台的JavaScript运行环境&#xff0c;它允许开发者使用JavaScript语言来构建高性能的网络应用程序和服务器端应用。Node.js的核心特点包括&#xff1a; 1. 事件驱动: Node.js采用了事件驱动的编程模型&#xff0c;通…

力扣 -- 647. 回文子串

解题步骤&#xff1a; 参考代码&#xff1a; class Solution { public:int countSubstrings(string s) {int ns.size();vector<vector<bool>> dp(n,vector<bool>(n));//无需初始化int ret0;//一定要从下往上填写每一行for(int in-1;i>0;i--){//每一行的i…

jvm--对象实例化及直接内存

文章目录 1. 创建对象2. 对象内存布局3. 对象的访问定位4. 直接内存&#xff08;Direct Memory&#xff09; 1. 创建对象 创建对象的方式&#xff1a; new最常见的方式、Xxx 的静态方法&#xff08;单例模式&#xff09;&#xff0c;XxxBuilder/XxxFactory 的静态方法Class 的…

CVE-2023-36845:Juniper Networks Junos OS EX远程命令执行漏洞

Juniper Networks Junos OS EX远程命令执行漏洞(CVE-2023-36845) 复现 0x01 前言 免责声明&#xff1a;请勿利用文章内的相关技术从事非法测试&#xff0c;由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失&#xff0c;均由使用者本人负责&#…

UE4 自带体积云应用

新建空关卡 点击该选项 全部点击一遍 拖进场景

uniapp 在uni.scss 根据@mixin定义方法 、通过@include全局使用

在官方文档中提及到uni.scss中变量的使用&#xff0c;而我想定义方法&#xff0c;这样写css样式更方便 一、官方文档的介绍 根据官方文档我知道&#xff0c;在这面定义的变量全局都可使用。接下来我要在这里定义方法。 二、在uni.scss文件中定义方法 我在uni.scss文件中定义了…

不用休眠的 Kotlin 并发:深入对比 delay() 和 sleep()

本文翻译自&#xff1a; https://blog.shreyaspatil.dev/sleepless-concurrency-delay-vs-threadsleep 毫无疑问&#xff0c;Kotlin 语言中的协程 Coroutine 极大地帮助了开发者更加容易地处理异步编程。该特性中封装的诸多高效 API&#xff0c;可以确保开发者花费更小的精力去…