如何控制kotlin项目back的时候,只回退webview的路由

在 Kotlin 项目中控制返回键只回退 WebView 的路由,需要重写 onBackPressed() 方法并检查 WebView 的浏览历史。以下是完整的实现方案:

1. 基础实现

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private lateinit var webView: WebView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        webView = findViewById(R.id.webView)
        setupWebView()
        webView.loadUrl("https://your-website.com")
    }

    // 核心:重写返回键逻辑
    override fun onBackPressed() {
        if (webView.canGoBack()) {
            // WebView 有历史记录,回退到上一个页面
            webView.goBack()
        } else {
            // WebView 没有历史记录,执行默认返回操作(退出应用)
            super.onBackPressed()
        }
    }

    private fun setupWebView() {
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        
        webView.webViewClient = WebViewClient()
    }
}

2. 增强版本(推荐)

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private lateinit var webView: WebView
    private var backPressedTime: Long = 0
    
    // 配置项
    companion object {
        private const val EXIT_DELAY = 2000L // 2秒内按两次退出
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        webView = findViewById(R.id.webView)
        setupWebView()
        webView.loadUrl("https://your-website.com")
    }

    override fun onBackPressed() {
        when {
            webView.canGoBack() -> {
                // 情况1: WebView 有历史记录,回退网页
                webView.goBack()
            }
            supportFragmentManager.backStackEntryCount > 0 -> {
                // 情况2: 有 Fragment 在回退栈中,回退 Fragment
                supportFragmentManager.popBackStack()
            }
            else -> {
                // 情况3: 没有历史记录,提示双击退出
                if (backPressedTime + EXIT_DELAY > System.currentTimeMillis()) {
                    super.onBackPressed() // 真正退出
                } else {
                    Toast.makeText(this, "再按一次退出应用", Toast.LENGTH_SHORT).show()
                    backPressedTime = System.currentTimeMillis()
                }
            }
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun setupWebView() {
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        webSettings.setSupportZoom(true)
        webSettings.builtInZoomControls = true
        webSettings.displayZoomControls = false
        
        // 确保所有链接都在 WebView 内打开
        webView.webViewClient = object : WebViewClient() {
            override fun shouldOverrideUrlLoading(
                view: WebView,
                request: WebResourceRequest
            ): Boolean {
                view.loadUrl(request.url.toString())
                return true
            }
            
            // 兼容旧版本
            override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
                view.loadUrl(url)
                return true
            }
        }
    }
}

3. 监听 WebView 历史变化

如果需要更精确的控制,可以监听 WebView 的历史变化:

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private lateinit var webView: WebView
    private var isOnFirstPage = true

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        webView = findViewById(R.id.webView)
        setupWebView()
        webView.loadUrl("https://your-website.com")
    }

    override fun onBackPressed() {
        if (!isOnFirstPage) {
            // 不在首页,回退 WebView
            webView.goBack()
        } else {
            // 在首页,执行退出逻辑
            if (backPressedTime + EXIT_DELAY > System.currentTimeMillis()) {
                super.onBackPressed()
            } else {
                Toast.makeText(this, "再按一次退出应用", Toast.LENGTH_SHORT).show()
                backPressedTime = System.currentTimeMillis()
            }
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun setupWebView() {
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        
        webView.webViewClient = object : WebViewClient() {
            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                super.onPageStarted(view, url, favicon)
                updateBackButtonState()
            }
            
            override fun onPageFinished(view: WebView?, url: String?) {
                super.onPageFinished(view, url)
                updateBackButtonState()
            }
        }
        
        // 监听 URL 变化
        webView.webViewClient = object : WebViewClient() {
            override fun doUpdateVisitedHistory(view: WebView?, url: String?, isReload: Boolean) {
                super.doUpdateVisitedHistory(view, url, isReload)
                updateBackButtonState()
            }
        }
    }
    
    private fun updateBackButtonState() {
        // 检查是否在第一个页面(通常是初始加载的页面)
        isOnFirstPage = !webView.canGoBack()
        
        // 可选:更新 UI,比如显示/隐藏返回按钮
        updateNavigationUI()
    }
    
    private fun updateNavigationUI() {
        // 例如:在 Toolbar 中显示/隐藏返回按钮
        supportActionBar?.setDisplayHomeAsUpEnabled(!isOnFirstPage)
    }
    
    // 处理 Toolbar 的返回按钮
    override fun onSupportNavigateUp(): Boolean {
        onBackPressed()
        return true
    }
}

4. 与 JavaScript 路由配合

如果你的 WebView 加载的是 SPA(单页应用),可能需要与 JavaScript 路由配合:

