Skip to content

2. 内置的弹窗实现

Jun edited this page Jul 24, 2021 · 23 revisions

为了方便使用,已经内置了几种常见弹窗的实现;从1.8.5版本开始,所有内置弹窗都支持设置你自己的布局。

显示确认和取消对话框

   new XPopup.Builder(getContext()).asConfirm("我是标题", "我是内容",
                           new OnConfirmListener() {
                               @Override
                               public void onConfirm() {
                                  toast("click confirm");
                               }
                           })
                           .show();

显示带输入框的确认和取消对话框

    new XPopup.Builder(getContext()).asInputConfirm("我是标题", "请输入内容。",
                            new OnInputConfirmListener() {
                                @Override
                                public void onConfirm(String text) {
                                    toast("input text: " + text);
                                }
                            })
                            .show();

显示中间弹出的列表弹窗

    new XPopup.Builder(getContext())
                            //.maxWidth(600)
                            .asCenterList("请选择一项", new String[]{"条目1", "条目2", "条目3", "条目4"},
                            new OnSelectListener() {
                                @Override
                                public void onSelect(int position, String text) {
                                    toast("click " + text);
                                }
                            })
                            .show();

显示中间弹出的加载框

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

显示从底部弹出的列表弹窗

    // 这种弹窗从 1.0.0版本开始实现了优雅的手势交互和智能嵌套滚动
    new XPopup.Builder(getContext())
                            .asBottomList("请选择一项", new String[]{"条目1", "条目2", "条目3", "条目4", "条目5"},
                            new OnSelectListener() {
                                @Override
                                public void onSelect(int position, String text) {
                                    toast("click " + text);
                                }
                            })
                            .show();

显示依附于某个View或者某个点的弹窗

    new XPopup.Builder(getContext())
                            .atView(v)  // 依附于所点击的View,内部会自动判断在上方或者下方显示
                            .asAttachList(new String[]{"分享", "编辑", "不带icon"},
                            new int[]{R.mipmap.ic_launcher, R.mipmap.ic_launcher},
                            new OnSelectListener() {
                                @Override
                                public void onSelect(int position, String text) {
                                    toast("click " + text);
                                }
                            })
                            .show();

如果是想依附于某个View的触摸点,则需要先watch该View,然后当单击或长按触发的时候去显示:

    // 必须在事件发生前,调用这个方法来监视View的触摸
    final XPopup.Builder builder = new XPopup.Builder(getContext())
                    .watchView(view.findViewById(R.id.btnShowAttachPoint));
    view.setOnLongClickListener(new View.OnLongClickListener() {
        @Override
        public boolean onLongClick(View v) {
            builder.asAttachList(new String[]{"置顶", "复制", "删除"}, null,
                                    new OnSelectListener() {
                                        @Override
                                        public void onSelect(int position, String text) {
                                            toast("click " + text);
                                        }
                                    })
                                    .show();
            return false;
        }
    });

asAttachList方法内部是对AttachPopupView的封装,如果你的布局不是列表,可以继承AttachPopupView实现自己想要的布局。 AttachPopupView会出现在目标的上方或者下方,如果你想要出现在目标的左边或者右边(像微信朋友圈那样点赞的弹窗),可以继承HorizontalAttachPopupView,然后编写你的布局即可。

最简单示例如下:

    public class CustomAttachPopup extends HorizontalAttachPopupView {
        public CustomAttachPopup(@NonNull Context context) {
            super(context);
        }
    
        @Override
        protected int getImplLayoutId() {
            return R.layout.custom_attach_popup;
        }
    
        @Override
        protected void onCreate() {
            super.onCreate();
            findViewById(R.id.tv_zan).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    ToastUtils.showShort("赞");
                }
            });
            findViewById(R.id.tv_comment).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    ToastUtils.showShort("评论");
                }
            });
        }
    }

大图浏览弹窗

