【问题标题】:Open URL in WebView instead of default Browser在 WebView 而不是默认浏览器中打开 URL
【发布时间】:2014-05-22 15:49:35
【问题描述】:

我正在创建简单的Webview 应用程序,其中包含textview 上的一些链接,并在webview 而不是默认浏览器中打开这些链接。我的textview 包含各种不同的 URL,我正在尝试在我的应用程序的 web 视图中打开每个链接。

这里代码:

tv.setText("www.google.com  www.facebook.com  www.yahoo.com");
tv.setMovementMethod(LinkMovementMethod.getInstance());;
tv.setText(Html.fromHtml(tv.getText().toString()));
Linkify.addLinks(tv, Linkify.WEB_URLS);


WebViewClient yourWebClient = new WebViewClient()
       {
           // Override page so it's load on my view only
           @Override
           public boolean shouldOverrideUrlLoading(WebView  view, String  url)
           {
            // This line we let me load only pages inside Firstdroid Webpage
            if ( url.contains("www") == true )
               // Load new URL Don't override URL Link
               return false;

            // Return true to override url loading (In this case do nothing).
            return true;
           }
       };

wv.getSettings().setJavaScriptEnabled(true);   
    wv.getSettings().setSupportZoom(true);      

    wv.getSettings().setBuiltInZoomControls(true); 
    wv.setWebViewClient(yourWebClient);

    // Load URL
    wv.loadUrl(url);

已尝试使用 thisthisthis 示例,但无法解决 textview 中的多个链接的问题。请帮我解决这个问题。感谢您的帮助。

编辑 我的Textview 包含如下字符串:

hello xyz some more statements... xyz.com/abc/
hello xyz some more statements... xyz.com/abc/
hello xyz some more statements... xyz.com/abc/
hello xyz some more statements... xyz.com/abc/

像这样它有很多字符串和多个 URL

【问题讨论】:

  • 你想改变什么在做什么或不做什么?
  • 我想从textview打开url点击在webview中打开
  • 你想要不同网页视图中的所有链接吗?
  • 在同一个 WebView 中没有。无论哪个用户单击 textview 中的链接,该链接都应在一个 webview 中打开
  • 从 TextView 发布一些示例内容,以便我们可以看到多个链接是如何分开的。您希望如何显示这些链接?一个接一个或在多个同时的 WebView 或 ???

标签: android url webview textview intentfilter


【解决方案1】:

需要解决以下问题:

  1. 链接TextView
  2. 找到一种方法来监听对 TextView 中链接的点击
  3. 获取点击链接的url并加载到WebView中
  4. 可选:使 TextView 可点击而不失去选择文本的能力
  5. 可选:在 TextView 中处理格式化文本(不同的文本大小和样式)

#1 链接 TextView

这是最简单的问题,你已经解决了。我建议这样做:

String text = "These are some sample links:\nwww.google.com\nwww.facebook.com\nwww.yahoo.com";
Spannable spannable = new SpannableString( Html.fromHtml(text) );
Linkify.addLinks(spannable, Linkify.WEB_URLS);

我在这里使用 Spannable 来解决问题 #2。

#2 + #3 监听链接点击并在 WebView 中打开它们

要找出链接何时被点击并检索我们必须打开的 URL,我们将 TextView 中的所有 URLSpan 替换为我们的 LinkSpan(这就是我们需要 Spannable 的原因):

URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
for (URLSpan urlSpan : spans) {
    LinkSpan linkSpan = new LinkSpan(urlSpan.getURL());
    int spanStart = spannable.getSpanStart(urlSpan);
    int spanEnd = spannable.getSpanEnd(urlSpan);
    spannable.setSpan(linkSpan, spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
    spannable.removeSpan(urlSpan);
}

我们的 LinkSpan 只是抓取点击的 url 并在 WebView 中打开它:

private class LinkSpan extends URLSpan {
    private LinkSpan(String url) {
        super(url);
    }

    @Override
    public void onClick(View view) {
        String url = getURL();
        if (mWebView != null && url != null) {
            mWebView.loadUrl(url);
        }
    }
}

现在显然我们必须在实例变量中保留对 WebView 的引用才能使其工作。为了使这个答案尽可能简短,我选择将 LinkSpan 定义为内部类,但我建议将其定义为顶级。注册一个监听器或将 WebView 作为参数传递给构造函数。

如果没有将 MovementMethod 设置为 LinkMovementMethod,TextView 根本不会打开链接:

tv.setMovementMethod(LinkMovementMethod.getInstance());
tv.setText(spannable, BufferType.SPANNABLE);

最后但同样重要的是,让我们确保 WebView 不会启动浏览器,而是在应用程序中加载页面:

mWebView.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        // we handle the url ourselves if it's a network url (http / https) 
        return ! URLUtil.isNetworkUrl(url);
    }
});

