2016-08-09 36 views
4

我試圖在TextView/EditText裏格式化Hashtags(就像材料設計規範中提到的芯片一樣)。我可以使用ReplacementSpan來格式化背景。但問題是我無法增加TextView/EditText中的行間距。看到下面的圖片 enter image description hereAndroid - 爲使用ReplacementSpan的SpannableStringBuilder添加頁邊距

問題是如何爲hashtags添加頂部和底部邊距?

這裏就是我的背景添加到文本的代碼:Android中

/** 
    * First draw a rectangle 
    * Then draw text on top 
    */ 
    @Override 
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { 
     RectF rect = new RectF(x, top, x + measureText(paint, text, start, end), bottom); 
     paint.setColor(backgroundColor); 
     canvas.drawRoundRect(rect, CORNER_RADIUS, CORNER_RADIUS, paint); 
     paint.setColor(textColor); 
     canvas.drawText(text, start, end, x, y, paint); 
    } 
+0

所以沒有它最終爲你工作? –

回答

4

我前一陣子也有類似的問題,這是解決方案,我想出來的:

託管TextView的XML格式:

<TextView 
     android:layout_width="match_parent" 
     android:layout_height="wrap_content" 
     android:paddingTop="18dp" 
     android:paddingBottom="18dp" 
     android:paddingLeft="8dp" 
     android:paddingRight="8dp" 
     android:gravity="fill" 
     android:textSize="12sp" 
     android:lineSpacingExtra="10sp" 
     android:textStyle="bold" 
     android:text="@{viewModel.renderedTagBadges}"> 

ReplacementSpan

public class TagBadgeSpannable extends ReplacementSpan implements LineHeightSpan { 

    private static int CORNER_RADIUS = 30; 
    private final int textColor; 
    private final int backgroundColor; 
    private final int lineHeight; 

    public TagBadgeSpannable(int lineHeight, int textColor, int backgroundColor) { 
     super(); 
     this.textColor = textColor; 
     this.backgroundColor = backgroundColor; 
     this.lineHeight = lineHeight; 
    } 

    @Override 
    public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) { 
     final float textSize = paint.getTextSize(); 
     final float textLength = x + measureText(paint, text, start, end); 
     final float badgeHeight = textSize * 2.25f; 
     final float textOffsetVertical = textSize * 1.45f; 

     RectF badge = new RectF(x, y, textLength, y + badgeHeight); 
     paint.setColor(backgroundColor); 
     canvas.drawRoundRect(badge, CORNER_RADIUS, CORNER_RADIUS, paint); 

     paint.setColor(textColor); 
     canvas.drawText(text, start, end, x, y + textOffsetVertical, paint); 
    } 

    @Override 
    public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { 
     return Math.round(paint.measureText(text, start, end)); 
    } 

    private float measureText(Paint paint, CharSequence text, int start, int end) { 
     return paint.measureText(text, start, end); 
    } 

    @Override 
    public void chooseHeight(CharSequence charSequence, int i, int i1, int i2, int i3, Paint.FontMetricsInt fontMetricsInt) { 
     fontMetricsInt.bottom += lineHeight; 
     fontMetricsInt.descent += lineHeight; 
    } 
} 
定製版本

最後一個創建Spannable的構建器

public class AndroidTagBadgeBuilder implements TagBadgeBuilder { 

    private final SpannableStringBuilder stringBuilder; 
    private final String textColor; 
    private final int lineHeight; 

    public AndroidTagBadgeBuilder(SpannableStringBuilder stringBuilder, int lineHeight, String textColor) { 
     this.stringBuilder = stringBuilder; 
     this.lineHeight = lineHeight; 
     this.textColor = textColor; 
    } 

    @Override 
    public void appendTag(String tagName, String badgeColor) { 
     final String nbspSpacing = "\u202F\u202F"; // none-breaking spaces 

     String badgeText = nbspSpacing + tagName + nbspSpacing; 
     stringBuilder.append(badgeText); 
     stringBuilder.setSpan(
      new TagBadgeSpannable(lineHeight, Color.parseColor(textColor), Color.parseColor(badgeColor)), 
      stringBuilder.length() - badgeText.length(), 
      stringBuilder.length()- badgeText.length() + badgeText.length(), 
      Spanned.SPAN_EXCLUSIVE_EXCLUSIVE 
     ); 
     stringBuilder.append(" "); 
    } 

    @Override 
    public CharSequence getTags() { 
     return stringBuilder; 
    } 

    @Override 
    public void clear() { 
     stringBuilder.clear(); 
     stringBuilder.clearSpans(); 
    } 
} 

結果將是這個樣子: Rendered badges in TextView

扭捏在TagBadgeSpannable措施,以自己的喜好。

我已經上傳了一個非常小的示例項目使用此代碼github所以隨時檢查出來。

注:樣品採用Android數據綁定,並寫入MVVM風格

4

文本標記是記錄如此糟糕,編寫這些代碼就像是在漆黑的感覺你的方式。

我已經做了一點,所以我會分享我所知道的。

您可以通過在LineHeightSpan內包裹芯片跨度來處理行間距。 LineHeightSpan是一個擴展ParagraphStyle標記接口的接口,因此它會告訴您它會影響段落級別的外觀。也許解釋它的一個好方法是將ReplacementSpan的子類與HTML <span>進行比較,而像LineHeightSpan這樣的ParagraphStyle跨度類似於HTML <div>

LineHeightSpan接口包括一個方法:

public void chooseHeight(CharSequence text, int start, int end, 
         int spanstartv, int v, 
         Paint.FontMetricsInt fm); 

這種方法被稱爲每一行中的段落

  • text是你Spanned字符串。
  • start是字符的在當前行
  • end的開始索引的字符的在當前行
  • spanstartv是(這個)垂直的整個跨度本身的偏移量結束時的指數
  • v是(這個)垂直當前行
  • fm的偏移量爲FontMetrics對象,它實際上是一個返回(入/出)參數。您的代碼將更改爲fmTextView將在繪圖時使用這些代碼。

那麼TextView會做什麼就是調用這個方法每一行它處理一次。根據參數以及您的Spanned字符串,您可以設置FontMetrics以使用您選擇的值渲染該行。

下面是一個例子,我在列表中沒有的項目符號項(認爲<ol><li>),我想每個列表項目之間的一些分離:

@Override 
    public void chooseHeight(CharSequence text, int start, int end, int spanstartv, int v, Paint.FontMetricsInt fm) { 

     int incr = Math.round(.36F * fm.ascent); // note: ascent is negative 

     // first line: add space to the top 
     if (((Spanned) text).getSpanStart(this) == start) { 
      fm.ascent += incr; 
      fm.top = fm.ascent + 1; 
     } 

     // last line: add space to the bottom 
     if (((Spanned) text).getSpanEnd(this) == end) { 
      fm.bottom -= incr; 
     } 

    } 

你的版本很可能會更簡單,只是改變了FontMetrics的它所調用的每一行的方式都是相同的。

說到破譯FontMetrics,記錄器和調試器是你的朋友。你只需要不斷調整價值觀,直到你得到你喜歡的東西。

+0

真的很好的解釋。謝謝你的親切解釋。 –

0

Doesnt BackgroundColorSpan work?

對於您的特定情況,還可以爲TextView設置lineSpacing。

最後一個選項(未測試此項),將計算跨度的高度大於您繪製的大小。您可以檢查DynamicDrawableSpan中的getSize實現,以瞭解如何使用給定的FontMetrics實例作爲參數來設置跨度的高度。