您好,登錄后才能下訂單哦!
今天就跟大家聊聊有關(guān)如何在Android中利用WebView打開(kāi)TextView超鏈接,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結(jié)了以下內(nèi)容,希望大家根據(jù)這篇文章可以有所收獲。
使用Html
常規(guī)方法,給定一段標(biāo)準(zhǔn)html文檔,使用Html.fromHtml()
封裝,直接使用TextView顯示。
TextView textView = (TextView) findViewById(R.id.detailed_question_tv_answer); String testString = "親,一般遇到這問(wèn)題您可以這樣哦:<br>1.可以<font color='#ff8785'><a href='http://m.kaola.com'>催發(fā)貨</a></font>哦~<br>2.然后耐心等待哦~<br>3.1-3天后新也可以撥打我們的客服."; textView.setMovementMethod(LinkMovementMethod.getInstance()); // 設(shè)置鏈接顏色 textView.setLinkTextColor(getResources().getColor(R.color.red_ff8785)); Spanned htmlString = Html.fromHtml(testString); textView.setText(htmlString);
使用常規(guī)方法無(wú)論怎么設(shè)置,鏈接都會(huì)使用隱式Intent打開(kāi),即使用外部的瀏覽器打開(kāi),不符合咱們產(chǎn)品的需求呀。怎么才能監(jiān)聽(tīng)這個(gè)使用并使用內(nèi)部WebView打開(kāi)呢?使用SpannableStringBuilder。
使用SpannableStringBuilder
直接上代碼。
TextView textView = (TextView) findViewById(R.id.detailed_question_tv_answer); String testString = "親,一般遇到這問(wèn)題您可以這樣哦:<br>1.可以<font color='#ff8785'><a href='http://m.kaola.com'>催發(fā)貨</a></font>哦~<br>2.然后耐心等待哦~<br>3.1-3天后新也可以撥打我們的客服."; textView.setMovementMethod(LinkMovementMethod.getInstance()); textView.setLinkTextColor(getResources().getColor(R.color.red_ff8785)); String linkText = "催發(fā)貨"; int startIndexOfLink = testString.indexOf(linkText); int endIndexOfLink = startIndexOfLink + linkText.length(); SpannableStringBuilder spannableStringBuilder = new SpannableStringBuilder(testString); spannableStringBuilder.setSpan(new ClickableSpan() { @Override public void onClick(View widget) { ActivityUtils.startWebviewActivity(DetailedQuestionActivity.this, "http://m.kaola.com", false); } }, startIndexOfLink, endIndexOfLink, Spannable.SPAN_INCLUSIVE_EXCLUSIVE); textView.setText(spannableStringBuilder);
當(dāng)然,這個(gè)方法是有很大的局限性的,必須知道鏈接在文案中的具體位置,以及鏈接的地址才能夠使用這種方法。按照這種思路,我們必須使用正則表達(dá)式獲取對(duì)應(yīng)的a標(biāo)簽才能得到鏈接。這種方法拿到的鏈接在文案中的具體位置是難以把握的,很有可能出錯(cuò)。
Html + SpannableStringBuilder
有沒(méi)有第三種方法,即能夠解析到給定文案中的所有Html標(biāo)簽,又能夠使用內(nèi)置的WebView打開(kāi)這個(gè)鏈接?從第一種方法中,我們直接使用Html.fromHtml()
方法拿到對(duì)應(yīng)的Spanned結(jié)果,我們可以從這里入手,看看這個(gè)方法是怎么解析html標(biāo)簽的
public static Spanned fromHtml(String source, ImageGetter imageGetter, TagHandler tagHandler) { // 使用org.ccil.cowan.tagsoup.Parser作為解析器 Parser parser = new Parser(); try { parser.setProperty(Parser.schemaProperty, HtmlParser.schema); } catch (org.xml.sax.SAXNotRecognizedException e) { // Should not happen. throw new RuntimeException(e); } catch (org.xml.sax.SAXNotSupportedException e) { // Should not happen. throw new RuntimeException(e); } // 使用HtmlToSpannedConverter將Ttml轉(zhuǎn)換成Spanned HtmlToSpannedConverter converter = new HtmlToSpannedConverter(source, imageGetter, tagHandler, parser); return converter.convert(); }
接下來(lái)看一下HtmlToSpannedConverter.convert()
這個(gè)方法。HtmlToSpannedConverter實(shí)現(xiàn)了ContentHandler接口,ContentHandler用于處理Xml文檔的解析細(xì)節(jié)。
public Spanned convert() { mReader.setContentHandler(this); try { mReader.parse(new InputSource(new StringReader(mSource))); } catch (IOException e) { // We are reading from a string. There should not be IO problems. throw new RuntimeException(e); } catch (SAXException e) { // TagSoup doesn't throw parse exceptions. throw new RuntimeException(e); } // Fix flags and range for paragraph-type markup. Object[] obj = mSpannableStringBuilder.getSpans(0, mSpannableStringBuilder.length(), ParagraphStyle.class); for (int i = 0; i < obj.length; i++) { int start = mSpannableStringBuilder.getSpanStart(obj[i]); int end = mSpannableStringBuilder.getSpanEnd(obj[i]); // If the last line of the range is blank, back off by one. if (end - 2 >= 0) { if (mSpannableStringBuilder.charAt(end - 1) == '\n' && mSpannableStringBuilder.charAt(end - 2) == '\n') { end--; } } if (end == start) { mSpannableStringBuilder.removeSpan(obj[i]); } else { mSpannableStringBuilder.setSpan(obj[i], start, end, Spannable.SPAN_PARAGRAPH); } } return mSpannableStringBuilder; }
我們關(guān)心Html是如何被轉(zhuǎn)換成Spanned就夠了。在整個(gè)解析Html的過(guò)程中,是通過(guò)SpannableStringBuilder.setSpan(Object what, int start, int end, int flags)
這個(gè)方法不斷進(jìn)行Html->Spanned
轉(zhuǎn)換的。例如,遇到一個(gè)a標(biāo)簽,則會(huì)通過(guò)下邊的方法設(shè)置一個(gè)Span,在SpannabbleStringBuilder內(nèi)部,Span用一個(gè)數(shù)組表示,是可以累加的。
// 遇到a標(biāo)簽頭 private static void startA(SpannableStringBuilder text, Attributes attributes) { String href = attributes.getValue("", "href"); int len = text.length(); text.setSpan(new Href(href), len, len, Spannable.SPAN_MARK_MARK); } // a標(biāo)簽結(jié)束 private static void endA(SpannableStringBuilder text) { int len = text.length(); Object obj = getLast(text, Href.class); int where = text.getSpanStart(obj); text.removeSpan(obj); if (where != len) { Href h = (Href) obj; if (h.mHref != null) { text.setSpan(new URLSpan(h.mHref), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); } } }
可以看到a標(biāo)簽的轉(zhuǎn)換方法,實(shí)際上,a標(biāo)簽最后被轉(zhuǎn)換成了一個(gè)URLSpan。
看到這里,思路就來(lái)了!實(shí)際上,Html.fromHtml()
方法最后轉(zhuǎn)換成的對(duì)象是一個(gè)SpannableStringBuilder,我們可以拿到這個(gè)對(duì)象的引用,然后獲取所有的URLSpan,最后把這些URLSpan全部轉(zhuǎn)換成可以監(jiān)聽(tīng)的事件不就實(shí)現(xiàn)了嗎?實(shí)際上并沒(méi)有這么簡(jiǎn)單,URLSpan是一個(gè)類,繼承自ClickableSpan,覆蓋了其中的onClick(View)
方法:
public class URLSpan extends ClickableSpan implements ParcelableSpan { private final String mURL; public URLSpan(String url) { mURL = url; } public URLSpan(Parcel src) { mURL = src.readString(); } public int getSpanTypeId() { return TextUtils.URL_SPAN; } public int describeContents() { return 0; } public void writeToParcel(Parcel dest, int flags) { dest.writeString(mURL); } public String getURL() { return mURL; } @Override public void onClick(View widget) { Uri uri = Uri.parse(getURL()); Context context = widget.getContext(); Intent intent = new Intent(Intent.ACTION_VIEW, uri); intent.putExtra(Browser.EXTRA_APPLICATION_ID, context.getPackageName()); context.startActivity(intent); } }
這里已經(jīng)默認(rèn)使用了隱式Intent的方式打開(kāi)Uri。我們不能直接改變URLSpan類的實(shí)現(xiàn)方式,但可以繼承這個(gè)類并覆蓋掉它的onClick(View)
方法,或者直接繼承ClickableSpan。但是,這樣還是會(huì)有問(wèn)題,原先的URLSpan早就在解析的時(shí)候存在于SpannableStringBuilder中的,我們需要先移除對(duì)應(yīng)的URLSpan,然后再設(shè)置自己實(shí)現(xiàn)的新的ClickableSpan就可以了。
具體代碼如下:
public static SpannableStringBuilder setTextLinkOpenByWebView(final Context context, String answerString) { if (!TextUtils.isEmpty(answerString)) { Spanned htmlString = Html.fromHtml(answerString); if (htmlString instanceof SpannableStringBuilder) { SpannableStringBuilder spannableStringBuilder = (SpannableStringBuilder) htmlString; // 取得與a標(biāo)簽相關(guān)的Span Object[] objs = spannableStringBuilder.getSpans(0, spannableStringBuilder.length(), URLSpan.class); if (null != objs && objs.length != 0) { for (Object obj : objs) { int start = spannableStringBuilder.getSpanStart(obj); int end = spannableStringBuilder.getSpanEnd(obj); if (obj instanceof URLSpan) { //先移除這個(gè)Span,再新添加一個(gè)自己實(shí)現(xiàn)的Span。 URLSpan span = (URLSpan) obj; final String url = span.getURL(); spannableStringBuilder.removeSpan(obj); spannableStringBuilder.setSpan(new ClickableSpan() { @Override public void onClick(View widget) { ActivityUtils.startWebviewActivity(context, url, true); } }, start, end, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); } } } return spannableStringBuilder; } } return new SpannableStringBuilder(answerString); }
看完上述內(nèi)容,你們對(duì)如何在Android中利用WebView打開(kāi)TextView超鏈接有進(jìn)一步的了解嗎?如果還想了解更多知識(shí)或者相關(guān)內(nèi)容,請(qǐng)關(guān)注億速云行業(yè)資訊頻道,感謝大家的支持。
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。