XPopup 实现通用弹窗动画效果

under Android  tag     Published on July 1st , 2021 at 09:16 am

GitHub 地址:https://github.com/li-xiaojun/XPopup

XPopup

轻松实现应用上各种常见弹窗

每种弹窗的功能和使用场景如下(来源上面 GitHub 地址):

  • CenterPopupView:中间弹窗的弹窗,比如:确认取消对话框,Loading弹窗等,如果不满意默认的动画效果,可以设置不同的动画器
  • BottomPopupView:从底部弹出的弹窗,比如:从底部弹出的分享弹窗,知乎的从底部弹出的评论弹窗,抖音从底部弹出的评论弹窗。这种弹窗 带有智能的嵌套滚动和手势拖动,默认不能设置其他的动画器;但调用enableDrag(false)时会禁用嵌套滚动和手势拖动,此时支持设置任意的动 画器
  • AttachPopupView/HorizontalAttachPopupView:Attach弹窗是需要依附于某个点或者某个View来显示的弹窗,效果和系统的PopupMenu类似;其 中AttachPopupView会出现在目标的上方或者下方。如果希望想要微信朋友圈点赞弹窗那样的效果,出现在目标的左边或者右边,则需要继承 HorizontalAttachPopupView来做。 如果你想要带气泡的弹窗,XPopup还提供了带气泡的BubbleAttachPopupView和BubbleHoriztontalAttachPopupView使用。
  • DrawerPopupView:从界面的左边或者右边弹出的像DrawerLayout那样的弹窗,Drawer弹窗本身是横向滑动的,但对ViewPager和HorizontalScrollView等横向滑动控件做了兼容,在弹窗内部可以放心使用它们
  • PartShadowPopupView:局部阴影弹窗,因为它的阴影效果是局部的,并不全都是阴影。效果类似于淘宝商品列表下拉筛选弹窗,内部其实是Attach 弹窗的一种实现,因为仍然要依附于某个View出现
  • FullScreenPopupView:全屏弹窗,看起来和Activity一样。该弹窗其实是继承Center弹窗进行的一种实现,可以设置任意的动画器
  • ImageViewerPopupView:大图浏览弹窗,就像微信朋友圈点击缩略图看大图的效果一样;但是体验比微信的好多了。
  • PositionPopupView:自由定位弹窗,如果你想让弹窗显示左上角,或者右上角,或者任意位置,并且不需要依附任何View,此时你需要它。
  • LoadingPopupView:中间弹出的加载框,可定义加载中的文字。

本篇简单介绍 CenterPopupViewBottomPopupViewAttachListPopupViewBubbleAttachPopupViewPartShadowPopupViewImageViewerPopupViewLoadingPopupView 的使用。

添加依赖

implementation 'com.github.li-xiaojun:XPopup:2.4.3'

根目录的build.gradle中添加如下:

allprojects {
    repositories {
        ...
        maven { url 'https://jitpack.io' }
    }
}

编译版本必须 >= 29

compileSdkVersion 29

便捷方法

所有便捷方法(当然是只能显示一种弹窗效果):

便捷方法使用方便,Ctrl 进入方法能看到方法说明。需要使用自定义布局时要注意自定义布局的 id,有强制要求要存在的 id,asAttachList()提到的atView():设置弹窗依附的 View ,Attach 弹窗必须设置这个。

asImageViewer()

单张图片场景。多图片场景(你有多张图片需要浏览)点击上面链接。

if (imageViewerPopupView == null) {
    imageViewerPopupView = new XPopup.Builder(this)
        .asImageViewer(viewBinding.iv, getResources().getDrawable(R.mipmap.img), new XPopupImageLoader() {
            @Override
            public void loadImage(int position, @NonNull Object uri, @NonNull ImageView imageView) {
                //如果你确定你的图片没有超级大的,直接这样写就行
                Glide.with(imageView).load(uri).apply(new RequestOptions().override(SIZE_ORIGINAL)).into(imageView);
            }

            @Override
            public File getImageFile(@NonNull Context context, @NonNull Object uri) {
                //实现这个方法,返回uri对应的缓存文件,可参照下面的实现,内部保存图片会用到。如果你不需要保存图片这个功能,可以返回null。
                return null;
            }
        });
}

asLoading()

LoadingPopupView popupView = new XPopup.Builder(getContext())
                            .asLoading("正在加载中...")
                            .show();

自定义弹窗

一:根据自己的需求编写一个类继承对应的弹窗;

二:重写 getImplLayoutId() 返回弹窗的布局,在 onCreate 中像 Activity 那样编写你的逻辑即可。

