/BasePopup

亲,还在为PopupWindow烦恼吗?不如试试BasePopup,你会爱上他的~

Primary LanguageJavaApache License 2.0Apache-2.0

1.x版本 | 2.x版本

Logo图片似乎加载不出来

BasePopup - Android下打造通用便捷的PopupWindow

Release Candy License Api Author

apk体验下载

导航



特性

  • 更简单更精准的控制显示位置,通过Gravity和offset来控制您的PopupWindow
  • 本库为抽象类,对子类几乎没有约束,您完全可以像定制Activity一样来定制您的PopupWindow
  • 支持AnimationAnimator,随意控制您的PopupWindow的动画,再也不用去写蛋疼的xml了
  • 背景变暗、背景换色甚至背景给个Drawable都是一句话的事情
  • 背景模糊亦或是局部模糊也仅仅需要您一句话的配置
  • 返回键控制点击外部Dismiss控制外部事件响应控制三者分离,再也不用担心我的PopupWindow各种按键响应问题
    • 如果不满足默认的事件,没问题,我们还提供了事件传递,您的事件您来把握
  • PopupWindow跟随AnchorView位置不准?屏幕外不消失?在这里,Link方法为您排忧解难
  • 支持链式调用,还在为简单的PopupWindow使用不得不继承库的抽象类而感到烦躁?不妨来试试QuickPopupBuilder,想必您会爱上它的


注意事项

WARN:

  • 请务必仔细阅读本README,每个版本升级请务必查阅更新日志,这可以为您减少不必要弯路
  • 请注意引用版本的问题,Release版本是稳定版,Candy是预览版。
    • Release版本:一般在Candy版本反复验证修复后发布到Release,如果您对稳定性要求较高,请使用Release版本。
    • Candy版本:一般新功能、issue修复都会发布到Candy版本,Candy版本发布比较频繁,但通常会拥有新的功能,如果您喜欢试验新功能同时对稳定性要求不高,请使用Candy版本。
    • Release和Candy两个版本互相切换可能会导致Build失败,这时候您Clean一下Project即可
  • 如果您是以前1.x版本的用户,现在想更新到2.x,请在更新前查阅:1.x迁移到2.x帮助文档

Android P已经适配,感谢@Guolei1130收集的方法。

文章地址:android_p_no_sdkapi_support

本库一开始采用360的方法,但不得不走Native,为了个Popup不得不引入so感觉很不值得,在看到这篇文章后,才想起UnSafe类,因此本库采用方法5。

如果以后UnSafe类移除掉的话,再考虑Native方法。

最后再一次感谢大牛提供的方法~



快速入门


更多使用方法请查看Wiki#使用方法

依赖

Release Candy
Download Download

添加依赖到Gradle(请把{$latestVersion}替换成上面的Jcenter标签所示版本)

	dependencies {
	        implementation 'com.github.razerdp:BasePopup:{$latestVersion}'
	        
	        //candy版本
	        //implementation 'com.github.razerdp:BasePopup_Candy:{$latestVersion}'
	}

配置

模糊配置

