91超碰碰碰碰久久久久久综合_超碰av人澡人澡人澡人澡人掠_国产黄大片在线观看画质优化_txt小说免费全本

溫馨提示×

溫馨提示×

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

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

怎么在Android中利用SpannableString對內容進行格式化

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

這篇文章將為大家詳細講解有關怎么在Android中利用SpannableString對內容進行格式化,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。

要實現的效果:

  1. 將話題進行變色并且可以點擊提示對應的話題文本內容

  2. 將圖片表情替換掉對應的表情關鍵字顯示

  3. 將鏈接地址替換成一個鏈接的圖片和”網頁鏈接”四個字顯示

  4. 將@的用戶進行變色并且可以點擊提示對應的話題文本內容

需要:

  1. 使用正則表達式提取文本內對應的”話題”、”表情”、”網頁鏈接”、以及”@用戶”內容

  2. 使用 SpannableString 格式化提取到的文本

  3. 給格式化的部分添加點擊事件

定義正則表達式

首先定義”話題”、”表情”、”網頁鏈接”、以及”@用戶”對應的正則表達式和對應的 Pattern。SCHEME 下文會提到具體的用處的。

public class WeiboPattern {

 // #話題#
 public static final String REGEX_TOPIC = "#[\\p{Print}\\p{InCJKUnifiedIdeographs}&&[^#]]+#";
 // [表情]
 public static final String REGEX_EMOTION = "\\[(\\S+?)\\]";
 // url
 public static final String REGEX_URL = "http://[a-zA-Z0-9+&@#/%?=~_\\\\-|!:,\\\\.;]*[a-zA-Z0-9+&@#/%=~_|]";
 // @人
 public static final String REGEX_AT = "@[\\w\\p{InCJKUnifiedIdeographs}-]{1,26}";

 
 public static final Pattern PATTERN_TOPIC = Pattern.compile(REGEX_TOPIC);
 public static final Pattern PATTERN_EMOTION = Pattern.compile(REGEX_EMOTION);
 public static final Pattern PATTERN_URL = Pattern.compile(REGEX_URL);
 public static final Pattern PATTERN_AT = Pattern.compile(REGEX_AT);

 public static final String SCHEME_TOPIC = "topic:";
 public static final String SCHEME_URL = "url:";
 public static final String SCHEME_AT = "at:";

}

提取匹配部分并使用 SpannableString 格式化

我將此過程寫到一個方法內了,下面直接上代碼,代碼中有詳細的注釋解釋:

/**
 * 格式化微博文本
 *
 * @param context 上下文
 * @param source 源文本
 * @param textView 目標 TextView
 * @return SpannableStringBuilder
 */
