I'm doing an Android Game, and I'm using a function like this to show texts on the device screen:
public void drawString(String text, int x, int y, Paint paint) {
canvas.drawText(text, x, y, paint);
}
And I try to show the following message:
g.drawString("Player: " + playerString+ " :\n" + messageString,SCREENWIDTH / 2, SCREENHEIGHT / 2, paint);
However instead of a newline (\n) I get a strange character (a square).
Anyone can help me?
Thanks
Instead of drawString call drawText
and for break lines call drawText twice with Y offset.
look here for example
Draw multi-line text to Canvas
public void drawString(Canvas canvas, String text, int x, int y, TextPaint paint) {
if (text.contains("\n")) {
String[] texts = text.split("\n");
for (String txt : texts) {
canvas.drawText(txt, x, y, paint);
y += paint.getTextSize();
}
} else {
canvas.drawText(text, x, y, paint);
}
}
Related
I'm trying to write a fill rectangle function for my project. And I was wondering how the fillRect(int, int, int, int) method of Java's java.awt.Graphics class is implemented. I tried finding the source code by right clicking in a Java program and "Open Declaration" (in the eclipse IDE) but all I can find is the abstract declaration of the method.
Basically I want to know how a simple Java code like this:
protected void paintComponent(Graphics g) {
g.setColor(Color.GREEN);
g.fillRect(10, 10, 620, 460);
}
can produce a green rectangle like the on in the window below:
I only know of two shape filling methods : the Flood-fill & Boundary-fill algorithms .
The issue with those is that they both needs existing boundaries (monochrome for Boundary-fill and mono- or polychrome for Flood-fill).
so that with the boundary-fill algorithm it would be something like :
void fillRect(int x, int y, int w, int h){
drawLine(x, y, x+w, y); // Draw top boundary
drawLine(x, y+h, x+w, y+h); // Draw bottom boundary
drawLine(x, y, x, y+h); // Draw left boundary
drawLine(x+w, y, x+w, y+h); // Draw right boundary
boundaryFill4D(x+1, y+1, GREEN, GREEN); // for simplicity I'm hardcoding color
}
void boundaryFill4D(int x, int y, int fill_color, int boundary_color)
{
if(getpixel(x, y) != boundary_color &&
getpixel(x, y) != fill_color)
{
putpixel(x, y, fill_color);
boundaryFill4D(x + 1, y, fill_color, boundary_color);
boundaryFill4D(x, y + 1, fill_color, boundary_color);
boundaryFill4D(x - 1, y, fill_color, boundary_color);
boundaryFill4D(x, y - 1, fill_color, boundary_color);
}
}
As you can see from this example I had to first draw the 4 sides before I could "bucket fill" the the outlined rectangle. I was wondering if there is a better way to do this.
I'm making a 2D card game. In it I have a custom font uses drawString from the Graphics library to draw it in with custom color and size. Problem is that this seems incredibly unoptimized. I can't seem to find out why. With 5 cards on screen, each one using this method 4 times each, I got from 3,800 fps down to 350 fps. Here's the way I draw text:
public static void drawString(Graphics g, String text, int xPos, int yPos, boolean center, Color c, Font font) {
g.setColor(c);
g.setFont(font);
int x = xPos;
int y = yPos;
if (center) {
FontMetrics fm = g.getFontMetrics(font);
x = xPos - fm.stringWidth(text) / 2;
y = (yPos - fm.getHeight() / 2) + fm.getAscent();
}
g.drawString(text, x, y);
}
After a bit of googling here's how I did it. This image is created when a Card UI element is added to the screen. Then is used to just overlay on the card's image. The x and y scaling is done in a separate class. The code mentioned in the original comment is in the Text class, along with the other drawRestrictedText method:
public BufferedImage createTextOverlayImage(){
BufferedImage overlay = new BufferedImage(imageWidth, height, BufferedImage.TYPE_INT_ARGB);
Graphics g = overlay.createGraphics();
Text.drawString(g, name,
(imageWidth / 2) + wd.getScaleX(18),
wd.getScaleY(21),
true,
Color.black, Assets.font20Bold);
Text.drawString(g, cost + "",
wd.getScaleX(28),
wd.getScaleY(28),
true,
Color.blue, Assets.font28Bold);
Text.drawRestrictedString(g, keyWords,
wd.getScaleX(20),
wd.getScaleY(162),
imageWidth,
Color.magenta,
Assets.font18Bold);
Text.drawRestrictedString(g, description,
wd.getScaleX(20),
wd.getScaleY(185),
imageWidth - wd.getScaleX(35),
Color.black,
Assets.font18);
return overlay;
}
Here's the site I used for reference: http://www.java2s.com/Tutorials/Java/Graphics_How_to/Text/Write_text_onto_image.htm
I have coordinates of rectangle to draw and I want to centre some text inside this rectangle.
int x, y, width, height;
String str = "This is a text";
x = 15;
y = 15;
width = 20;
heights = 30;
g.drawRect(x, y, width, height);
g.drawString(str, x + width/2, y + height/2);
If you want to center the text then you need to know the length of the text so you know its width relative to the width of the rectangle. This is done by getting the FontMetrics instance from the Graphics object.
So the basic code would be:
FontMetrics fm = g.getFontMetrics();
int stringWidth = fm.getStringWidth(...);
int xDiff = (width - stringWidth) / 2;
g.drawString(str, x + xDiff, ...);
Of course you will also need to center based on the height.
Doc says...
public abstract void drawString(String theString,
int x,
int y)
Renders the text of the specified iterator applying its attributes in accordance with the specification of the TextAttribute class.
The baseline of the leftmost character is at position (x, y) in this graphics context's coordinate system.
So... you are drawing your string starting at the center point of the rect but not the string.
https://docs.oracle.com/javase/7/docs/api/java/awt/FontMetrics.html - get the font metrics from the gc and figure out how wide the string is. Then subtract half of that from the start x. Do something similar for y (remember that height doesn't start at base.)
I am trying to achieve a count badge-like effect in a text view with a replacement span. I am using a drawable (circle defined in xml) as background as for some reason the canvas.drawCircle method did not seem to work. I want to draw the text on top of this drawable, however, no matter what I try, it never appears. Does anybody have any idea what the problem could be?
My custom replacement span class:
public class CircleBackgroundSpan extends ReplacementSpan {
private Drawable circle;
public CircleBackgroundSpan(Drawable drawable)
{
circle = drawable;
}
#Override
public int getSize(Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) {
int textWidth = Math.round(measureText(paint, text, start, end));
return Math.max(textWidth, circle.getIntrinsicWidth());
}
#Override
public void draw(Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, Paint paint) {
Rect bounds = new Rect();
circle.setBounds((int) x, top, (int) x + circle.getIntrinsicWidth(), top + circle.getIntrinsicHeight());
circle.draw(canvas);
paint.setColor(0x0000A8);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(16);
paint.getTextBounds(text.toString(), 0, text.subSequence(start, end).length(), bounds);
canvas.drawText(text, start, end, x + circle.getIntrinsicWidth()/2f , y, paint);
}
private float measureText(Paint paint, CharSequence text, int start, int end)
{
return paint.measureText(text, start, end);
}
}
Usage:
SpannableString spannableString = new SpannableString(selected+" items selected");
String numberString = String.valueOf(selected);
spannableString.setSpan(new CircleBackgroundSpan(getResources().getDrawable(R.drawable.multiselect_circle)), 0, numberString.length(), Spannable.SPAN_INCLUSIVE_INCLUSIVE);
multiselect.setText(spannableString, TextView.BufferType.SPANNABLE);
And the result:
I am trying to get something like this:
Any ideas?
Turns out that the problem was with setting the paint colour. After I changed paint.setColor(0x0000A8) to paint.setColor(Color.parseColor("#0000A8"), the text was shown.
I'm trying to create a text view (black colour) containing numbers 1 to 7 (each number on top and in the centre of each grey rectangle - just like the image I've drawn below) but I'm not sure what properties I need to add in order to achieve this. I believe the code needs to go in the loop section but I don't what code. What can be done so that a number appears centralised in each grey rectangle?
desired outcome
current outcome
public class RectangleTextView extends View {
private final Paint mBackPaint = new Paint();
private final Paint mRedPaint = new Paint();
private int mSideRectWidth = 10;
public RectangleTextView(Context context, AttributeSet attrs) {
super(context, attrs);
mBackPaint.setColor(Color.BLACK);
mRedPaint.setColor(Color.RED);
}
#Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (getWidth() == 0)
return;
//draw grey boxes
setBackgroundColor(Color.parseColor("#808080"));
int boxWidth = getWidth() / 7;
//draw black lines and/or draw text in centre of each rectangle
for (int i = 0; i < 7; i++) {
canvas.drawLine(mSideRectWidth + boxWidth * i, 0, mSideRectWidth + boxWidth * i, getHeight(), mBackPaint);
canvas.drawText(?);
}
//draw text in centre of each rectangle
?
//draw left end rectangle
canvas.drawRect(0, 0, mSideRectWidth, getHeight(), mRedPaint);
//draw right end rectangle
canvas.drawRect(getWidth() - mSideRectWidth, 0, getWidth(), getHeight(), mRedPaint);
}
}
Use canvas.drawText(). You can do it in the same loop that you draw the black lines.
for (int i = 0; i < 7; i++) {
canvas.drawLine(mSideRectWidth + boxWidth * i, 0, mSideRectWidth + boxWidth * i, getHeight(), mBackPaint);
float x = ...
float y = ...
canvas.drawText(Integer.toString(i + 1), x, y, mBlackPaint);
}
You will have to figure out the x and y value for the placement of the text. If you use paint.setTextAlign(Align.CENTER), then that simplifies the x value calculation, it's just halfway between the black lines.
Use the Paint.getTextBounds() method to get the bounding area of the text, then the exact center vertically and horizontally can be extracted from the resulting Rect object:
Paint paint = new Paint();
paint.setTextSize( textSize );
Rect bounds = new Rect();
if( text != null )
paint.getTextBounds(text, 0, text.length(), bounds);
else
bounds.set(0,0,0,0);
location.x -= bounds.exactCenterX();
location.y -= bounds.exactCenterY();