2017-04-07 274 views
5

我想畫一個SpannedStringCanvas如何在Android中使用Canvas.drawText繪製一個Spanned字符串

enter image description here

SpannableString spannableString = new SpannableString("Hello World!"); 
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED); 
BackgroundColorSpan backgroundSpan = new BackgroundColorSpan(Color.YELLOW); 
spannableString.setSpan(foregroundSpan, 1, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
spannableString.setSpan(backgroundSpan, 3, spannableString.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
textView.setText(spannableString); 

上面的例子是使用TextView,這反過來使用Layout繪製文本與跨度繪製。我知道using a Layout is the recommended way將文本繪製到畫布上。不過,我正在從頭開始自己的文本佈局,所以我需要自己實現這一點。

做這樣的事情不工作

canvas.drawText(spannableString, 0, spannableString.length(), 0, 0, mTextPaint); 

因爲drawText只能從spannableString獲取文本,沒有任何的跨度。繪圖顏色由TextPaint單獨處理。

如何使用canvas.drawText(或drawTextRun)繪製跨度信息(特別是前景色和背景色)?

相關

計劃的解決方案

我要直接做一個自我的答案,但這個是談到了差異更大比我想象的要困難。所以我會先發帖,然後每當我能弄明白的時候添加一個答案。 (我當然會歡迎任何搶先回答。)

下面是我到目前爲止的作品:

+0

那麼'Layout#draw'實際上有什麼問題? – pskink

+0

@pskink,標準'佈局'只處理LTR和RTL水平文本。我正在爲[傳統蒙古語](http://stackoverflow.com/q/29739720/3681880)製作垂直文本佈局。我試過在過去的各個[佈局黑客(http://stackoverflow.com/a/29739721/3681880),但即使是那些不允許我旋轉的表情符號或CJK字符。所以我從頭開始。 [I有基本的佈局的功能結束](https://github.com/suragch/mongol-library/blob/master/mongol-library/src/main/java/net/studymongolian/mongollibrary/MongolLayout.java);現在我需要支持spanned文本。 – Suragch

+0

好吧,現在我看到它不是一個「正常」的'你好世界'你在你的圖片 – pskink

回答

3

對於大多數人來這個問題,你應該使用一個StaticLayout提請您跨越文本。請參閱this answer尋求幫助。

但是,如果你實際上需要繪製自己的跨區文本,那麼你就需要loop through all the spanned ranges並分別繪製每一個。您還需要測量每個跨度中文本的長度,以便您知道從哪裏開始繪製下一個跨度。

下面的代碼處理BackgroundColorSpanForegroundColorSpan

// set up the spanned string 
SpannableString spannableString = new SpannableString("Hello World!"); 
ForegroundColorSpan foregroundSpan = new ForegroundColorSpan(Color.RED); 
BackgroundColorSpan backgroundSpan = new BackgroundColorSpan(Color.YELLOW); 
spannableString.setSpan(foregroundSpan, 1, 8, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 
spannableString.setSpan(backgroundSpan, 3, spannableString.length() - 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 

// draw each span one at a time 
int next; 
float xStart = 0; 
float xEnd; 
for (int i = 0; i < spannableString.length(); i = next) { 

    // find the next span transition 
    next = spannableString.nextSpanTransition(i, spannableString.length(), CharacterStyle.class); 

    // measure the length of the span 
    xEnd = xStart + mTextPaint.measureText(spannableString, i, next); 

    // draw the highlight (background color) first 
    BackgroundColorSpan[] bgSpans = spannableString.getSpans(i, next, BackgroundColorSpan.class); 
    if (bgSpans.length > 0) { 
     mHighlightPaint.setColor(bgSpans[0].getBackgroundColor()); 
     canvas.drawRect(xStart, mTextPaint.getFontMetrics().top, xEnd, mTextPaint.getFontMetrics().bottom, mHighlightPaint); 
    } 

    // draw the text with an optional foreground color 
    ForegroundColorSpan[] fgSpans = spannableString.getSpans(i, next, ForegroundColorSpan.class); 
    if (fgSpans.length > 0) { 
     int saveColor = mTextPaint.getColor(); 
     mTextPaint.setColor(fgSpans[0].getForegroundColor()); 
     canvas.drawText(spannableString, i, next, xStart, 0, mTextPaint); 
     mTextPaint.setColor(saveColor); 
    } else { 
     canvas.drawText(spannableString, i, next, xStart, 0, mTextPaint); 
    } 

    xStart = xEnd; 
} 

下圖中的頂部字符串是用上面的代碼繪製的。底部的字符串是用常規的TextView(使用StaticLayout)繪製的。

enter image description here

+0

您可以像處理前景跨度一樣處理大多數其他跨度。更新繪製狀態的方法,它會自動執行。(我現在只是將其添加爲註釋,因爲我忘記了手上的細節。如果這是你想了解更多信息,然後問我,我會更新這個答案。) – Suragch

相關問題