将多行文本绘制到Canvas

一个希望很快的问题,但我似乎无法find任何示例…我想通过Canvas写多行文本到自定义View ,并在onDraw()我有:

 ... String text = "This is\nmulti-line\ntext"; canvas.drawText(text, 100, 100, mTextPaint); ... 

我希望这会导致换行符,但是我看到的是\n会出现的神秘字符。

任何指针赞赏。

保罗

不幸的是,Android不知道\n是什么。 你必须做的是去除\n ,然后抵消Y来获得你的文字在下一行。 所以像这样的东西:

 canvas.drawText("This is", 100, 100, mTextPaint); canvas.drawText("multi-line", 100, 150, mTextPaint); canvas.drawText("text", 100, 200, mTextPaint); 

我发现另一种使用静态布局的方法。 代码在这里供任何人参考:

 TextPaint mTextPaint=new TextPaint(); StaticLayout mTextLayout = new StaticLayout(mText, mTextPaint, canvas.getWidth(), Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); canvas.save(); // calculate x and y position where your text will be placed textX = ... textY = ... canvas.translate(textX, textY); mTextLayout.draw(canvas); canvas.restore(); 

只需遍历每一行:

 int x = 100, y = 100; for (String line: text.split("\n")) { canvas.drawText(line, x, y, mTextPaint); y += mTextPaint.descent() - mTextPaint.ascent(); } 

这是我的解决scheme,是基于@戴夫的答案(谢谢btw ;-))

 import android.graphics.Canvas; import android.graphics.Paint; public class mdCanvas { private Canvas m_canvas; public mdCanvas(Canvas canvas) { m_canvas = canvas; } public void drawMultiline(String str, int x, int y, Paint paint) { for (String line: str.split("\n")) { m_canvas.drawText(line, x, y, paint); y += -paint.ascent() + paint.descent(); } } } 

我试图inheritancecanvas,但它并不真的让你。 所以这是一个中间阶级!