从1.9.0-alpha开始支持背景模糊(只需要一个方法:setBlurBackgroundEnable(boolean)

RenderScript最低支持api 17(更低的情况将会使用fastblur),您需要在gradle配置一下代码

defaultConfig {
        renderscriptTargetApi 25
        renderscriptSupportModeEnabled true
    }

普通使用

1.编写您的xml文件

像您平时定制View布局文件一样定制您的PopupWindow布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:background="@android:color/holo_blue_dark"
    android:orientation="vertical"
    >

    <TextView
        android:id="@+id/tx_1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:padding="16dp"
        android:text="test1"
        android:textColor="@color/color_black1"/>

</LinearLayout>

2.创建您的Popup类并继承BasePopupWindow

public class DemoPopup extends BasePopupWindow {
    public DemoPopup(Context context) {
        super(context);
    }

    @Override
    public View onCreateContentView() {
        return null;
    }
}

3.补充对应方法

强烈建议在onCreateContentView()里使用createPopupById()来进行inflate,这样本库才能正确的做出对应的解析和适配

public class DemoPopup extends BasePopupWindow {
    public DemoPopup(Context context) {
        super(context);
    }

    // 必须实现,这里返回您的contentView
    // 为了让库更加准确的做出适配,强烈建议使用createPopupById()进行inflate
    @Override
    public View onCreateContentView() {
        return createPopupById(R.layout.popup_normal);
    }
    
    // 以下为可选代码(非必须实现)
    // 返回作用于PopupWindow的show和dismiss动画,本库提供了默认的几款动画,这里可以自由实现
    @Override
    protected Animation onCreateShowAnimation() {
        return getDefaultScaleAnimation(true);
    }

    @Override
    protected Animation onCreateDismissAnimation() {
        return getDefaultScaleAnimation(false);
    }
}

4.show!

展示PopupWindow的方法有三种种,分别是showPopupWindow()showPopupWindow(View anchor)showPopupWindow(int x, int y)

new DemoPopup(getContext()).showPopupWindow();
//new DemoPopup(getContext()).showPopupWindow(v);
//new DemoPopup(getContext()).showPopupWindow(x,y);

这三个方法有不同的含义:

  • showPopupWindow():无参传入,此时PopupWindow参考对象为屏幕(或者说整个DecorView),Gravity的表现就像在FrameLayout里面的Gravity表现一样,表示其处于屏幕的哪个方位
  • showPopupWindow(View anchor):传入AnchorView,此时PopupWindow参考对象为传入的anchorView,Gravity的表现则意味着这个PopupWindow应该处于目标AnchorView的哪个方位
  • showPopupWindow(int x, int y):传入位置信息,此时PopupWindow将会在指定位置弹出。

建议:如果PopupWindow需要重复展示或者保留状态,建议作为成员变量使用,而不要作为局部变量每次都创建

关于Gravity的更多api,请查看:Wiki-Api:Gravity

例子展示:

  • showPopupWindow()无参传入
gravity = CENTER
上述例子中xml写明了layout_gravity=center
gravity = RIGHT | CENTER_VERTICAL

  • showPopupWindow(View v)传入anchorView
gravity = CENTER
上述例子中xml写明了layout_gravity=center
gravity = RIGHT | CENTER_VERTICAL

  • showPopupWindow(int x, int y)传入位置x,y坐标
gravity = CENTER
上述例子中xml写明了layout_gravity=center


QuickPopupBuilder链式调用

QuickPopupBuilder支持链式调用生成一个基于QuickPopup的PopupWindow,该Builder旨在快速建立一个简单的不包含复杂逻辑的PopupWindow,如上述案例,避免过于简单的PopupWindow也要继承BasePopupWindow,导致存在过多的类。

示例代码


如果您并不需要很详细的定义一个PopupWindow,您也可以选择QuickPopupBuilder采取链式写法快速编写出一个Popup以使用。

注意:默认QuickPopupBuilder.QuickPopupConfig配置中PopupWindow动画为淡入淡出

        QuickPopupBuilder.with(getContext())
                .contentView(R.layout.popup_normal)
                .config(new QuickPopupConfig()
                        .gravity(Gravity.RIGHT | Gravity.CENTER_VERTICAL)
                        .withClick(R.id.tx_1, new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                Toast.makeText(getContext(), "clicked", Toast.LENGTH_LONG).show();
                            }
                        }))
                .show();
		//.show(anchorView);
show() show(anchorView)


Api(请看Wiki)

请看wiki(陆续完善中)

Link👉WIKI


更新日志 (历史更新)

  • 【Candy】2.1.6-alpha(2019/01/03)

    • 修复wrap_content下,在某个view显示同时底部空间不足以完整显示内容时无法完整显示内容的问题
  • 【Release】2.1.5(2019/01/02)

    • 新年新气象~祝大家新年快乐,zhu事顺意-V-
    • 2.1.5 如期新年发布,改动如下:
      • 优化了获取是否展示虚拟按键的方法
      • 利用了另外一个*方法来判断全面屏是否含有虚拟按键
      • 针对showPopupWindow(anchorview)同时clipToScreen(true)时,无法完整展示满屏的View的问题
      • 增加setAlignBackgroundGravity()方法,背景对齐的位置由您来制定~
      • 增加update(int width ,int height)方法
      • 修复构造器传入width/height失效的问题,增加setWidth/setHeight方法
      • 构造器增加延迟加载参数,如果您的Popup需要提前传参后,请在构造其中传入true以确认延迟加载
        • 如果使用延迟加载,初始化时机由您来制定,您需要调用delayInit()方法来进行BasePopup的初始化
  • 【Candy】2.1.5

    • 2.1.5-prerelease(2018/12/29)
      • 优化了获取是否展示虚拟按键的方法
    • 2.1.5-beta5(2018/12/29)
      • 用了另外一个*方法来判断全面屏是否有虚拟按键
    • 2.1.5-beta4(2018/12/27)
      • 针对showPopupWindow(anchorview)同时clipToScreen(true)时,无法完整展示满屏的view的问题
        • 该方法目前可能不稳定(某些极限情况没想到~)
    • 2.1.5-beta3(2018/12/25)
      • 针对小米手势全面屏高度获取不准确的修复
      • 说实话,那么多魔改ROM,全面屏高度获取不准这东西也能改API,我表示无力吐槽了
    • 2.1.5-beta2(2018/12/25)
      • 修复2.1.5-beta默认backgroundgravity的问题
    • 2.1.5-beta(2018/12/25)
      • 增加setAlignBackgroundGravity()方法,背景对齐的位置由您来制定~
      • 增加update(int width ,int height)方法
      • 修复构造器传入width/height失效的问题,增加setWidth/setHeight方法
    • 2.1.5-alpha2(2018/12/23)
      • 构造器增加延迟加载参数,如果您的Popup需要提前传参后,请在构造其中传入true以确认延迟加载
        • 如果使用延迟加载,初始化时机由您来制定,您需要调用delayInit()方法来进行BasePopup的初始化
    • 2.1.5-alpha(2018/12/23)
      • 适配刘海屏全面屏(双显示屏暂不适配)
      • 感谢#114的提供~
      • Release年后发布,如果您有需要,请更新到此candy版。
  • 【Release】2.1.4(2018/12/21)

    • 建议更新到这个版本!
    • 非常抱歉~因为一时疏忽忘记合并一些东西,导致2.1.3版本在不拦截事件的情况下,无anchorView弹窗会导致位置问题,在2.1.4重新合并了代码,对此造成的影响,深表歉意。
      • 以后的版本一定会经过3个或以上的candy迭代仔细检查后再发!
  • 【Release】2.1.3(2018/12/21)

    • 正式发布2.1.3release
    • 增加linkTo(View)方法
    • 支持update方法来跟随view或者指定位置更新
    • 全面优化系统原有的popupwindow定位方法,全版本统一。
    • 2.x的坑基本补完
    • 19年,我们再见-V-
  • 【Candy】2.1.3-alpha2(2018/12/20)

    • 增加linkTo(View)方法,跟随anchorView状态?一个方法就足够了~
    • 2.x的坑基本补完~如无意外,这个功能将会是18年最后一个功能性更新了


