在WebView中打开URL而不是默认浏览器

时间:2014-04-09 12:31:01

标签: android url webview textview intentfilter

我正在使用Webview上的一些链接创建简单的textview应用,并在webview而不是默认浏览器中打开这些链接。我的textview包含各种不同的网址,我正在尝试在我的应用的网页浏览中打开每个链接。

这里代码:

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

6 个答案:

答案 0 :(得分:27)

需要解决以下问题:

  1. 链接TextView
  2. 找一种方法来收听TextView
  3. 中链接的点击
  4. 获取所点击链接的网址并将其加载到WebView
  5. 可选:使TextView可单击而不会失去选择文本的能力
  6. 可选:处理TextView中的格式化文本(不同的文本大小和样式)
  7. #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,我们会通过LinkSpan替换TextView中的所有URLSpans(这就是我们需要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只是抓取点击的网址并在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);
                }
            }
        }
    }
    

答案 1 :(得分: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();
    }
});

答案 2 :(得分:1)

单击某个链接时,您的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())); 

    } 

答案 3 :(得分:0)

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

答案 4 :(得分:0)

@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);
    }
}

它就像一个魅力〜

答案 5 :(得分:-1)

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);