#4 可点击和可选择的TextView + #5 格式化文本

如果将 MovementMethod 设置为 LinkMovementMethod,您可以单击链接,但不能再选择文本(您需要 ArrowKeyMovementMethod)。为了解决这个问题,我创建了一个继承自 ArrowKeyMovementMethod 的自定义 MovementMethod 类,并添加了单击链接的功能。最重要的是,它能够处理格式化的文本。因此,如果您决定在 TextView 中使用不同的字体大小和样式,下面的 MovementMethod 将覆盖它(也适用于 EditTexts):

/**
 * ArrowKeyMovementMethod does support selection of text but not the clicking of links.
 * LinkMovementMethod does support clicking of links but not the selection of text.
 * This class adds the link clicking to the ArrowKeyMovementMethod.
 * We basically take the LinkMovementMethod onTouchEvent code and remove the line
 *      Selection.removeSelection(buffer);
 * which deselects all text when no link was found.
 */
public class EnhancedLinkMovementMethod extends ArrowKeyMovementMethod {

    private static EnhancedLinkMovementMethod sInstance;

    private static Rect sLineBounds = new Rect();

    public static MovementMethod getInstance() {
        if (sInstance == null) {
            sInstance = new EnhancedLinkMovementMethod();
        }
        return sInstance;
    }

    @Override
    public boolean onTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
        int action = event.getAction();

        if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {

            int index = getCharIndexAt(widget, event);
            if (index != -1) {
                ClickableSpan[] link = buffer.getSpans(index, index, ClickableSpan.class);
                if (link.length != 0) {
                    if (action == MotionEvent.ACTION_UP) {
                        link[0].onClick(widget);
                    }
                    else if (action == MotionEvent.ACTION_DOWN) {
                        Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));
                    }
                    return true;
                }
            }
            /*else {
                Selection.removeSelection(buffer);
            }*/

        }

        return super.onTouchEvent(widget, buffer, event);
    }

    private int getCharIndexAt(TextView textView, MotionEvent event) {
        // get coordinates
        int x = (int) event.getX();
        int y = (int) event.getY();
        x -= textView.getTotalPaddingLeft();
        y -= textView.getTotalPaddingTop();
        x += textView.getScrollX();
        y += textView.getScrollY();

        /*
         * Fail-fast check of the line bound.
         * If we're not within the line bound no character was touched
         */
        Layout layout = textView.getLayout();
        int line = layout.getLineForVertical(y);
        synchronized (sLineBounds) {
            layout.getLineBounds(line, sLineBounds);
            if (! sLineBounds.contains(x, y)) {
                return -1;
            }
        }

        // retrieve line text
        Spanned text = (Spanned) textView.getText();
        int lineStart = layout.getLineStart(line);
        int lineEnd = layout.getLineEnd(line);
        int lineLength = lineEnd - lineStart;
        if (lineLength == 0) {
            return -1;
        }
        Spanned lineText = (Spanned) text.subSequence(lineStart, lineEnd);

        // compute leading margin and subtract it from the x coordinate
        int margin = 0;
        LeadingMarginSpan[] marginSpans = lineText.getSpans(0, lineLength, LeadingMarginSpan.class);
        if (marginSpans != null) {
            for (LeadingMarginSpan span : marginSpans) {
                margin += span.getLeadingMargin(true);
            }
        }
        x -= margin;

        // retrieve text widths
        float[] widths = new float[lineLength];
        TextPaint paint = textView.getPaint();
        paint.getTextWidths(lineText, 0, lineLength, widths);

        // scale text widths by relative font size (absolute size / default size)
        final float defaultSize = textView.getTextSize();
        float scaleFactor = 1f;
        AbsoluteSizeSpan[] absSpans = lineText.getSpans(0, lineLength, AbsoluteSizeSpan.class);
        if (absSpans != null) {
            for (AbsoluteSizeSpan span : absSpans) {
                int spanStart = lineText.getSpanStart(span);
                int spanEnd = lineText.getSpanEnd(span);
                scaleFactor = span.getSize() / defaultSize;
                int start = Math.max(lineStart, spanStart);
                int end = Math.min(lineEnd, spanEnd);
                for (int i = start; i < end; i++) {
                    widths[i] *= scaleFactor;
                }
            }
        }

        // find index of touched character
        float startChar = 0;
        float endChar = 0;
        for (int i = 0; i < lineLength; i++) {
            startChar = endChar;
            endChar += widths[i];
            if (endChar >= x) {
                // which "end" is closer to x, the start or the end of the character?
                int index = lineStart + (x - startChar < endChar - x ? i : i + 1);
                //Logger.e(Logger.LOG_TAG, "Found character: " + (text.length()>index ? text.charAt(index) : ""));
                return index;
            }
        }

        return -1;
    }
}

