Android 는 SpannableString 을 이용 하여 웨 이 보 콘 텐 츠 포맷 을 실현 합 니 다.

머리말
안 드 로 이 드 개발 에서 많은 정보 전 시 는 TextView 를 통 해 보 여 줘 야 합 니 다.일반적인 정보 만 보 여 준다 면 TextView setText(CharSequence str)설정 을 사용 하면 됩 니 다.그러나 TextView 의 이 내용 은 특정한 필드 를 캡 처 해 야 할 때 점 치기 와 응답 을 받 을 수 있 습 니 다.이 럴 때 SpannableString 을 사용 해 야 합 니 다.SpannableString 과 TextView 를 결합 하면 특정한 텍스트 에 대해 특정한 처 리 를 쉽게 할 수 있다.예 를 들 어 텍스트 색상,배경 색 을 수정 하고 문 자 를 그림 으로 바 꾸 어 실현 하 며 효 과 를 클릭 하 는 등 이다.
우선 최종 실현 효과 도 를 살 펴 보 자.

첫 번 째 카드 안의 웨 이 보 는 원본 텍스트 정보 이 고 두 번 째 카드 안의 웨 이 보 는 첫 번 째 포맷 된 텍스트 내용 으로 웨 이 보 안의'화제','표정','웹 링크',그리고'@사용자'를 모두 처리 하고 클릭 하여 공식 웨 이 보 에서 보 여 준 스타일 과 일치 하도록 한다.
구현 할 효과:
4.567917.화 제 를 변색 시 키 고 제시 에 대응 하 는 화제 의 텍스트 내용 을 클릭 할 수 있 습 니 다
  • 이미지 표정 을 대응 하 는 표정 키 워드 를 교체 합 니 다
  • 링크 주 소 를 링크 의 그림 과'웹 링크'네 글자 로 바 꾸 면
  • 4
  • @사용 자 를 변색 시 키 고 해당 하 는 화제 의 텍스트 내용 을 클릭 할 수 있 습 니 다.
  • 필요:
  • 정규 표현 식 을 사용 하여 텍스트 에 대응 하 는'화제','표정','웹 링크',그리고'@사용자'내용 을 추출 합 니 다
  • 추출 한 텍스트 를 SpannableString 으로 포맷 합 니 다포맷 된 부분 에 클릭 이벤트 추가
    정규 표현 식 정의
    먼저'화제','표정','웹 링크','@사용자'에 대응 하 는 정규 표현 식 과 해당 하 는 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 에서 계승 한 Vertical ImageSpan 을 사용 하면 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))
    최종 효과 그림 은 글 의 시작 효과 와 같 고 클릭 할 수 있 습 니 다.'웹 링크'를 클릭 할 때 나타 나 는 Toast 알림 을 보 여 줍 니 다.

    총결산
    본 고 는 SpannableString 이 자주 사용 하 는 장면 만 소개 했다.예 를 들 어 특정한 텍스트 의 색 채 를 수정 하고 특정한 텍스트 를 교체 하 며 특정한 텍스트 의 클릭 이 벤트 를 소개 했다.그러나 SpannableString 의 강 함 은 이 뿐만 이 아니다.SpannableString 의 더 많은 용법 은 공식 문 서 를 읽 을 수 있 습 니 다.자,이상 이 이 글 의 전체 내용 입 니 다.본 논문 의 내용 이 여러분 의 학습 이나 업무 에 어느 정도 도움 이 되 기 를 바 랍 니 다.궁금 한 점 이 있 으 시 면 댓 글 을 남 겨 주 셔 서 저희 에 대한 지지 에 감 사 드 립 니 다.

    좋은 웹페이지 즐겨찾기