YoKeyword / Fragmentation

[DEPRECATED] A powerful library that manage Fragment for Android
Apache License 2.0
9.72k stars 2.11k forks source link

swipeback 过程,上层fragment 空白 #575

Closed KinaZhen closed 6 years ago

KinaZhen commented 6 years ago

如题,这个是有一定很大概率会出现这个问题,请问是什么原因? 我是上层fragment 是不可swipe 所以没有 new SwipeBackFragmentDelegate ,没有嵌入 swipelayout.

YoKeyword commented 6 years ago

swipeback 过程,上层fragment 空白 我是上层fragment 是不可swipe

听起来是矛盾的: 滑动过程中 上层Fragment空白,说明滑动的是上层Fragment,但是上层Fragment又是不可滑动的.... 不理解

如果想禁止滑动,有2个方式: 1、setSwipeBackEnable(false) 2、不继承SwipeBackFragment/Activity

KinaZhen commented 6 years ago

可能我表达的有问题,现在我的activity 里 有个根fragment1 , 根fragment 上start(fragment2) , 那么fragment2 swipeback 时 ,偶尔出现空白; 现在我把start(fragment2) 改成 extraTransaction().startDontHideSelf(fragment2) 则不会出现

YoKeyword commented 6 years ago

出现空白的化,应该是在滑动开始时,上一个Fragment的View没有setVisibility(View.VISIBLE);, 之前没有收到过相关反馈, 建议你断点调试下,是否没有调用setVisibility(View.VISIBLE);

相关代码在这里

Jiiiiiin commented 6 years ago

@YoKeyword 如果是Fragment里面嵌套的webview,就不支持类似webView.goBack(),就是说在嵌套一层判断 哈哈,要求是不是有点离谱了,不过现在混合开发也蛮多的,关注中,谢谢

YoKeyword commented 6 years ago

@Jiiiiiin 在Fragment的onBackPressedSupport()中 判断是否webview可goBack,然后选择处理并拦截

Jiiiiiin commented 6 years ago

@YoKeyword 做了,这个问题其实不是fragmentions 或者说fragment的锅,其实是安卓webview自身的锅,看知乎那个评论,大家都遇到了,而且我测了一下各大行,jbridge类型app,都存在,平安银行直接被我测挂了(白屏之后,所有webview都白),最后我写成这样了:

package cn.jiiiiiin.vplus.core.webview.util;

import android.webkit.WebView;

import com.blankj.utilcode.util.KeyboardUtils;
import com.blankj.utilcode.util.NetworkUtils;
import com.blankj.utilcode.util.ToastUtils;

import java.util.Objects;

import cn.jiiiiiin.vplus.core.app.ViewPlus;
import cn.jiiiiiin.vplus.core.util.log.LoggerProxy;
import cn.jiiiiiin.vplus.core.webview.AbstractWebViewDelegate;
import cn.jiiiiiin.vplus.core.webview.AbstractWebViewWrapperDelegate;
import cn.jiiiiiin.vplus.core.webview.WebViewDelegateImpl;

/**
 * 处理webview的返回
 * 处理webview容器fragment的返回
 *
 * @author jiiiiiin
 * @version 1.0
 */
public class BackPressedHandler {

    public static boolean onBack(AbstractWebViewWrapperDelegate wrapperDelegate, AbstractWebViewDelegate.ILifeCycleListener lifeCycleListener) {
        LoggerProxy.d("BackPressedHandler 通用返回方法被调用");
        final WebViewDelegateImpl webDelegate = wrapperDelegate.getWebDelegate();
        WebView webView = null;
        // webview已经“加载完毕”,这个时候才能让用户返回
        try {
            webView = webDelegate.getWebView();
        } catch (Exception e) {
            LoggerProxy.e(e, "获取webview出错");
            return false;
        }
        if (webView != null && wrapperDelegate.isWebViewIsLoading()) {
            if (webView.canGoBack()) {
                LoggerProxy.d("没有初始化网页完毕,webview还可以返回,先返回看看");
                webView.goBack();
            } else {
                ViewPlus.getHandler().postDelayed(() -> {
                    LoggerProxy.d("没有初始化网页完毕,但是webview已经实例化了,直接pop wrapperDelegate");
                    wrapperDelegate.pop();
                }, 500);
            }
        } else if (wrapperDelegate.isShowErrorLocalPage()) {
            // 如果加载的是错误页面,那么就直接弹掉页面
            wrapperDelegate.setShowErrorLocalPage(false);
            wrapperDelegate.pop();
            // !如果是isShowErrorLocalPage那就说明是渲染了本地的错误页面,那么不需要判断webview是否可以goback
        } else {
            boolean lifeCycleListenerHandlerFlag = false;
            if (lifeCycleListener != null) {
                // 不同的webvidew dalegate可以做不同的拦截处理
                lifeCycleListenerHandlerFlag = lifeCycleListener.onWebViewDelegateBackPressedSupport();
                LoggerProxy.d("检查mLifeCycleListener的处理结果 %s", lifeCycleListenerHandlerFlag);
            }
            if (!lifeCycleListenerHandlerFlag) {
                // ! 因为确认这个方法不会使用view参数
                // wrapperDelegate.onClickBackBtn.onClick(null);
                AbstractWebViewWrapperDelegate.ITitleBarEventListener titleBarEventListener = wrapperDelegate.getTitleBarEventListener();
                // 隐藏键盘
                KeyboardUtils.hideSoftInput(Objects.requireNonNull(wrapperDelegate.getActivity()));
                // 隐藏所有密码键盘
                wrapperDelegate.hideProgressBar();
                boolean isNotidy = false;
                if (titleBarEventListener != null) {
                    // 目前只有加载我们自身的wrapperDelegate具有该listener
                    // 交给前端去处理
                    // 标识是否通知前端【ON_HEADER_BAR_TAP_BCK_LISTENER】去处理返回点击事件
                    isNotidy = titleBarEventListener.onBackBtnClick();
                }
                if (!isNotidy) {
                    if (webView != null && webView.canGoBack()) {
                        webView.goBack();
                    } else {
                        wrapperDelegate.pop();
                    }
                }
            } else {
                LoggerProxy.d("mLifeCycleListener 已经自己处理了返回事件");
            }
        }
        return true;
    }
}