完整的活动代码

这是我使用的完整示例活动代码。它应该完全符合您的要求。它使用的是我的 EnhandedMovementMethod,但您可以使用简单的 LinkMovementMethod(具有前面提到的缺点)。

public class LinkTestActivity extends Activity {

    private WebView mWebView;

    @SuppressLint("SetJavaScriptEnabled")
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);
        mWebView = (WebView) findViewById(R.id.webView);
        TextView tv = (TextView) findViewById(R.id.textView);

        String text = "These are some sample links:\nwww.google.com\nwww.facebook.com\nwww.yahoo.com";

        // Linkify the TextView
        Spannable spannable = new SpannableString( Html.fromHtml(text) );
        Linkify.addLinks(spannable, Linkify.WEB_URLS);

        // Replace each URLSpan by a LinkSpan
        URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
        for (URLSpan urlSpan : spans) {
            LinkSpan linkSpan = new LinkSpan(urlSpan.getURL());
            int spanStart = spannable.getSpanStart(urlSpan);
            int spanEnd = spannable.getSpanEnd(urlSpan);
            spannable.setSpan(linkSpan, spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
            spannable.removeSpan(urlSpan);
        }

        // Make sure the TextView supports clicking on Links
        tv.setMovementMethod(EnhancedLinkMovementMethod.getInstance());
        tv.setText(spannable, BufferType.SPANNABLE);

        // Make sure we handle clicked links ourselves
        mWebView.setWebViewClient(new WebViewClient() {
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                // we handle the url ourselves if it's a network url (http / https) 
                return ! URLUtil.isNetworkUrl(url);
            }
        });

        mWebView.getSettings().setJavaScriptEnabled(true);   
        mWebView.getSettings().setSupportZoom(true);      
        mWebView.getSettings().setBuiltInZoomControls(true);
    }

    private class LinkSpan extends URLSpan {
        private LinkSpan(String url) {
            super(url);
        }

        @Override
        public void onClick(View view) {
            String url = getURL();
            if (mWebView != null && url != null) {
                mWebView.loadUrl(url);
            }
        }
    }
}

【讨论】:

  • EnhancedLinkMovementMethod 似乎不允许正确选择
  • @kenyee 但确实如此;-)。该代码适用于我的数以千计的用户,如果您不这么认为,您必须出示一些证据。
  • 所以我是个白痴...我没有设置 android:textIsSelectable="true"...大声笑。对不起:-)
  • 优秀的答案。写得好,解释得好:)
【解决方案2】:

WebViewClient 是通过在您的 WebView 引用上调用 setWebViewClient() 方法设置的。

首先设置webview的所有属性然后调用loadURL(String url)方法,它会打开子链接到同一个webview而不是打开浏览器。

ProgressDialog progressDialog = new ProgressDialog(WebActivity.this);
WebView webview= (WebView) findViewById(R.id.webview);

