Android Canvas: draw multiline text in a rectangle

Ja ich weis: der Titel ist in Englisch, aber der Beitrag ist in Deutsch. Aber irgendwie ist mir kein guter und vorallem kurzer Titel eingefallen. Es geht wieder um Canvas, speziell wie man einen Text ein- oder mehrzeilig in ein Rechteck schreiben kann. Hierfür gibt es verschiedene Möglichkeiten, die ich in diesem Artikel beschreibe.

 

 

1. Möglichkeit

Normalerweise findet man nach 3 Minuten googlen eine Lösung, aber nicht bei diesem Problem. Bei Stackoverflow gab es zwar eine Lösung namens StaticLayout, aber irgendwie ist die Lösung nicht schön und auch nicht performant. Der Code sieht so aus:

RectF rect = new RectF(....)

StaticLayout sl = new StaticLayout("This is my text that must fit to a rectangle", textPaint, (int)rect.width(), Layout.Alignment.ALIGN_CENTER, 1, 1, false);

canvas.save();
canvas.translate(rect.left, rect.top);
sl.draw(canvas);
canvas.restore();

 

2. Möglichkeit

Daher habe ich nach einer weiteren Möglichkeit gesucht und bin auf dieses Beispiel hier mit einem TextRect gestoßen. Es war genau das, was ich gesucht habe. ABER ohne Padding und wenn man ein zweites Mal das TextRect gezeichnet hat, war die Schrift weg. Daher habe ich mir kurzer Hand eine eigene Implementierung geschrieben, die so aussieht:

Verwendung


 
		// set up font
		final Paint fontPaint = new Paint();
		fontPaint.setColor( Color.BLACK);
		fontPaint.setAntiAlias( true );
		fontPaint.setTextSize( 24 );
		// Farbe und Padding
 		TextRect textRect = new TextRect( fontPaint, 8);
	 
		final int h = textRect.prepare("Hallo Welt", 30, 30);
 
		// draw text 
		textRect.draw( canvas ,50, 50);

Klasse

package de.axxg.test;
 
import java.util.ArrayList;
 
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.Rect;
 
/**
 * Alternative Implementierung
 * @author axxg.de
 *
 */
public class TextRect {
	private Rect bounds = new Rect();
	
	
	private ArrayList<String> ergList;
	private String text = null;
	private Paint paint = null;
	private float padding = 0;
	private FontMetricsInt metrics = null;
 
	public TextRect(final Paint paint) {
		metrics = paint.getFontMetricsInt();
		this.paint = paint;
		this.padding = 0;
	}
	
	public TextRect(final Paint paint, float padding) {
		metrics = paint.getFontMetricsInt();
		this.paint = paint;
		this.padding = padding;
	}
	
	public String getText(){
		return text;
	}
 
	public void prepare(final String text, final float orgMaxWidth, final float orgMaxHeight) {
		this.text = text;
		
		final int maxWidth = Math.round(orgMaxWidth - (padding * 2));
		final int maxHeight = Math.round(orgMaxHeight - (padding * 2));
		
		final int lineHeight = -metrics.ascent + metrics.descent;
		
		final int maxZeilenAnz = maxHeight / lineHeight;
		
		ergList = new ArrayList<String>();
		
		int zeiger = 0;
		for (int i = 0; i < maxZeilenAnz; i++) {
			String zeile = "";
			
			boolean zulang = false;
			while (!zulang && zeiger < text.length()) {
				String tmpZzeile = zeile + text.charAt(zeiger);
				paint.getTextBounds(tmpZzeile, 0, tmpZzeile.length(), bounds);
				if(bounds.width() > maxWidth){
					zulang = true;
					
					// falls es zu lang wird!
					if((i == (maxZeilenAnz - 1)) && zeile.length() > 4){
						zeile = zeile.substring(0, zeile.length() - 3) + "...";
					}else{
						// schauen ob ein Leerzeichen in der naehe ist
						int posLeerz = zeile.lastIndexOf(" ");
						if(posLeerz != -1 && (6 > (zeile.length() - posLeerz))){
							
							zeiger = text.substring(0, zeiger).lastIndexOf(" ") + 1;
							zeile = zeile.substring(0, posLeerz);
						}
					}
				}else{
					zeile = tmpZzeile;
					zeiger++;
				}
			}
			if(!zeile.equalsIgnoreCase("")){
				ergList.add(zeile);			
			}
			if(zeiger > text.length()){
				break;
			}
		}
	}
 
	/**
	 * Draw prepared text at given position.
	 * 
	 * @param canvas
	 *            - canvas to draw text into
	 * @param left
	 *            - left corner
	 * @param top
	 *            - top corner
	 */
	public void draw(final Canvas canvas, final float orgLeft, final float orgTop) {
		final float left = orgLeft + padding;
		final float top = orgTop + padding;
 
		final int before = -metrics.ascent;
		final int after = metrics.descent + metrics.leading;
		float y = top;
		
		
		for (String zeile : ergList) {
			y += before;
			canvas.drawText(zeile, left, y, paint);
			y += after;
			
		}
	}
}

GitHub

Das ganze Beispiel findet ihr natürlich auch auf GitHub.

 

Die Quellen

 

Copyright © 2015 AxxG – Alexander Gräsel



Keine Antworten : “Android Canvas: draw multiline text in a rectangle”

Trackbacks/Pingbacks

  1. HTML5 Canvas: draw multiline text in a rectangle | AxxG Blog - […] Kennt ihr das? Vor ca 2. Wochen brauchte ich ein Skript, was mir in Java bzw. Android diverse Texte…

Kommentar verfassen