代码可能看起来很丑,但是我们的返回确实有太多东西需要处理,关键的代码就是:

 public static boolean onBack(AbstractWebViewWrapperDelegate wrapperDelegate, AbstractWebViewDelegate.ILifeCycleListener lifeCycleListener) {
        LoggerProxy.d("BackPressedHandler 通用返回方法被调用");
        final WebViewDelegateImpl webDelegate = wrapperDelegate.getWebDelegate();
        WebView webView = null;
        // webview已经“加载完毕”,这个时候才能让用户返回
        try {
            webView = webDelegate.getWebView();
        } catch (Exception e) {
            LoggerProxy.e(e, "获取webview出错");
            return false;
        }
        if (webView != null && wrapperDelegate.isWebViewIsLoading()) {
// 这里检测webview还没有【跑完,这里就有多种情况,其中最关键的就是WebViewClient:onPageStarted开始标记,WebChromeClient:onProgressChanged的prosgess第一次,注意是第一次到100,则标记完成,这个标记就是wrapperDelegate.isWebViewIsLoading,当然还有很多比如WebChromeClient中的错误、协议拦截等等标记为完成】,如果在没有完成的时候,用户强行返回,那个webview的bug就来了,百发百中,即只要一白全白,只能杀掉进程,我的处理是postDelayed延缓一下,当然这里就要看前端页面的响应速度了,也就是,问题的关键只能去优化前端,其实最后我观察:招商银行、微众银行....都是这样处理,平安仗着自己前端快很难点出来,一出来也是一个鸟样。
            if (webView.canGoBack()) {
                LoggerProxy.d("没有初始化网页完毕,webview还可以返回,先返回看看");
                webView.goBack();
            } else {
                ViewPlus.getHandler().postDelayed(() -> {
                    LoggerProxy.d("没有初始化网页完毕,但是webview已经实例化了,直接pop wrapperDelegate");
                    wrapperDelegate.pop();
                }, 500);
            }
        } else
Jiiiiiin commented 6 years ago

@YoKeyword 我们前端用的是vue所以webview的canback对我们无效,只能通过协议去通知前端【用户需要返回了】,前端在自己处理自身的路由,如果路由到底了,那么就反向通知我们popwebview:


isNotidy = titleBarEventListener.onBackBtnClick();

// jump-->

    @Override
    public boolean onBackBtnClick() {
        LoggerProxy.d("h5通用头部返回按钮的监听事件被调用 %s", ON_HEADER_BAR_TAP_BCK_LISTENER);
        if (!StringUtils.isEmpty(ON_HEADER_BAR_TAP_BCK_LISTENER)) {
            JsBridgeCommHandler.callJs(getWebViewOrNullllll(), ON_HEADER_BAR_TAP_BCK_LISTENER);
            return true;
        } else {
            // 第三方webview会走这里
            //_onPopWebViewDelegate();
            return false;
        }
    }

而且也存在友商的不是SPA的应用所以也需要做if (webView.canGoBack()) {判断。所以上面的代码看起来很屎就是这个原因。

Jiiiiiin commented 6 years ago

@YoKeyword 不知道有没有兴趣研究一下这个webview的神坑,哈哈,我觉得就算换了x5也怕是一个鸟样。

Jiiiiiin commented 6 years ago

但是我刚才玩了一下微信,【急速返回】,好像没有导致这个问题,的确有点牛逼,等项目稳定了我换一个试试。