Kotlin 端:

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private lateinit var webView: WebView
    private var isSPA = true // 是否是单页应用

    override fun onBackPressed() {
        if (isSPA) {
            // 对于 SPA,通过 JavaScript 处理路由回退
            webView.evaluateJavascript("""
                if (window.history.length > 1) {
                    window.history.back();
                    false; // 表示 JavaScript 处理了回退
                } else {
                    true; // 表示需要原生处理回退
                }
            """.trimIndent()) { result ->
                // result 为 "true" 表示需要退出应用
                if (result == "true") {
                    handleAppExit()
                }
            }
        } else {
            // 传统多页应用
            if (webView.canGoBack()) {
                webView.goBack()
            } else {
                handleAppExit()
            }
        }
    }
    
    private fun handleAppExit() {
        if (backPressedTime + EXIT_DELAY > System.currentTimeMillis()) {
            super.onBackPressed()
        } else {
            Toast.makeText(this, "再按一次退出应用", Toast.LENGTH_SHORT).show()
            backPressedTime = System.currentTimeMillis()
        }
    }

    // 添加 JavaScript 接口用于双向通信
    private fun setupWebView() {
        webView.addJavascriptInterface(WebAppInterface(), "Android")
        
        webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                // 注入 JavaScript 来监听路由变化
                injectRouteListener()
            }
        }
    }
    
    private fun injectRouteListener() {
        webView.evaluateJavascript("""
            // 监听 SPA 路由变化
            if (typeof window.history !== 'undefined') {
                const originalPushState = history.pushState;
                const originalReplaceState = history.replaceState;
                
                history.pushState = function(...args) {
                    originalPushState.apply(this, args);
                    window.Android.onRouteChanged(window.location.href);
                };
                
                history.replaceState = function(...args) {
                    originalReplaceState.apply(this, args);
                    window.Android.onRouteChanged(window.location.href);
                };
                
                window.addEventListener('popstate', function() {
                    window.Android.onRouteChanged(window.location.href);
                });
            }
        """.trimIndent(), null)
    }
    
    inner class WebAppInterface {
        @JavascriptInterface
        fun onRouteChanged(url: String) {
            // JavaScript 通知路由变化
            runOnUiThread {
                updateBackButtonState()
            }
        }
        
        @JavascriptInterface
        fun canGoBack(canGoBack: Boolean) {
            // JavaScript 告知是否可以回退
            runOnUiThread {
                isOnFirstPage = !canGoBack
            }
        }
    }
}

JavaScript 端配合:

javascript 复制代码
// 在 JavaScript 中监听路由变化
let currentHistoryLength = window.history.length;

function updateBackState() {
    const canGoBack = window.history.length > 1;
    if (window.Android) {
        window.Android.canGoBack(canGoBack);
    }
}

// 监听路由变化
window.addEventListener('popstate', updateBackState);
window.addEventListener('pushState', updateBackState);
window.addEventListener('replaceState', updateBackState);

// 初始状态
updateBackState();

5. 完整的最佳实践方案

kotlin 复制代码
class MainActivity : AppCompatActivity() {

    private lateinit var webView: WebView
    private var backPressedTime: Long = 0
    private var isOnHomePage = true
    
    companion object {
        private const val EXIT_DELAY = 2000L
        private const val HOME_PAGE_URL = "https://your-website.com"
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        initWebView()
    }

    override fun onBackPressed() {
        when {
            webView.canGoBack() -> {
                webView.goBack() // 只回退 WebView 路由
            }
            else -> {
                // 双击退出逻辑
                if (backPressedTime + EXIT_DELAY > System.currentTimeMillis()) {
                    super.onBackPressed()
                } else {
                    showExitToast()
                    backPressedTime = System.currentTimeMillis()
                }
            }
        }
    }

    @SuppressLint("SetJavaScriptEnabled")
    private fun initWebView() {
        webView = findViewById(R.id.webView)
        
        val webSettings = webView.settings
        webSettings.javaScriptEnabled = true
        webSettings.domStorageEnabled = true
        
        webView.webViewClient = object : WebViewClient() {
            override fun onPageFinished(view: WebView?, url: String?) {
                // 更新首页状态
                isOnHomePage = url == HOME_PAGE_URL || !webView.canGoBack()
                updateUIState()
            }
        }
        
        webView.loadUrl(HOME_PAGE_URL)
    }
    
    private fun showExitToast() {
        Toast.makeText(this, "再按一次退出应用", Toast.LENGTH_SHORT).show()
    }
    
    private fun updateUIState() {
        // 可以根据状态更新 UI,比如显示/隐藏导航按钮
    }
}

关键点总结

  1. 核心方法 :重写 onBackPressed(),检查 webView.canGoBack()
  2. 用户体验:添加双击退出提示,避免误操作
  3. SPA 支持:对于单页应用,需要与 JavaScript 路由配合
  4. 状态管理:监听页面变化,准确判断是否在首页

这样就能确保返回键只回退 WebView 的路由,只有在没有历史记录时才执行退出逻辑。

相关推荐
拼好饭和她皆失1 小时前
C#学习入门
开发语言·学习·c#
分布式存储与RustFS1 小时前
MinIO 不再“开放”,RustFS 能否成为更优选择?
开发语言·安全·安全架构·企业存储·rustfs
Sunhen_Qiletian1 小时前
《Python开发之语言基础》第一集:python的语法元素
开发语言·python
從南走到北2 小时前
JAVA同城信息付费系统家政服务房屋租赁房屋买卖房屋装修信息发布平台小程序APP公众号源码
java·开发语言·小程序
月夜的风吹雨2 小时前
【C++红黑树】:自平衡二叉搜索树的精妙实现
开发语言·c++·红黑树
TechMasterPlus2 小时前
java:单例模式
java·开发语言·单例模式
栗子~~2 小时前
java-根据word模板灵活生成word文档-demo
java·开发语言·word
lqj_本人3 小时前
HarmonyOS + Cordova:在线资源加载与拦截缓存问题排查
harmonyos
Boop_wu3 小时前
[Java EE] 多线程 -- 初阶(5) [线程池和定时器]
java·开发语言