这种弹窗多用于App内列表中图片进行详细展示的场景,用法如下:

    //当你点击图片的时候执行以下代码:
    // 多图片场景(你有多张图片需要浏览)
    //srcView参数表示你点击的那个ImageView,动画从它开始,结束时回到它的位置。
    new XPopup.Builder(getContext()).asImageViewer(imageView, position, list, new OnSrcViewUpdateListener() {
            @Override
            public void onSrcViewUpdate(ImageViewerPopupView popupView, int position) {
                // 注意这里:根据position找到你的itemView。根据你的itemView找到你的ImageView。
                // Demo中RecyclerView里面只有ImageView所以这样写,不要原样COPY。
                // 作用是当Pager切换了图片,需要更新源View,
                popupView.updateSrcView((ImageView) recyclerView.getChildAt(position));
            }
        }, new SmartGlideImageLoader())
        .show();

    // 单张图片场景
    new XPopup.Builder(getContext())
        .asImageViewer(imageView, url, new SmartGlideImageLoader())
        .show();
    

** 说明:XPopup是个弹窗库,本身不负责加载图片,但为了方便使用,我已经提供了一个智能的SmartGlideImageLoader以供使用,它默认支持一般大图加载(如3000x4000); 如果要它支持超大超长图片(20000x40000)且不会OOM,需在构造传参SmartGlideImageLoader(true,0)即可,第2个参数是加载失败的展位图。

但如果你用的不是Glide,或者需求太复杂,请参考去实现即可。 如果图片加载不出来,往往是网络问题,一般跟XPopup无关,请自行检查。 **

关闭弹窗

先拿到弹窗对象,以Loading弹窗为例,其他也是一样:

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

执行消失:

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

复用项目已有布局

1.8.5版本开始,所有内置的弹窗支持设置自定义的布局了。如果你项目中已经有写好的弹窗布局,而想用XPopup提供的动画和交互能力,也是完全没有问题的。目前支持设置自定义布局的弹窗有:

  • Confirm弹窗,就是确认和取消弹窗
  • 带输入框的Confirm弹窗
  • Loading弹窗
  • 带列表的Attach弹窗,Center弹窗和Bottom弹窗

假设,你想使用XPopup的Confirm弹窗,但布局想用自己的,只需要这样设置一下,其他不用动即可:

new XPopup.Builder(getContext())
          .asConfirm(null, "您可以复用项目已有布局,来使用XPopup强大的交互能力和逻辑封装,弹窗的布局完全由你自己控制。\n" +
                                "需要注意的是:你自己的布局必须提供一些控件Id,否则XPopup找不到View。\n具体需要提供哪些Id,请查看WIKI[内置弹窗]一章。",
                        "关闭", "XPopup牛逼",
                        new OnConfirmListener() {
                            @Override
                            public void onConfirm() {
                                toast("click confirm");
                            }
                        }, null, R.layout.my_confim_popup)//绑定已有布局
            .show();

这样布局就是您自己的了,动画和交互XPopup会帮你完成。但是需要注意的是,你自己提供的布局必须包含一些id,否则XPopup无法找到View;id必须有,控件你可以随意组合与摆放。具体如下:

  • Confirm弹窗必须包含的TextView以及id有:tv_title,tv_content,tv_cancel,tv_confirm
  • 带输入框的Confirm弹窗在Confirm弹窗基础上需要增加一个id为et_input的EditText
  • Loading弹窗,如果你想显示一个Loading文字说明,则必须有一个id为tv_title的TextView;如果不需要文字说明,则没要求
  • 带列表的弹窗会多一个bindItemLayout()方法用来绑定item布局
  • 其他不在多说,看bindLayout方法说明,会说明要求哪些id

上面说要求的View是TextView,其实是TextView的子类也当然可以,比如Button。每种内置弹窗的bindLayout和bindItemLayout的要求都在方法说明上,无需记忆,用的时候查看下方法的说明即可。