中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

如何在Android中利用WebView打開TextView超鏈接

發布時間:2020-11-26 16:08:24 來源:億速云 閱讀:311 作者:Leah 欄目:移動開發

今天就跟大家聊聊有關如何在Android中利用WebView打開TextView超鏈接,可能很多人都不太了解,為了讓大家更加了解,小編給大家總結了以下內容,希望大家根據這篇文章可以有所收獲。

使用Html

常規方法,給定一段標準html文檔,使用Html.fromHtml()封裝,直接使用TextView顯示。

TextView textView = (TextView) findViewById(R.id.detailed_question_tv_answer);
String testString =
"親,一般遇到這問題您可以這樣哦:<br>1.可以<font color='#ff8785'><a href='http://m.kaola.com'>催發貨</a></font>哦~<br>2.然后耐心等待哦~<br>3.1-3天后新也可以撥打我們的客服.";
textView.setMovementMethod(LinkMovementMethod.getInstance());
// 設置鏈接顏色
textView.setLinkTextColor(getResources().getColor(R.color.red_ff8785));
Spanned htmlString = Html.fromHtml(testString);
textView.setText(htmlString);

使用常規方法無論怎么設置,鏈接都會使用隱式Intent打開,即使用外部的瀏覽器打開,不符合咱們產品的需求呀。怎么才能監聽這個使用并使用內部WebView打開呢?使用SpannableStringBuilder。

使用SpannableStringBuilder

直接上代碼。

TextView textView = (TextView) findViewById(R.id.detailed_question_tv_answer);
String testString =
 "親,一般遇到這問題您可以這樣哦:<br>1.可以<font color='#ff8785'><a href='http://m.kaola.com'>催發貨</a></font>哦~<br>2.然后耐心等待哦~<br>3.1-3天后新也可以撥打我們的客服.";
textView.setMovementMethod(LinkMovementMethod.getInstance());
textView.setLinkTextColor(getResources().getColor(R.color.red_ff8785));
String linkText = "催發貨";
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);

當然,這個方法是有很大的局限性的,必須知道鏈接在文案中的具體位置,以及鏈接的地址才能夠使用這種方法。按照這種思路,我們必須使用正則表達式獲取對應的a標簽才能得到鏈接。這種方法拿到的鏈接在文案中的具體位置是難以把握的,很有可能出錯。

Html + SpannableStringBuilder

有沒有第三種方法,即能夠解析到給定文案中的所有Html標簽,又能夠使用內置的WebView打開這個鏈接?從第一種方法中,我們直接使用Html.fromHtml()方法拿到對應的Spanned結果,我們可以從這里入手,看看這個方法是怎么解析html標簽的

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轉換成Spanned
 HtmlToSpannedConverter converter =
   new HtmlToSpannedConverter(source, imageGetter, tagHandler,
     parser);
 return converter.convert();
}

接下來看一下HtmlToSpannedConverter.convert()這個方法。HtmlToSpannedConverter實現了ContentHandler接口,ContentHandler用于處理Xml文檔的解析細節。

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

我們關心Html是如何被轉換成Spanned就夠了。在整個解析Html的過程中,是通過SpannableStringBuilder.setSpan(Object what, int start, int end, int flags)這個方法不斷進行Html->Spanned轉換的。例如,遇到一個a標簽,則會通過下邊的方法設置一個Span,在SpannabbleStringBuilder內部,Span用一個數組表示,是可以累加的。

// 遇到a標簽頭
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標簽結束
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標簽的轉換方法,實際上,a標簽最后被轉換成了一個URLSpan。

看到這里,思路就來了!實際上,Html.fromHtml()方法最后轉換成的對象是一個SpannableStringBuilder,我們可以拿到這個對象的引用,然后獲取所有的URLSpan,最后把這些URLSpan全部轉換成可以監聽的事件不就實現了嗎?實際上并沒有這么簡單,URLSpan是一個類,繼承自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);
 }
}

這里已經默認使用了隱式Intent的方式打開Uri。我們不能直接改變URLSpan類的實現方式,但可以繼承這個類并覆蓋掉它的onClick(View)方法,或者直接繼承ClickableSpan。但是,這樣還是會有問題,原先的URLSpan早就在解析的時候存在于SpannableStringBuilder中的,我們需要先移除對應的URLSpan,然后再設置自己實現的新的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標簽相關的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) {
      //先移除這個Span,再新添加一個自己實現的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);
}

看完上述內容,你們對如何在Android中利用WebView打開TextView超鏈接有進一步的了解嗎?如果還想了解更多知識或者相關內容,請關注億速云行業資訊頻道,感謝大家的支持。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

大渡口区| 民权县| 岑巩县| 招远市| 新兴县| 遵化市| 南丰县| 衡阳县| 漯河市| 新野县| 余姚市| 万全县| 通城县| 元朗区| 平乡县| 禹城市| 通渭县| 宿松县| 赣州市| 商洛市| 日喀则市| 金秀| 华阴市| 贺兰县| 黔西县| 清镇市| 普定县| 陆河县| 泰宁县| 洪泽县| 石城县| 万全县| 名山县| 新丰县| 东丰县| 城固县| 来凤县| 永清县| 济源市| 镇安县| 甘南县|