public static SpannableStringBuilder formatWeiBoContent(Context context, String source, TextView textView) {

 // 獲取到 TextView 的文字大小,后面的 ImageSpan 需要用到該值
 int textSize = (int) textView.getTextSize();

 // 若要部分 SpannableString 可點擊,需要如下設置
 textView.setMovementMethod(LinkMovementMethod.getInstance());

 // 將要格式化的 String 構建成一個 SpannableStringBuilder
 SpannableStringBuilder value = new SpannableStringBuilder(source);

 // 使用正則匹配話題
 Linkify.addLinks(value, WeiboPattern.PATTERN_TOPIC, WeiboPattern.SCHEME_TOPIC);
 // 使用正則匹配鏈接
 Linkify.addLinks(value, WeiboPattern.PATTERN_URL, WeiboPattern.SCHEME_URL);
 // 使用正則匹配@用戶
 Linkify.addLinks(value, WeiboPattern.PATTERN_AT, WeiboPattern.SCHEME_AT);

 // 自定義的匹配部分的點擊效果
 MyClickableSpan clickSpan;

 // 獲取上面到所有 addLinks 后的匹配部分(這里一個匹配項被封裝成了一個 URLSpan 對象)
 URLSpan[] urlSpans = value.getSpans(0, value.length(), URLSpan.class);

 // 遍歷所有的 URLSpan
 for (final URLSpan urlSpan : urlSpans) {
 // 點擊匹配部分效果
  clickSpan = new MyClickableSpan() {
   @Override
   public void onClick(View view) {
    ToastUtils.makeShort(urlSpan.getURL());
   }
  };
  // 話題
  if (urlSpan.getURL().startsWith(WeiboPattern.SCHEME_TOPIC)) {
   int start = value.getSpanStart(urlSpan);
   int end = value.getSpanEnd(urlSpan);
   value.removeSpan(urlSpan);
   // 格式化話題部分文本
   value.setSpan(clickSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  }
  // @用戶
  if (urlSpan.getURL().startsWith(WeiboPattern.SCHEME_AT)) {
   int start = value.getSpanStart(urlSpan);
   int end = value.getSpanEnd(urlSpan);
   value.removeSpan(urlSpan);
   // 格式化@用戶部分文本
   value.setSpan(clickSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  }
  // 鏈接
  if (urlSpan.getURL().startsWith(WeiboPattern.SCHEME_URL)) {
   int start = value.getSpanStart(urlSpan);
   int end = value.getSpanEnd(urlSpan);
   value.removeSpan(urlSpan);
   SpannableStringBuilder urlSpannableString = getUrlTextSpannableString(context, urlSpan.getURL(), textSize);
   value.replace(start, end, urlSpannableString);
   // 格式化鏈接部分文本
   value.setSpan(clickSpan, start, start + urlSpannableString.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
  }
 }

 // 表情需要單獨格式化
 Matcher emotionMatcher = WeiboPattern.PATTERN_EMOTION.matcher(value);
 while (emotionMatcher.find()) {
  String emotion = emotionMatcher.group();
  int start = emotionMatcher.start();
  int end = emotionMatcher.end();
  int resId = EmotionUtils.getImageByName(emotion);
  if (resId != -1) { // 表情匹配
   L.e("find emotion: " + emotion);
   Drawable drawable = context.getResources().getDrawable(resId);
   drawable.setBounds(0, 0, (int) (textSize * 1.3), (int) (textSize * 1.3));
   // 自定義的 VerticalImageSpan ,可解決默認的 ImageSpan 不垂直居中的問題
   VerticalImageSpan imageSpan = new VerticalImageSpan(drawable);
   value.setSpan(imageSpan, start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
  }
 }

 return value;
}
private static SpannableStringBuilder getUrlTextSpannableString(Context context, String source, int size) {
 SpannableStringBuilder builder = new SpannableStringBuilder(source);
 String prefix = " ";
 builder.replace(0, prefix.length(), prefix);
 Drawable drawable = context.getResources().getDrawable(R.drawable.ic_status_link);
 drawable.setBounds(0, 0, size, size);
 builder.setSpan(new VerticalImageSpan(drawable), prefix.length(), source.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
 builder.append(" 網頁鏈接");
 return builder;
}

getUrlTextSpannableString() :方法是用來返回一個圖標+”網頁鏈接” SpannableString,用于替換鏈接文本

上面將”話題”、”表情”、”網頁鏈接”都用了addLinks方法來標記的,然后統一處理。表情則是單獨處理的。

表情則使用如下方法事先做好映射:

public class EmotionUtils {

 public static LinkedHashMap<String, Integer> sMap;

 static {
  sMap = new LinkedHashMap<>();
  sMap.put("[doge]", R.drawable.d_doge);
  sMap.put("[污]", R.drawable.d_wu);
 }

 public static int getImageByName(String name) {
  Integer integer = sMap.get(name);
  return integer == null ? -1 : integer;
 }

}

還有剛才說到的自定義 MyClickableSpan 修改默認的樣式:

public class MyClickableSpan extends ClickableSpan {

 @Override
 public void onClick(View view) {

 }

 @Override
 public void updateDrawState(TextPaint ds) {
  super.updateDrawState(ds);
  ds.setColor(0xff03A9F4);
  ds.setUnderlineText(false);
 }

}

另外,由于默認的 ImageSpan 在 TextView 有使用android:lineSpacingExtra屬性時,不會垂直居中,所以使用到了網上的一個繼承自 ImageSpan 的 VerticalImageSpan 可以做到保持圖片在 TextView 內保持垂直居中:

public class VerticalImageSpan extends ImageSpan {

 public VerticalImageSpan(Drawable drawable) {
  super(drawable);
 }

 /**
  * update the text line height
  */
 @Override
 public int getSize(Paint paint, CharSequence text, int start, int end,
      Paint.FontMetricsInt fontMetricsInt) {
  Drawable drawable = getDrawable();
  Rect rect = drawable.getBounds();
  if (fontMetricsInt != null) {
   Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
   int fontHeight = fmPaint.descent - fmPaint.ascent;
   int drHeight = rect.bottom - rect.top;
   int centerY = fmPaint.ascent + fontHeight / 2;

   fontMetricsInt.ascent = centerY - drHeight / 2;
   fontMetricsInt.top = fontMetricsInt.ascent;
   fontMetricsInt.bottom = centerY + drHeight / 2;
   fontMetricsInt.descent = fontMetricsInt.bottom;
  }
  return rect.right;
 }

 /**
  * see detail message in android.text.TextLine
  *
  * @param canvas the canvas, can be null if not rendering
  * @param text the text to be draw
  * @param start the text start position
  * @param end the text end position
  * @param x  the edge of the replacement closest to the leading margin
  * @param top the top of the line
  * @param y  the baseline
  * @param bottom the bottom of the line
  * @param paint the work paint
  */
 @Override
 public void draw(Canvas canvas, CharSequence text, int start, int end,
      float x, int top, int y, int bottom, Paint paint) {

  Drawable drawable = getDrawable();
  canvas.save();
  Paint.FontMetricsInt fmPaint = paint.getFontMetricsInt();
  int fontHeight = fmPaint.descent - fmPaint.ascent;
  int centerY = y + fmPaint.descent - fontHeight / 2;
  int transY = centerY - (drawable.getBounds().bottom - drawable.getBounds().top) / 2;
  canvas.translate(x, transY);
  drawable.draw(canvas);
  canvas.restore();
 }

}

然后直接調用該方法格式化:

mTextView.setText(formatWeiBoContent(this,mTextView.getText().toString(),mTextView))

關于怎么在Android中利用SpannableString對內容進行格式化就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。

向AI問一下細節

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

AI

沙坪坝区| 吉隆县| 兴化市| 维西| 长兴县| 巫山县| 乃东县| 城口县| 石泉县| 青河县| 赤水市| 博罗县| 贵溪市| 怀安县| 泸定县| 扎兰屯市| 安宁市| 安福县| 丽江市| 淮北市| 武隆县| 康保县| 汕头市| 皋兰县| 章丘市| 巫溪县| 吴桥县| 万荣县| 建瓯市| 临沭县| 井研县| 梧州市| 长沙县| 新建县| 伊金霍洛旗| 安岳县| 上林县| 邛崃市| 翁牛特旗| 阿尔山市| 白河县|