webview.getSettings().setDomStorageEnabled(true);
webview.getSettings().setJavaScriptEnabled(true);
webview.setVerticalScrollBarEnabled(false);
webview.getSettings().setJavaScriptCanOpenWindowsAutomatically(true);
webview.getSettings().setPluginsEnabled(true);
webview.getSettings().setSupportMultipleWindows(true);
webview.getSettings().setSupportZoom(true);
webview.setVerticalScrollBarEnabled(false);
webview.setHorizontalScrollBarEnabled(false);

webview.loadUrl("http://www.google.com");

webview.setWebViewClient(new WebViewClient() {
    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String url) {
        return false;
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        super.onPageFinished(view, url);
        progressDialog.dismiss();
    }

    @Override
    public void onPageStarted(WebView view, String url,Bitmap favicon) {
        // TODO Auto-generated method stub
        super.onPageStarted(view, url, favicon);
        progressDialog.setMessage("Loading ...");
        progressDialog.setCancelable(false);
        progressDialog.setCanceledOnTouchOutside(false);
        progressDialog.show();
    }
});

【讨论】:

  • 是的,但问题是TextView 中有许多不同的 URL,当用户单击任何链接时,所需的 URL 必须在 WebView 中打开。所以我正在寻找如何完成这项工作。
【解决方案3】:

当点击某个链接时,您的Webview 应用程序将收到链接。用户必须手动选择您的应用程序。指定主机“like www.apptechinc.com”将在单击该站点的任何链接时启动此应用程序。

    <activity
    android:name=".MainActivity"
    android:label="@string/title_activity_main" >
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <action android:name="android.intent.action.VIEW" />
        <category android:name="android.intent.category.DEFAULT" />
        <data android:scheme="http" />
        <data android:scheme="https" />
        <data android:host="www.apptecxinc.com" />
    </intent-filter>
    </activity>

在活动中,您可以收到此链接:

    String action = getIntent().getAction();
    if (Intent.ACTION_VIEW.equals(action)) {
        Uri uri = getIntent().getData();
            webview.loadUrl(urlsetup(uri.getPath())); 

    } 

【讨论】:

    【解决方案4】:

    问题似乎是 Linkify 正在寻找一种检测链接的方案。如果没有http://,它不会将您的链接检测为 Web URL。解决方案是确保您的链接包含该方案或定义自定义正则表达式模式以匹配。从您的示例 TextView 数据中不清楚该模式应该是什么。 Here's a page 展示了如何使用自定义模式,一旦你弄清楚它是什么。

    【讨论】:

      【解决方案5】:

      @Emanuel Moecklin 的答案中最重要的部分是继承 URLSpan 并创建自己的 URLSpan 并覆盖 onClick

      受他的回答启发,我没有完全按照他的步骤进行操作,而是坚持使用Linkify。但是,由于其他一些原因,我创建了自己的 Linkify 类,其代码与原始 Linkify 几乎相同。

      然后,在 Linkify 中的 applyLink() 内部,我替换了

      URLSpan span = new URLSpan(url);
      

      InAppURLSpan span = new InAppURLSpan(url);
      

      InAppURLSpan 的代码在哪里:

      public static class InAppURLSpan extends URLSpan {
      
          public InAppURLSpan(String url) {
              super(url);
          }
      
          @Override
          public void onClick(View widget) {
              String url = getURL();
              Log.i("TNC_URL", url);
              Intent intent = new Intent(widget.getContext(), SingleWebViewActivity.class);
              intent.putExtra(Constants.INTENT_URL, url);
              widget.getContext().startActivity(intent);
          }
      }
      

      而且效果很好~

      【讨论】:

        【解决方案6】:
        WebView wv = (WebView) findViewById(R.id.webView1);
        wv.getSettings().setJavaScriptEnabled(true);
        wv.getSettings().setSupportZoom(true);      
        
        wv.getSettings().setBuiltInZoomControls(true);
        
        wv.setWebViewClient(new WebViewClient()
        {
            // Links clicked will be shown on the webview
            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url)
            {
                return super.shouldOverrideUrlLoading(view, url);
            }   
        }); 
        wv.loadUrl(url);
        

        【讨论】:

        • 我宁愿为您的链接制作 3 个文本视图/按钮,并将其 onClickListener 设置为执行 webview.loadurl(url)
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2014-07-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多