注意:自定义弹窗本质是一个自定义View,但是只需重写一个参数的构造,其他的不要重写,所有的自定义弹窗都是这样。

CenterPopupView

public class CenterDialog extends CenterPopupView {
    private Context context;

    public CenterDialog(@NonNull Context context) {
        super(context);
        this.context = context;
    }

    @Override
    protected int getImplLayoutId() {
        return R.layout.xpopup_center;
    }

    @Override
    protected void onCreate() {
        super.onCreate();
        // 逻辑代码,让弹窗消失直接 dismiss()
        ((TextView) findViewById(R.id.tv_title)).setText("自定义中间弹窗");
        ((TextView) findViewById(R.id.tv_content)).setText("跟所有的烦恼说拜拜");
        findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                dismiss();
            }
        });
        findViewById(R.id.tv_confirm).setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                Toast.makeText(context, "拜拜", Toast.LENGTH_SHORT).show();
                dismiss();
            }
        });
    }
}
使用该弹窗
if (centerDialog == null) {
    centerDialog = (CenterDialog) new XPopup.Builder(this)
        .asCustom(new CenterDialog(this));
    }
centerDialog.show();

BottomPopupView 和 CenterPopupView 一样使用

BottomPopupView 从底部弹出,CenterPopupView 从中间弹出

BubbleAttachPopupView

带气泡的

public class AttachPopupCenter extends BubbleAttachPopupView {
    private Context context;

    public AttachPopupCenter(@NonNull Context context) {
        super(context);
        this.context = context;
    }

    @Override
    protected int getImplLayoutId() {
        return R.layout.xpopup_attach;
    }

    @Override
    protected void onCreate() {
        super.onCreate();
        // 逻辑
    }
}
使用

atView():设置弹窗依附的 View ,Attach 弹窗必须设置这个,不管是带气泡的还是不带气泡的。
isCenterHorizontal():是否与目标水平居中,比如:默认情况下 Attach 弹窗依靠着目标的左边或者右边,如果 isCenterHorizontal 为 true,则与目标水平居中对齐

if (attachPopupCenter == null) {
    attachPopupCenter = (AttachPopupCenter) new XPopup.Builder(this)
        .popupPosition(PopupPosition.Bottom)
        .atView(tvAttachCenter)
        .isCenterHorizontal(true)
        .asCustom(new AttachPopupCenter(this));
}
attachPopupCenter.show();

PartShadowPopupView

写一个类继承它,和上面弹窗一样。

使用

PartShadowPopupView 也需要使用atView()

if (shadowPopup == null) {
    shadowPopup = (PartShadowPopup) new XPopup.Builder(this)
        .atView(tvShadow)
        .asCustom(new PartShadowPopup(this));
}
shadowPopup.show();

常用设置

这些功能都在 Builder 之后,显示弹窗之前设置。

