Open Url In Webview Instead Of Default Browser
Solution 1:
The following problems need to be solved:
- Linkify the TextView
- Find a way to listen to a click on a link in the TextView
- Get the url of the clicked link and load it in the WebView
- Optional: make the TextView clickable without losing the ability to select text
- Optional: handle formatted text in the TextView (different text sizes and styles)
#1 Linkify the TextView
That's the easiest problem and you already solved that one. I suggest to do it this way:
Stringtext = "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);
I'm using a Spannable here to solve problem #2.
#2 + #3 Listen to clicks on links and open them in the WebView
To find out when a link is clicked and retrieve the URL we have to open, we replace all URLSpans in the TextView by our LinkSpan (that's why we need a Spannable):
URLSpan[] spans = spannable.getSpans(0, spannable.length(), URLSpan.class);
for (URLSpan urlSpan : spans) {
LinkSpan linkSpan = newLinkSpan(urlSpan.getURL());
int spanStart = spannable.getSpanStart(urlSpan);
int spanEnd = spannable.getSpanEnd(urlSpan);
spannable.setSpan(linkSpan, spanStart, spanEnd, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
spannable.removeSpan(urlSpan);
}
Our LinkSpan simply grabs the clicked url and opens it in the WebView:
privateclassLinkSpanextendsURLSpan {
privateLinkSpan(String url) {
super(url);
}
@OverridepublicvoidonClick(View view) {
Stringurl= getURL();
if (mWebView != null && url != null) {
mWebView.loadUrl(url);
}
}
}
Now obviously we have to keep a reference to the WebView in an instance variable to make this work. To make this answer as short as possible I chose to define LinkSpan as an inner class but I'd recommend to define it as a top-level. Register a listener or pass the WebView as a parameter to the constructor instead.
Without setting the MovementMethod to LinkMovementMethod the TextView won't open links at all:
tv.setMovementMethod(LinkMovementMethod.getInstance());
tv.setText(spannable, BufferType.SPANNABLE);
Last but not least let's make sure the WebView doesn't start the browser but loads the page within the app:
mWebView.setWebViewClient(newWebViewClient() {
@OverridepublicbooleanshouldOverrideUrlLoading(WebView view, String url) {
// we handle the url ourselves if it's a network url (http / https) return ! URLUtil.isNetworkUrl(url);
}
});
#4 Clickable and selectable TextView + #5 formatted Text
If the MovementMethod is set to LinkMovementMethod you can click links but you can't select text any more (you need ArrowKeyMovementMethod for that). To solve this I created an custom MovementMethod class that inherits from ArrowKeyMovementMethod and adds the ability to click links. On top of that it is able to deal with formatted text. So if you decide to use different font sizes and styles in the TextView the following MovementMethod will have it covered (works with EditTexts as well):
/**
* 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.
*/publicclassEnhancedLinkMovementMethodextendsArrowKeyMovementMethod {
privatestatic EnhancedLinkMovementMethod sInstance;
privatestaticRectsLineBounds=newRect();
publicstatic MovementMethod getInstance() {
if (sInstance == null) {
sInstance = newEnhancedLinkMovementMethod();
}
return sInstance;
}
@OverridepublicbooleanonTouchEvent(TextView widget, Spannable buffer, MotionEvent event) {
intaction= event.getAction();
if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_DOWN) {
intindex= 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);
}
elseif (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(buffer, buffer.getSpanStart(link[0]), buffer.getSpanEnd(link[0]));
}
returntrue;
}
}
/*else {
Selection.removeSelection(buffer);
}*/
}
returnsuper.onTouchEvent(widget, buffer, event);
}
privateintgetCharIndexAt(TextView textView, MotionEvent event) {
// get coordinatesintx= (int) event.getX();
inty= (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
*/Layoutlayout= textView.getLayout();
intline= layout.getLineForVertical(y);
synchronized (sLineBounds) {
layout.getLineBounds(line, sLineBounds);
if (! sLineBounds.contains(x, y)) {
return -1;
}
}
// retrieve line textSpannedtext= (Spanned) textView.getText();
intlineStart= layout.getLineStart(line);
intlineEnd= layout.getLineEnd(line);
intlineLength= lineEnd - lineStart;
if (lineLength == 0) {
return -1;
}
SpannedlineText= (Spanned) text.subSequence(lineStart, lineEnd);
// compute leading margin and subtract it from the x coordinateintmargin=0;
LeadingMarginSpan[] marginSpans = lineText.getSpans(0, lineLength, LeadingMarginSpan.class);
if (marginSpans != null) {
for (LeadingMarginSpan span : marginSpans) {
margin += span.getLeadingMargin(true);
}
}
x -= margin;
// retrieve text widthsfloat[] widths = newfloat[lineLength];
TextPaintpaint= textView.getPaint();
paint.getTextWidths(lineText, 0, lineLength, widths);
// scale text widths by relative font size (absolute size / default size)finalfloatdefaultSize= textView.getTextSize();
floatscaleFactor=1f;
AbsoluteSizeSpan[] absSpans = lineText.getSpans(0, lineLength, AbsoluteSizeSpan.class);
if (absSpans != null) {
for (AbsoluteSizeSpan span : absSpans) {
intspanStart= lineText.getSpanStart(span);
intspanEnd= lineText.getSpanEnd(span);
scaleFactor = span.getSize() / defaultSize;
intstart= Math.max(lineStart, spanStart);
intend= Math.min(lineEnd, spanEnd);
for (inti= start; i < end; i++) {
widths[i] *= scaleFactor;
}
}
}
// find index of touched characterfloatstartChar=0;
floatendChar=0;
for (inti=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?intindex= 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;
}
}
Complete Activity code
Here's the complete sample Activity code that I used. It should do exactly what you want. It's using my EnhandedMovementMethod but you can use a simple LinkMovementMethod (with the drawbacks mentioned before).
publicclassLinkTestActivityextendsActivity {
private WebView mWebView;
@SuppressLint("SetJavaScriptEnabled")@OverrideprotectedvoidonCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mWebView = (WebView) findViewById(R.id.webView);
TextViewtv= (TextView) findViewById(R.id.textView);
Stringtext="These are some sample links:\nwww.google.com\nwww.facebook.com\nwww.yahoo.com";
// Linkify the TextViewSpannablespannable=newSpannableString( 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) {
LinkSpanlinkSpan=newLinkSpan(urlSpan.getURL());
intspanStart= spannable.getSpanStart(urlSpan);
intspanEnd= 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(newWebViewClient() {
@OverridepublicbooleanshouldOverrideUrlLoading(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);
}
privateclassLinkSpanextendsURLSpan {
privateLinkSpan(String url) {
super(url);
}
@OverridepublicvoidonClick(View view) {
Stringurl= getURL();
if (mWebView != null && url != null) {
mWebView.loadUrl(url);
}
}
}
}
Solution 2:
The WebViewClient is set by call setWebViewClient()
method on you WebView refrence.
Firstly set all properties for webview then call loadURL(String url)
method, It will open sublinks to same webview instead of open into browser.
ProgressDialog progressDialog = newProgressDialog(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(newWebViewClient() {
@OverridepublicbooleanshouldOverrideUrlLoading(WebView view, String url) {
returnfalse;
}
@OverridepublicvoidonPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
progressDialog.dismiss();
}
@OverridepublicvoidonPageStarted(WebView view, String url,Bitmap favicon) {
// TODO Auto-generated method stubsuper.onPageStarted(view, url, favicon);
progressDialog.setMessage("Loading ...");
progressDialog.setCancelable(false);
progressDialog.setCanceledOnTouchOutside(false);
progressDialog.show();
}
});
Solution 3:
Your Webview
Application will receive Link when some link is clicked. User manually have to select your application. specifying host "like www.apptechinc.com" will launch this application when any link from that site is clicked.
<activityandroid:name=".MainActivity"android:label="@string/title_activity_main" ><intent-filter><actionandroid:name="android.intent.action.MAIN" /><categoryandroid:name="android.intent.category.LAUNCHER" /></intent-filter><intent-filter><actionandroid:name="android.intent.action.VIEW" /><categoryandroid:name="android.intent.category.DEFAULT" /><dataandroid:scheme="http" /><dataandroid:scheme="https" /><dataandroid:host="www.apptecxinc.com" /></intent-filter></activity>
In Activity you can receive this link as :
Stringaction= getIntent().getAction();
if (Intent.ACTION_VIEW.equals(action)) {
Uriuri= getIntent().getData();
webview.loadUrl(urlsetup(uri.getPath()));
}
Solution 4:
The issue appears to be that Linkify is looking for a scheme to detect links. Without http://
, it isn't detecting your links as web URLs. The solution would be to make sure your links include the scheme or define a custom regular expression pattern to match. It's not clear from your sample TextView data what the pattern should be. Here's a page that shows how to use the custom pattern once you work out what it is.
Solution 5:
The most important part of @Emanuel Moecklin 's answer is to subclass URLSpan
and create your own URLSpan
which has onClick
overrided.
Inspired by his answer, instead of following fully his steps, I sticked to using Linkify
. However, for some other reasons, I created my own Linkify
class, with almost the same code as original Linkify
.
And then, inside applyLink()
in Linkify, I replaced
URLSpanspan=newURLSpan(url);
with
InAppURLSpanspan=newInAppURLSpan(url);
Where the code for InAppURLSpan
is:
publicstaticclassInAppURLSpanextendsURLSpan {
publicInAppURLSpan(String url) {
super(url);
}
@OverridepublicvoidonClick(View widget) {
Stringurl= getURL();
Log.i("TNC_URL", url);
Intentintent=newIntent(widget.getContext(), SingleWebViewActivity.class);
intent.putExtra(Constants.INTENT_URL, url);
widget.getContext().startActivity(intent);
}
}
And it worked like a charm~
Post a Comment for "Open Url In Webview Instead Of Default Browser"