我必须在这里添加我认为是STROKE WIDTH的版本。

 void drawMultiLineText(String str, float x, float y, Paint paint, Canvas canvas) { String[] lines = str.split("\n"); float txtSize = -paint.ascent() + paint.descent(); if (paint.getStyle() == Style.FILL_AND_STROKE || paint.getStyle() == Style.STROKE){ txtSize += paint.getStrokeWidth(); //add stroke width to the text size } float lineSpace = txtSize * 0.2f; //default line spacing for (int i = 0; i < lines.length; ++i) { canvas.drawText(lines[i], x, y + (txtSize + lineSpace) * i, paint); } } 

是。 使用canvas.getFontSpacing()作为增量。 我自己出于好奇而尝试过,它适用于任何字体大小。

我写了完整的例子

在这里输入图像说明

colors.xml

  <color name="transparentBlack">#64000000</color> 

java类

  public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Bitmap bm = BitmapFactory.decodeResource(getResources(), R.drawable.amit); ImageView imageView = (ImageView)findViewById(R.id.imageView); imageView.setImageBitmap(drawTextToBitmap(this, bm, "Name: Kolala\nDate: Dec 23 2016 12:47 PM, \nLocation: 440 Banquets & Restaurents")); } public Bitmap drawTextToBitmap(Context gContext, Bitmap bitmap, String gText) { Resources resources = gContext.getResources(); float scale = resources.getDisplayMetrics().density; android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig(); // set default bitmap config if none if(bitmapConfig == null) { bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888; } // resource bitmaps are imutable, // so we need to convert it to mutable one bitmap = bitmap.copy(bitmapConfig, true); Canvas canvas = new Canvas(bitmap); // new antialised Paint Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); // text color - #3D3D3D paint.setColor(Color.WHITE); // text size in pixels paint.setTextSize((int) (25 * scale)); // text shadow paint.setShadowLayer(1f, 0f, 1f, Color.WHITE); // draw text to the Canvas center Rect bounds = new Rect(); int noOfLines = 0; for (String line: gText.split("\n")) { noOfLines++; } paint.getTextBounds(gText, 0, gText.length(), bounds); int x = 20; int y = (bitmap.getHeight() - bounds.height()*noOfLines); Paint mPaint = new Paint(); mPaint.setColor(getResources().getColor(R.color.transparentBlack)); int left = 0; int top = (bitmap.getHeight() - bounds.height()*(noOfLines+1)); int right = bitmap.getWidth(); int bottom = bitmap.getHeight(); canvas.drawRect(left, top, right, bottom, mPaint); for (String line: gText.split("\n")) { canvas.drawText(line, x, y, paint); y += paint.descent() - paint.ascent(); } return bitmap; } } 

我重新使用了GreenBee提出的解决scheme,并且做了一个函数,如果截断发生,最后用“…”把一些多行文本画成指定的边界:

 public static void drawMultiLineEllipsizedText(final Canvas _canvas, final TextPaint _textPaint, final float _left, final float _top, final float _right, final float _bottom, final String _text) { final float height = _bottom - _top; final StaticLayout measuringTextLayout = new StaticLayout(_text, _textPaint, (int) Math.abs(_right - _left), Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); int line = 0; final int totalLineCount = measuringTextLayout.getLineCount(); for (line = 0; line < totalLineCount; line++) { final int lineBottom = measuringTextLayout.getLineBottom(line); if (lineBottom > height) { break; } } line--; if (line < 0) { return; } int lineEnd; try { lineEnd = measuringTextLayout.getLineEnd(line); } catch (Throwable t) { lineEnd = _text.length(); } String truncatedText = _text.substring(0, Math.max(0, lineEnd)); if (truncatedText.length() < 3) { return; } if (truncatedText.length() < _text.length()) { truncatedText = truncatedText.substring(0, Math.max(0, truncatedText.length() - 3)); truncatedText += "..."; } final StaticLayout drawingTextLayout = new StaticLayout(truncatedText, _textPaint, (int) Math.abs(_right - _left), Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); _canvas.save(); _canvas.translate(_left, _top); drawingTextLayout.draw(_canvas); _canvas.restore(); } 

尝试这个

 Paint paint1 = new Paint(); paint1.setStyle(Paint.Style.FILL); paint1.setAntiAlias(true); paint1.setColor(Color.BLACK); paint1.setTextSize(15); TextView tv = new TextView(context); tv.setTextColor(Color.BLACK); LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); llp.setMargins(5, 2, 0, 0); // llp.setMargins(left, top, right, bottom); tv.setLayoutParams(llp); tv.setTextSize(10); String text="this is good to see you , i am the king of the team"; tv.setText(text); tv.setDrawingCacheEnabled(true); tv.measure(MeasureSpec.makeMeasureSpec(canvas.getWidth(), MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(canvas.getHeight(), MeasureSpec.EXACTLY)); tv.layout(0, 0, tv.getMeasuredWidth(), tv.getMeasuredHeight()); canvas.drawBitmap(tv.getDrawingCache(), 5, 10, paint1); tv.setDrawingCacheEnabled(false); 

它会工作。 我testing了

  public Bitmap drawMultilineTextToBitmap(Context gContext, int gResId, String gText) { // prepare canvas Resources resources = gContext.getResources(); float scale = resources.getDisplayMetrics().density; Bitmap bitmap = BitmapFactory.decodeResource(resources, gResId); android.graphics.Bitmap.Config bitmapConfig = bitmap.getConfig(); // set default bitmap config if none if(bitmapConfig == null) { bitmapConfig = android.graphics.Bitmap.Config.ARGB_8888; } // resource bitmaps are imutable, // so we need to convert it to mutable one bitmap = bitmap.copy(bitmapConfig, true); Canvas canvas = new Canvas(bitmap); // new antialiased Paint TextPaint paint=new TextPaint(Paint.ANTI_ALIAS_FLAG); // text color - #3D3D3D paint.setColor(Color.rgb(61, 61, 61)); // text size in pixels paint.setTextSize((int) (14 * scale)); // text shadow paint.setShadowLayer(1f, 0f, 1f, Color.WHITE); // set text width to canvas width minus 16dp padding int textWidth = canvas.getWidth() - (int) (16 * scale); // init StaticLayout for text StaticLayout textLayout = new StaticLayout( gText, paint, textWidth, Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); // get height of multiline text int textHeight = textLayout.getHeight(); // get position of text's top left corner float x = (bitmap.getWidth() - textWidth)/2; float y = (bitmap.getHeight() - textHeight)/2; // draw text to the Canvas center canvas.save(); canvas.translate(x, y); textLayout.draw(canvas); canvas.restore(); return bitmap; } 

资料来源: http : //www.skoumal.net/en/android-drawing-multiline-text-on-bitmap/

没有StaticLayout的解决scheme

 //Get post text String text = post.getText(); //Get weight of space character in px float spaceWeight = paint.measureText(" "); //Start main algorithm of drawing words on canvas //Split text to words for (String line : text.split(" ")) { //If we had empty space just continue if (line.equals("")) continue; //Get weight of the line float lineWeight = paint.measureText(line); //If our word(line) doesn't have any '\n' we do next if (line.indexOf('\n') == -1) { //If word can fit into current line if (cnv.getWidth() - pxx - defaultMargin >= lineWeight) { //Draw text cnv.drawText(line, pxx, pxy, paint); //Move start x point to word weight + space weight pxx += lineWeight + spaceWeight; } else { //If word can't fit into current line //Move x point to start //Move y point to the next line pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); //Draw cnv.drawText(line, pxx, pxy, paint); //Move x point to word weight + space weight pxx += lineWeight + spaceWeight; } //If line contains '\n' } else { //If '\n' is on the start of the line if (line.indexOf('\n') == 0) { pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); cnv.drawText(line.replaceAll("\n", ""), pxx, pxy, paint); pxx += lineWeight + spaceWeight; } else { //If '\n' is somewhere in the middle //and it also can contain few '\n' //Split line to sublines String[] subline = line.split("\n"); for (int i = 0; i < subline.length; i++) { //Get weight of new word lineWeight = paint.measureText(subline[i]); //If it's empty subline that's mean that we have '\n' if (subline[i].equals("")) { pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); cnv.drawText(subline[i], pxx, pxy, paint); continue; } //If we have only one word if (subline.length == 1 && i == 0) { if (cnv.getWidth() - pxx >= lineWeight) { cnv.drawText(subline[0], pxx, pxy, paint); pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); } else { pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); cnv.drawText(subline[0], pxx, pxy, paint); pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); } continue; } //If we have set of words separated with '\n' //it is the first word //Make sure we can put it into current line if (i == 0) { if (cnv.getWidth() - pxx >= lineWeight) { cnv.drawText(subline[0], pxx, pxy, paint); pxx = defaultMargin; } else { pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); cnv.drawText(subline[0], pxx, pxy, paint); pxx = defaultMargin; } } else { pxx = defaultMargin; pxy += paint.descent() - paint.ascent(); cnv.drawText(subline[i], pxx, pxy, paint); pxx += lineWeight + spaceWeight; } } } } } 

我的dynamic文本大小和间距的例子,对我很好…

 public Bitmap fontTexture(String string, final Context context) { float text_x = 512; float text_y = 512; final float scale = context.getResources().getDisplayMetrics().density; int mThreshold = (int) (THRESHOLD_DIP * scale + 0.5f); String[] splited = string.split("\\s+"); double longest = 0; for(String s:splited){ if (s.length() > longest) { longest = s.length(); } } if(longest > MAX_STRING_LENGTH) { double ratio = (double) MAX_STRING_LENGTH / longest; mThreshold = (int) ((THRESHOLD_DIP * ((float) ratio)) * scale + 0.5f); } Bitmap bitmap = Bitmap.createBitmap(1024, 1024, Bitmap.Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/dotted_font.ttf"); TextPaint mTextPaint=new TextPaint(); mTextPaint.setColor(Color.YELLOW); mTextPaint.setTextAlign(Paint.Align.CENTER); mTextPaint.setTextSize(mThreshold); mTextPaint.setTypeface(font); StaticLayout mTextLayout = new StaticLayout(string, mTextPaint, canvas.getWidth(), Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, false); canvas.save(); canvas.translate(text_x, text_y); mTextLayout.draw(canvas); canvas.restore(); return bitmap; }