Skip to content

Instantly share code, notes, and snippets.

@nutiteq
Last active October 11, 2015 03:57
Show Gist options
  • Save nutiteq/3798924 to your computer and use it in GitHub Desktop.
Save nutiteq/3798924 to your computer and use it in GitHub Desktop.
DefaultLabel.java
package com.nutiteq.ui;
import java.util.LinkedList;
import java.util.List;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Paint.Style;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.RectF;
import com.nutiteq.components.TextureInfo;
import com.nutiteq.style.LabelStyle;
/**
* Simple text-based map label containing title and optional description.
*/
public class DefaultLabel extends Label {
private static final LabelStyle DEFAULT_STYLE = LabelStyle.builder().build();
private final String title;
private final String description;
private final LabelStyle style;
private Paint paint = null;
private int titleHeight;
private int descriptionHeight;
private int descriptionWidth;
private int width;
private int height;
/**
* Default constructor.
*
* @param title
* label title.
*/
public DefaultLabel(String title) {
this(title, null);
}
/**
* Constructor for label with both title and description. Description will be aligned to
* the center of the label.
*
* @param title
* label title.
* @param description
* label description. Description supports \n separator for multiline texts.
*/
public DefaultLabel(String title, String description) {
this(title, description, DEFAULT_STYLE);
}
/**
* Constructor for label with both title and description and description alignment style.
*
* @param title
* label title.
* @param description
* label description. Description supports \n separator for multiline texts.
* @param descAlign
* description alignment style.
*/
public DefaultLabel(String title, String description, Align descAlign) {
this(title, description, LabelStyle.builder().setDescriptionAlign(descAlign).build());
}
/**
* Constructor for label with title, description and custom label style.
*
* @param title
* label title.
* @param description
* label description. Description supports \n separator for multiline texts.
* @param style
* label style.
*/
public DefaultLabel(String title, String description, LabelStyle style) {
this.title = title;
this.description = description;
this.style = style;
}
/**
* Get label title.
*
* @return label title.
*/
public String getTitle() {
return title;
}
/**
* Get label description.
*
* @return label description.
*/
public String getDescription() {
return description;
}
private void initializePaint() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setTextAlign(style.descriptionAlign);
paint.setStyle(Style.FILL);
paint.setTypeface(style.titleFont);
paint.setTextSize(style.titleSize);
Rect titleSize = new Rect();
paint.getTextBounds(title, 0, title.length(), titleSize);
titleHeight = titleSize.height();
if (description != null && description.length()>0) {
paint.setTypeface(style.descriptionFont);
paint.setTextSize(style.descriptionSize);
String[] lines = wrapText(description, paint, style.descriptionWrapWidth, 0);
Rect lineSize = new Rect();
int descWidth = 0, descHeight = 0;
for (String line : lines) {
if (line.length() == 0) {
line = " "; // HACKFIX: getTextBounds does not work with empty strings
}
paint.getTextBounds(line, 0, line.length(), lineSize);
descHeight += lineSize.height();
descWidth = Math.max(descWidth, lineSize.width());
}
descriptionHeight = descHeight;
descriptionWidth = descWidth;
width = (int) Math.max(titleSize.width(), descriptionWidth) + 2 * style.edgePadding;
height = (int) titleSize.height() + descriptionHeight + 2 * style.edgePadding + lines.length * style.linePadding;
} else {
descriptionHeight = 0;
descriptionWidth = 0;
width = (int) titleSize.width() + 2 * style.edgePadding;
height = (int) titleSize.height() + 2 * style.edgePadding;
}
}
@Override
public TextureInfo drawMarkerLabel() {
if (paint == null) {
initializePaint();
}
TextureInfo textureInfo = getTextureInfo(width, height + Math.max(0, style.triangleSize - style.borderSize));
Canvas canvas = new Canvas(textureInfo.bitmap);
// Draw background bubble
paint.setColor(style.borderColor);
canvas.drawRoundRect(new RectF(0, 0, width, height), style.roundedOutside, style.roundedOutside, paint);
paint.setColor(style.backgroundColor);
canvas.drawRoundRect(new RectF(style.borderSize, style.borderSize, width - style.borderSize, height - style.borderSize),
style.roundedInside, style.roundedInside, paint);
// Draw triangle
Path triangleOutside = new Path();
triangleOutside.moveTo(0, 0);
triangleOutside.lineTo(style.triangleSize, 0);
triangleOutside.lineTo(style.triangleSize / 2, style.triangleSize);
triangleOutside.lineTo(0, 0);
float newBorderSize = (int) (style.borderSize * 1.4);
Path triangleInside = new Path();
triangleInside.moveTo(newBorderSize, 0);
triangleInside.lineTo(style.triangleSize - newBorderSize, 0);
triangleInside.lineTo(style.triangleSize / 2, style.triangleSize - newBorderSize * 1.5f);
triangleInside.lineTo(newBorderSize, 0);
canvas.translate(width / 2 - style.triangleSize / 2, height - style.borderSize);
paint.setColor(style.borderColor);
canvas.drawPath(triangleOutside, paint);
paint.setColor(style.backgroundColor);
canvas.drawPath(triangleInside, paint);
canvas.setMatrix(null);
// Draw text
paint.setColor(style.titleColor);
paint.setTypeface(style.titleFont);
paint.setTextSize(style.titleSize);
Align align = paint.getTextAlign();
paint.setTextAlign(style.titleAlign);
canvas.drawText(title, width / 2, style.edgePadding + titleHeight, paint);
paint.setTextAlign(align);
if (description != null) {
paint.setColor(style.descriptionColor);
paint.setTypeface(style.descriptionFont);
paint.setTextSize(style.descriptionSize);
int x = style.edgePadding;
if (Align.CENTER.equals(paint.getTextAlign())) {
x = width / 2;
} else if (Align.RIGHT.equals(paint.getTextAlign())) {
x = width - style.edgePadding;
}
drawString(canvas, paint, description, x, style.edgePadding + titleHeight);
}
return textureInfo;
}
@Override
public float getMarkerLabelAlpha() {
return style.alpha;
}
private void drawString(Canvas canvas, Paint paint, String str, int x, int y) {
Rect bounds = new Rect();
String[] lines = wrapText(str, paint, style.descriptionWrapWidth, 0);
int yoff = 0;
for (String line : lines) {
if (line.length() == 0) {
line = " "; // HACKFIX: getTextBounds does not work with empty strings
}
paint.getTextBounds(line, 0, line.length(), bounds);
yoff += bounds.height() + style.linePadding;
canvas.drawText(line, x, y + yoff, paint);
}
}
private static String[] wrapText(String text, Paint paint, int wrapWidth, int currentX) {
// wrapped text lines
final List<String> wt = new LinkedList<String>();
final int len = text.length();
if (len == 0) {
return new String[0];
}
int first = 0;
int last = 0;
boolean emptylineflag = false;
for (int i = 0; i < len; i++) {
// split at '\n'
if (text.charAt(i) == '\n') {
wt.add(text.substring(first, i));
first = i + 1;
last = i;
emptylineflag = false;
continue;
}
// update last if necessary
if (text.charAt(i) == ' ') {
last = i;
// don't check to split - we would have splitted at the previous char
continue;
}
// check that wrapping is switched on
if (wrapWidth < 0) {
continue;
}
// check length, see if we should split
float w = paint.measureText(text, first, i + 1);
// we should not split if we printed nothing on the previous line
if ((first == 0 && w + currentX > wrapWidth) || (first != 0 && w > wrapWidth)) {
// yep, we should split, first to last, then the rest
// if last is equal to first, the first word doesn't fit, so add an
// empty string
// but set a flag to avoid adding it again
if (first < last || (first == last && !emptylineflag)) {
String s = text.substring(first, last);
wt.add(s);
}
if (first < last) {
// start with last+1
emptylineflag = false;
first = last + 1;
} else {
emptylineflag = true;
}
}
}
// add the last string
wt.add(text.substring(first));
return wt.toArray(new String[wt.size()]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment