Skip to content

6. 常见问题(必看)

Jun edited this page Dec 17, 2021 · 10 revisions

虽然弹窗已经解决了很多Bug,已经很稳定,但是仍然可能出现Bug。如果出问题,请第一时间看看我的Demo有没有问题,如果Demo没有问题,那往往是你使用不当造成的。

和Databind/ViewBinding结合

//DataBinding 在 onCreate 调用
binding = DataBindingUtil.bind(getPopupImplView());

//ViewBinding
binding = XXXBinding.bind(getPopupImplView());

按返回键直接退出界面而不是关闭弹窗

弹窗之所以能响应返回键,是因为弹窗默认会获取焦点。如果你调用了isRequestFocus(false)方法的话,弹窗会失去焦点,从而无法响应返回按键。

如果你并没有调用这个方法,弹窗也无法响应返回键,那往往是弹窗布局内的某个View强制获取焦点了,比如WebView。此时给弹窗的根布局设置2个属性即可,不让View强制获取焦点。

属性如下:

android:focusable="false"
android:focusableInTouchMode="false"

Fragment中使用弹窗出现内存泄漏

由于弹窗的宿主不是Fragment,而是Activity;所以XPopup目前能做到宿主Activity关闭的时候清除资源避免内存泄漏。但在Fragment中使用,当你的Fragment被destroy了后,如果你设置了XPopup的回调或者使用的是Attach系列弹窗,可能会有内存泄漏。泄漏是由弹窗的popupInfo字段引起的,这个字段可能会引用View对象和XPopupCallback对象。

那为什么我不在弹窗dismiss的时候进行清除资源呢?因为XPopup不确定弹窗消失后还会不会复用,因为你可能会复用弹窗对象;所以popupInfo无法做到在弹窗dismiss的时候进行清除。对于这种Fragment场景的泄漏,你有几种解决方案:

  1. 也可以无需处理,因为Activity只要关闭,弹窗就全部释放资源

  2. XPopup提供了isDestroyOnDismiss设置,如果你的弹窗只使用一次,最好设置这个,它能保证在弹窗消失的时候进行彻底的资源释放

  3. 最坏的情况是,你的弹窗在Fragment中使用,并且弹窗对象会重复使用多次,每个弹窗对象都提供了destroy()方法用于彻底释放资源,你可以选择在合适的时间手动调用该方法,即可避免内存泄漏

希望弹窗的高度是固定的

两种方法:

  1. 编写弹窗布局的时候高度指定固定的值,比如200dp;
  2. 可以重写getPopupHeight()方法,返回一个固定值,这种方式更灵活,可以在代码中动态计算;

弹窗弹出后下层的View仍然能够滑动

一般出现在长按场景,比如长按列表Item弹出弹窗,此时不松手继续上下滑动,下层的列表仍然能够滑动。此时需要在长按事件中调用一下XPopup.fixLongClick(v)

view.findViewById(R.id.btnShowAttachPoint).setOnLongClickListener(new View.OnLongClickListener() {
            @Override
            public boolean onLongClick(View v) {
                XPopup.fixLongClick(v);//重要:能保证弹窗弹出后,下层的View无法滑动
                builder.asAttachList(new String[]{"置顶", "复制"}, null,new OnSelectListener() {
                            @Override
                            public void onSelect(int position, String text) {
                                toast("click " + text);
                            }
                        })
                        .show();
                return true;
            }
        });

弹窗中使用Fragment

XPopup默认是Dialog实现,由于Android的限制,Dialog中无法直接使用Fragment,所以需要设置isViewMode(true);开启View模式后弹出本质上是挂载到decorView上的一个View了,不再是Dialog。

使用Fragment时,有个注意事项如下:

**弹窗开启View模式后,本质就是Activity里一个View。在弹窗内嵌入Fragment的场景中,等同于在View中使用Fragment。

当弹窗消失后,由于Fragment被Activity的FragmentManager缓存,会导致弹窗重新创建/显示的时候,Fragment会命中缓存,生命周期不再执行。为了处理这种情况,只需重写: getInternalFragmentNames() 方法,返回嵌入的Fragment名称,XPopup会在弹窗消失时自动移除Fragment缓存。**

 /**
     *  在弹窗内嵌入Fragment的场景中,当弹窗后,由于Fragment被Activity的FragmentManager缓存,
     *  会导致弹窗重新创建的时候,Fragment会命中缓存,生命周期不再执行。为了处理这种情况,只需重写:
     *  getInternalFragmentNames() 方法,返回嵌入的Fragment名称,XPopup会自动移除Fragment缓存。
     *  名字是: Fragment.getClass().getSimpleName()
     * @return
     */
    protected List<String> getInternalFragmentNames(){
        return null;
    }

如果你的弹窗是一次性使用,而非复用,一定要设置.isDestroyOnDismiss(true)

当然如果你想自己处理Fragment的移除逻辑,就不用管这个方法了;可以在onDiamiss方法中取移除你的Fragment。