Android WebView findNext()点击屏幕后跳转位置异常求助
Hey there! I totally get your frustration with this WebView search behavior—native Android WebView's findAllAsync() and findNext() can be tricky when users interact with the WebView content. Let's break down why this happens and how to fix it.
Why This Happens
The native WebView search methods (findAllAsync()/findNext()) rely on an internal search session that gets reset when you tap anywhere on the WebView. This reset makes findNext() start searching from the tapped position instead of continuing the original sequence. Disabling touch or clearing focus doesn't help because the session reset happens deep in WebView's internal logic when it registers a tap event.
Solution: Custom Search & Highlight with JavaScript
The most reliable fix is to take full control of the search and navigation using JavaScript. This way, you maintain the search state independently, so WebView taps won't disrupt the sequence. Here's how to implement it:
Step 1: Enable JavaScript in WebView
First, make sure JavaScript is enabled in your WebView settings:
webView.getSettings().setJavaScriptEnabled(true);
Step 2: Inject JavaScript Functions
Add these JavaScript functions to handle search, highlighting, and navigation. You can inject them when the WebView finishes loading your HTML:
webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished(WebView view, String url) { super.onPageFinished(view, url); // Inject JS code String jsCode = """ // Clear existing highlights and reset state let currentMatchIndex = -1; let totalMatches = 0; function searchAndHighlight(text) { // Remove old highlights document.querySelectorAll('.highlight, .current-highlight').forEach(el => { el.replaceWith(document.createTextNode(el.textContent)); }); if (!text.trim()) { totalMatches = 0; currentMatchIndex = -1; return; } // Find all matches const regex = new RegExp(text, 'gi'); const body = document.body; const matches = []; let match; const bodyHtml = body.innerHTML; while ((match = regex.exec(bodyHtml)) !== null) { matches.push({ start: match.index, length: match[0].length }); } totalMatches = matches.length; currentMatchIndex = -1; // Highlight all matches (reverse to avoid index shifting) let newHtml = bodyHtml; matches.reverse().forEach(match => { const start = match.start; const end = start + match.length; newHtml = newHtml.substring(0, start) + '<span class="highlight">' + newHtml.substring(start, end) + '</span>' + newHtml.substring(end); }); body.innerHTML = newHtml; } function goToNextMatch() { if (totalMatches === 0) return; // Update previous current highlight const prevCurrent = document.querySelector('.current-highlight'); if (prevCurrent) { prevCurrent.classList.remove('current-highlight'); prevCurrent.classList.add('highlight'); } // Move to next match (loop back to start if at end) currentMatchIndex = (currentMatchIndex + 1) % totalMatches; // Highlight current match and scroll to it const highlights = document.querySelectorAll('.highlight'); const current = highlights[currentMatchIndex]; current.classList.remove('highlight'); current.classList.add('current-highlight'); current.scrollIntoView({ behavior: 'smooth', block: 'center' }); } """; view.evaluateJavascript(jsCode, null); // Inject CSS for highlights String css = """ const style = document.createElement('style'); style.textContent = ` .highlight { background-color: yellow; } .current-highlight { background-color: red; } `; document.head.appendChild(style); """; view.evaluateJavascript(css, null); } });
Step 3: Update Your Button Click Listeners
Replace your existing search and next button logic with calls to the JavaScript functions:
Search Button
searchButton.setOnClickListener(v -> { String searchText = et.getText().toString().trim(); // Escape special characters to avoid JS syntax errors String escapedText = searchText.replace("'", "\\'").replace("\"", "\\\""); webView.evaluateJavascript("searchAndHighlight('" + escapedText + "')", null); });
Next Button
next.setOnClickListener(v -> { webView.evaluateJavascript("goToNextMatch()", null); });
Key Benefits of This Approach
- Full Control: You maintain the search state (
currentMatchIndex,totalMatches) independently, so WebView taps won't reset it. - Customizable: You can easily tweak highlight colors, scroll behavior, or add a "previous" button with minimal changes.
- User-Friendly: Users can still interact with the WebView (tap links, scroll, zoom) without breaking the search sequence.
Note on Special Characters
Make sure to escape special characters in the search text (like quotes) to avoid JavaScript syntax errors. The example above includes basic escaping, but you can expand it for more edge cases if needed.
内容的提问来源于stack exchange,提问作者sara