例子预览

GravityPopupFrag LocatePopupFrag
AnyPosPopupFrag UpdatePopupFrag
BlurSlideFromBottomPopupFrag CommentPopup
SlideFromBottomPopup InputPopup
ListPopup MenuPopup

打赏(看在我那么努力维护的份上。。。给个零食呗~)

微信 支付宝

交流群:590777418

因为目前还有朋友圈项目,建立了一个交流群,出于懒得管理那么多,所以如果有想法或者优化建议或者其他问题,欢迎加入“朋友圈交流群”


常见问题

Q:如何取消默认的背景颜色

A:调用setBackgroundColor(Color.TRANSPARENT)或者setBackground(0)

Q:如何在dismiss()时不执行退出动画

A:调用dismiss(false)或者dismissWithOutAnimate()

Q:点击popupwindow背景部分不想让popupwindow隐藏怎么办

A:设置setAllowDismissWhenTouchOutside(false)

Q:Service里无法弹出

A:PopupWindow需要windowToken,因此ApplicationContext或者Service里面是无法弹出的,建议通过发出事件通知栈顶Activity来弹出

Q:如何不让PopupWindow的蒙层覆盖状态栏

A:设置setPopupWindowFullScreen(false)

Q:如何点击back键不关闭pop

A:设置setBackPressEnable(false)

Q:为什么设置setAllowInterceptTouchEvent(false)后,蒙层或者背景模糊都消失了

A:在2.0.0到2.0.9之间,setAllowInterceptTouchEvent()不影响蒙层或背景,但从2.1.0开始,不再开启这个黑科技,而是选择跟官方保持同步,原因在于如果背景模糊或者有蒙层,那么该PopupWindow理应拦截事件,而不应该穿透事件,否则不应该拥有背景蒙层。

同时,因为系统PopupWindow默认是clipToScreen,也就是限制PopupWindow在屏幕内显示,当view边缘超过屏幕的时候,PopupWindow会反向移动以完整展示内容,因此如果您的PopupWindow需要突破屏幕显示(比如高度是全屏,但展示在某个view下面,此时bottom大于屏幕底部),请设置setClipToScreen(false)

Q:根布局高度match_parentwrap_content的区别

A:当根布局是match_parent的时候,basepopup会做一定的差异处理。
当您设置了setClipToScreen(true)时,如果您的根布局是match_parent,那么意味着您的布局最大高度仅为屏幕高度,如果您的根布局是wrap_content,那么最大高度可能是可以突破屏幕高度的。
例如demo中的全屏listview

<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"   // 请留意这里
    android:background="@android:color/white"
    >

    <ListView
        android:id="@+id/popup_list"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:divider="@color/line_bg"
        android:dividerHeight="0.5dp"
        android:scrollbars="vertical"
        />
</RelativeLayout>
layout_height = match_parent layout_height = wrap_content


留意两张图的listview底部区别,其中wrap_content底部已经突破屏幕底部,无法完整显示。


License

Apache-2.0