new XPopup.Builder(getContext())
    .isViewMode(true) // 是否切换为View实现,默认是Dialog实现,具体区别看方法说明
    .hasShadowBg(true) // 是否有半透明的背景,默认为true
    .shadowBgColor()   //单独给弹窗设置背景阴影色,默认用全局的值
    .animationDuration() //单独给弹窗设置动画时长,默认走全局的值
    .hasBlurBg(true) // 是否有高斯模糊的背景,默认为false
    .isDestroyOnDismiss(true) //是否在消失的时候销毁资源,默认false。如果你的弹窗对象只使用一次,非常推荐设置这个,可以杜绝内存泄漏。如果会使用多次,千万不要设置
    .dismissOnBackPressed(true) // 按返回键是否关闭弹窗,默认为true
    .dismissOnTouchOutside(true) // 点击外部是否关闭弹窗,默认为true
    .isClickThrough(false)    //点击弹窗外部时,是否允许点击到下方界面,默认false
    .notDismissWhenTouchInView(view) //如果点击了传入的View则弹窗不消失,点击弹窗外部的其他地方再消失,
    .autoFocusEditText(true)    //是否让输入框自动获取焦点,默认为true
    .autoOpenSoftInput(true) //是否弹窗显示的同时打开输入法,只在包含输入框的弹窗内才有效,默认为false
    .popupAnimation(PopupAnimation.ScaleAlphaFromCenter) // 设置内置的动画
    .customAnimator(null) // 设置自定义的动画器
    .moveUpToKeyboard(false) // 软键盘弹出时,弹窗是否移动到软键盘上面,默认为true
    .popupPosition(PopupPosition.Right)//手动指定弹窗出现在目标的什么位置,对Attach和Drawer类型弹窗生效
    .hasStatusBarShadow(false) //是否有状态栏阴影,目前对Drawer弹窗和FullScreen弹窗生效
    .positionByWindowCenter(false) //默认是false,是否以屏幕中心进行定位,默认是false,
                                   //为false时根据Material范式进行定位,主要影响Attach系列弹窗
    .isLightStatusBar(true) //是否是亮色状态栏,默认false;亮色模式下,状态栏图标和文字是黑色
    .hasStatusBar(true) //是否显示状态栏,默认显示,一般不用设置设置, 当你App强制全屏时需要设置
    .hasNavigationBar(true) //是否显示导航栏,默认显示,一般不用设置,当你App修改了导航栏颜色的时候需要设置
    .keepScreenOn(false) //是否保持屏幕常亮,默认false
    .offsetX(-10) //弹窗在x方向的偏移量
    .offsetY(-10) //弹窗在y方向的偏移量
    .maxWidth(10) //设置弹窗的最大宽度,如果重写弹窗的getMaxWidth(),以重写的为准
    .maxHeight(10) //设置弹窗的最大高度,如果重写弹窗的getMaxHeight(),以重写的为准
    .popupWidth(10) //设置弹窗的宽度,受最大宽度限制,如果重写弹窗的getPopupHeight(),以重写的为准
    .popupHeight(10) //设置弹窗的高度,受最大高度限制,如果重写弹窗的getPopupHeight(),以重写的为准
    .isCenterHorizontal(true)//是否和目标水平居中,比如:默认情况下Attach弹窗依靠着目标的左边或者右边,
                             //如果isCenterHorizontal为true,则与目标水平居中对齐
    .isRequestFocus(false)//默认为true,默认情况下弹窗会抢占焦点,目的是为了响应返回按键按下事件;如果为false,则不抢焦点
    .enableShowWhenAppBackground(true) //默认为false,是否允许弹窗在应用后台的时候也能显示。需要开启悬浮窗权限,一行代码即可实现
    .enableDrag(true) //是否启用拖拽,默认为true,目前对Bottom和Drawer弹窗有用
    .isThreeDrag(true) //是否启用三阶拖拽(类似于BottomSheet),默认为false,目前对Bottom弹窗有用。如果enableDrag(false)则无效。
    .isDarkTheme(true)  //是否启用暗色主题
    .borderRadius(10)  //为弹窗设置圆角,默认是15,对内置弹窗生效
    .autoDismiss(false) // 操作完毕后是否自动关闭弹窗,默认为true;比如点击ConfirmPopup的确认按钮,默认自动关闭;如果为false,则不会关闭
    .setPopupCallback(new SimpleCallback() { //设置显示和隐藏的回调
        @Override
        public void onCreated() { 
             // 弹窗内部onCreate执行完调用
        }
        @Override
        public void beforeShow() {
             super.beforeShow();
             Log.e("tag", "beforeShow,在每次show之前都会执行,可以用来进行多次的数据更新。");
        }
        @Override
        public void onShow() {
            // 完全显示的时候执行
        }
        @Override
        public void onDismiss() {
            // 完全隐藏的时候执行
        }
        //如果你自己想拦截返回按键事件,则重写这个方法,返回true即可
        @Override
        public boolean onBackPressed() {
             ToastUtils.showShort("我拦截的返回按键,按返回键XPopup不会关闭了");
             return true; //默认返回false
        }
        //监听软键盘高度变化,高度为0说明软键盘关闭,反之则打开
        @Override
        public void onKeyBoardStateChanged(int height) {
            super.onKeyBoardStateChanged(height);
            Log.e("tag", "onKeyBoardStateChanged height: " + height);
        }
        //监听弹窗拖拽,适用于能拖拽的弹窗
        @Override
        public void onDrag(BasePopupView popupView, int value, float percent) {
        }
    })
    .asXXX() //所有的设置项都要写在asXXX()方法调用之前

执行消失

//有三个消失方法可供选择:
popupView.dismiss();  //立即消失
popupView.delayDismiss(300);//延时消失,有时候消失过快体验可能不好,可以延时一下
popupView.smartDismiss(); //会等待弹窗的开始动画执行完毕再进行消失,可以防止接口调用过快导致的动画不完整。

本文由 surface 创作,采用 知识共享署名4.0 国际许可协议进行许可,转载前请务必署名
  文章最后更新时间为:September 29th , 2021 at 04:36 pm
分享到:Twitter  Weibo  Facebook