Skip to content

3. 自定义弹窗

李晓俊 edited this page May 6, 2019 · 20 revisions

当你自定义弹窗的时候,需要选择继承CenterPopupViewBottomPopupViewAttachPopupView/HorizontalAttachPopupViewDrawerPopupViewPartShadowPopupViewFullScreenPopupView其中之一。

每种弹窗的功能和使用场景如下:

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

自定义Center弹窗

    class CustomPopup extends CenterPopupView {
            //自定义弹窗本质是一个自定义View,但是只需重写这个构造,其他的不用重写
            public CustomPopup(@NonNull Context context) {
                super(context);
            }
            // 返回自定义弹窗的布局
            @Override
            protected int getImplLayoutId() {
                return R.layout.custom_popup;
            }
            // 执行初始化操作,比如:findView,设置点击,或者任何你弹窗内的业务逻辑
            @Override
            protected void onCreate() {
                super.onCreate();
                findViewById(R.id.tv_close).setOnClickListener(new OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        dismiss(); // 关闭弹窗
                    }
                });
            }
            // 设置最大宽度,看需要而定
            @Override
            protected int getMaxWidth() {
                return super.getMaxWidth();
            }
            // 设置最大高度,看需要而定
            @Override
            protected int getMaxHeight() {
                return super.getMaxHeight();
            }
            // 设置自定义动画器,看需要而定
            @Override
            protected PopupAnimator getPopupAnimator() {
                return super.getPopupAnimator();
            }
            /**
              * 弹窗的宽度,用来动态设定当前弹窗的宽度,受getMaxWidth()限制
              *
              * @return
              */
             protected int getPopupWidth() {
                 return 0;
             }

             /**
              * 弹窗的高度,用来动态设定当前弹窗的高度,受getMaxHeight()限制
              *
              * @return
              */
             protected int getPopupHeight() {
                 return 0;
             }
        }

使用自定义弹窗:

    new XPopup.Builder(getContext())
            .asCustom(new CustomPopup(getContext()))
            .show();

自定义Attach弹窗

注意:默认情况下所有的弹窗背景都是透明的,但Attach弹窗比较特殊,由于内部实现的原因,会自带白色的背景。当你重写getPopupImplLayout()返回一个自定义的布局时,自定义布局的背景并不能覆盖弹窗的背景,这样就会有2个背景。Attach弹窗提供了getPopupBackground()方法用来自定义弹窗的背景,重写这个方法即可。比如:

public class CustomAttachPopup2 extends AttachPopupView {
    public CustomAttachPopup2(@NonNull Context context) {
        super(context);
    }
    @Override
    protected int getImplLayoutId() {
        return R.layout.custom_attach_popup2;
    }
    //如果要自定义弹窗的背景,不要给布局设置背景图片,重写这个方法返回一个Drawable即可
    @Override
    protected Drawable getPopupBackground() {
        return getResources().getDrawable(R.drawable.shadow_bg);
    }
}

自定义DrawerLayout类型弹窗

对于DrawerLayout类型的弹窗,我只能帮你做好弹窗效果和手势交互。里面的UI和逻辑是无法帮你完成的,所以需要自定义一个弹窗,继承DrawerPopupView。代码非常简单,如下:

    public class CustomDrawerPopupView extends DrawerPopupView {
        public CustomDrawerPopupView(@NonNull Context context) {
            super(context);
        }
        @Override
        protected int getImplLayoutId() {
            return R.layout.custom_drawer_popup;
        }
        @Override
        protected void onCreate() {
            super.onCreate();
            findViewById(R.id.btn).setOnClickListener(new OnClickListener() {
                @Override
                public void onClick(View v) {
                    Toast.makeText(getContext(), "nothing!!!", Toast.LENGTH_SHORT).show();
                }
            });
        }
    }

使用自定义的DrawerLayout弹窗:

    new XPopup.Builder(getContext())
            .popupPosition(PopupPosition.Right)//右边
            .hasStatusBarShadow(true) //启用状态栏阴影
            .asCustom(new CustomDrawerPopupView(getContext()))
            .show();

自定义局部阴影弹窗

这种效果的阴影是局部范围的,也要依附于某个View,在其上方或者下方显示。常见于列表条件筛选弹窗,比如京东或者淘宝的商品列表筛选。同样我只能帮你把复杂的交互效果做了,弹窗里面的UI和逻辑需要你自己继承PartShadowPopupView来做,这当然非常简单。 最简单的示例如下:

    public class CustomPartShadowPopupView extends PartShadowPopupView {
        public CustomPartShadowPopupView(@NonNull Context context) {
            super(context);
        }
        @Override
        protected int getImplLayoutId() {
            return R.layout.custom_part_shadow_popup; // 编写你自己的布局
        }
        @Override
        protected void onCreate() {
            super.onCreate();
            // 实现一些UI的初始和逻辑处理
        }
    }

显示的时候仍然需要指定atView显示,内部会智能判断应该如何展示以及使用最佳的动画器:

    new XPopup.Builder(getContext())
        .atView(ll_container)
        .asCustom(new CustomPartShadowPopupView(getContext()))
        .show();

自定义Bottom类型的弹窗

自定义Bottom类型的弹窗会比较常见,默认Bottom弹窗带有手势交互和嵌套滚动;如果您不想要手势交互可以调用enableDrag(false)方法关闭。

如果弹窗内有输入框,在弹出输入法的情况下,弹窗默认会贴附在输入法之上,并且保证不会盖住输入框;目前Center和Bottom类型弹窗有此效果。

请注意:弹窗的宽高是自适应的,大部分情况下都应该将弹窗布局的高设置为wrap_content;除非你希望得到一个高度撑满的弹窗。

Demo中有一个模仿知乎评论的实现,代码如下:

    public class ZhihuCommentPopup extends BottomPopupView {
        VerticalRecyclerView recyclerView;
        public ZhihuCommentPopup(@NonNull Context context) {
            super(context);
        }
        @Override
        protected int getImplLayoutId() {
            return R.layout.custom_bottom_popup;
        }

        @Override
        protected void onCreate() {
            super.onCreate();
            recyclerView = findViewById(R.id.recyclerView);

            ArrayList<String> strings = new ArrayList<>();
            for (int i = 0; i < 30; i++) {
                strings.add("");
            }
            CommonAdapter<String> commonAdapter = new CommonAdapter<String>(R.layout.adapter_zhihu_comment, strings) {
                @Override
                protected void bind(@NonNull ViewHolder holder, @NonNull String s, int position) {}
            };
            commonAdapter.setOnItemClickListener(new MultiItemTypeAdapter.SimpleOnItemClickListener(){
                @Override
                public void onItemClick(View view, RecyclerView.ViewHolder holder, int position) {
                    dismiss();
                }
            });
            recyclerView.setAdapter(commonAdapter);
        }
        // 最大高度为Window的0.85
        @Override
        protected int getMaxHeight() {
            return (int) (XPopupUtils.getWindowHeight(getContext())*.85f);
        }
    }

自定义全屏弹窗

public class CustomFullScreenPopup extends FullScreenPopupView {
    public CustomFullScreenPopup(@NonNull Context context) {
        super(context);
    }
    @Override
    protected int getImplLayoutId() {
        return R.layout.custom_fullscreen_popup;
    }
    @Override
    protected void onCreate() {
        super.onCreate();
        //初始化
    }
}
Clone this wiki locally