I write a book reader. For the correctly display text on my pages, I need to know, How many characters can be on display ?
TextPaint mTextPaint=new TextPaint();
mTextPaint.setTextSize(16);
StaticLayout mTextLayout = new StaticLayout(newText, mTextPaint, canvas.getWidth() - 20, Alignment.ALIGN_NORMAL, 1.5f, 0.0f, false);
canvas.save();
int x = 10, y = 50;
y += p.ascent() + p.descent();
canvas.translate(x, y);
mTextLayout.draw(canvas);
canvas.restore();
First you need to get last visible line. You can get it with Layout.getLineForVertical method.
After that you can get text offset for line with methods Layout.getLineEnd/Layout.getLineVisibleEnd
Example:
int line = mTextLayout.getLineForVertical(canvas.getHeight()); // position of last visible line
int chars = mTextLayout.getLineEnd(line);
Related
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 using the onTouchEvent() method with the following case:
case MotionEvent.ACTION_MOVE:
if (motionEvent.getHistorySize() > 0) {
for (int i = 1, n = motionEvent.getHistorySize(); i < n; i++) {
int calcX = (int) motionEvent.getHistoricalX(i) - (int) motionEvent.getHistoricalX(i - 1);
treesX += calcX;
}
}
break;
The treesX variable represents the point at which the background is drawn as a bitmap, and another background is drawn next to it with the screen width added to treesX, and after the screen width has been scrolled both images jump one screen width back to give the effect of a seamless background, as the following code shows:
public void draw() {
if (ourHolder.getSurface().isValid()) {
canvas = ourHolder.lockCanvas();
if ((int) treesX < -screenWidth) {
treesX += screenWidth;
}
if ((int) treesX > 0) {
treesX -= screenWidth;
}
canvas.drawBitmap(trees, null, new Rect((int) treesX, 0, screenWidth + (int) treesX, screenHeight), paint);
canvas.drawBitmap(trees, null, new Rect((int) treesX + screenWidth, 0, 2 * screenWidth + (int) treesX, screenHeight), paint);
}
ourHolder.unlockCanvasAndPost(canvas);
}
This means that the two backgrounds should always be right next to each other, however when I scroll the screen, they come apart slightly or overlap slightly, is there any way I can rectify this? Any part of the code that is causing them to be out of sync?
When the user drags, you will receive many MotionEvents with ACTION_MOVE. Each event will have a larger history size than the last. This means your for loop will take longer to execute the more the user drags or the longer they keep their finger down.
There's really no need for you to loop over all the historical values just to figure out the current distance that has been dragged. Simply save the X value of the initial ACTION_DOWN, and then on each ACTION_MOVE you compare the most recent X value against the saved one.
I have created a watermark text that is vertically centered.
This is the line I used
PdfPatternPainter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
string, x, y, -90);
What i want to do now is to make the watermark diagonal. changing the angle value will make it diagonal, but then its x,y position is not centered anymore.
this is my current method
public static void createWaterMarkPDF(ArrayList<String> watermark, PdfReader reader, PdfStamper stamper) throws Exception {
Rectangle pageSize = reader.getPageSize(1);
final float WATERMARK_PAGE_ANGLE = 270;
BaseFont font = BaseFont.createFont(BaseFont.HELVETICA, BaseFont.WINANSI, BaseFont.NOT_EMBEDDED);
PdfPatternPainter painter = stamper.getOverContent(1).createPattern(pageSize.getWidth(), pageSize.getHeight());
painter.setColorStroke(new BaseColor(192, 192, 192));
int FONT_SIZE = 80;
painter.beginText();
painter.setTextRenderingMode(PdfPatternPainter.TEXT_RENDER_MODE_FILL);
painter.setFontAndSize(font, FONT_SIZE);
for (int i=0; i<watermark.size(); i++) {
String string = watermark.get(i);
// Values are opposite since we are doing a vertical alignment angle
float width = font.getAscentPoint(string, FONT_SIZE) + font.getDescentPoint(string, FONT_SIZE);
float height = painter.getEffectiveStringWidth(string, true);
float x = (pageSize.getWidth() - width) / 2;
if (i == 0)
x += (width * 3);
else if (i == 2)
x -= (width * 3);
painter.showTextAlignedKerned(Element.ALIGN_MIDDLE,
string, x,
Util.transformY(pageSize, (pageSize.getHeight() - height) / 2),
WATERMARK_PAGE_ANGLE);
}
painter.endText();
// Transparency of watermark
PdfGState state = new PdfGState();
state.setFillOpacity(0.1f);
for (int i=reader.getNumberOfPages(); i>0; i--) {
Rectangle thisPageSize = reader.getPageSize(i);
PdfContentByte overContent = stamper.getOverContent(i);
overContent.setColorFill(new PatternColor(painter));
overContent.setGState(state);
overContent.rectangle(thisPageSize.getLeft(), thisPageSize.getBottom(), thisPageSize.getWidth(), thisPageSize.getHeight());
overContent.fill();
}
}
it draws vertical lines and centered. watermark is an ArrayList. you can add 3 strings.
if i change the angle to , say 300 (or -60) to make it diagonal, the positioning is whacked. do i need to use an AffineTransform here? although i already tried, didnt work for me unless i lacked something here?
I'm trying to display the text on custom ImageView using canvas.
This is my TextPaint:
mPaint = new TextPaint();
mPaint.setDither(true);
mPaint.setColor(0xFFFFFFFF);
mPaint.setTextAlign(Paint.Align.CENTER);
mPaint.setTypeface(tf);
mPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
mPaint.setTextSize(mSize);
User enters his text in EditText which is converted into String(sText) and then into SpannableString(ssText). That SpannableString is being displayed on the canvas.
This is my onDraw:
int x = this.getWidth() / 2, y = 10;
Editable.Factory fac = Editable.Factory.getInstance();
Editable edit = fac.newEditable(ssText);
DynamicLayout layout = new DynamicLayout(edit, mPaint, (x * 2), Alignment.ALIGN_NORMAL, 1, 0, false);
canvas.translate(x, y);
layout.draw(canvas);
canvas.restore();
Now, I'm taking some strings(mText) as input (one string in one line) from user in EditText of which I have to change color in the already displayed SpannableString(ssText) on the canvas.
for (String line : mText.split("\n")) { // one string one line
if (sText.matches(".*\\b" + line + "\\b.*")) {
int len = line.length();
// get the starting index of String line in sText
for (int i = -1; (i = sText.indexOf(mText, i + 1)) != -1;) {
// change the color of required text in ssText
ssText.setSpan(new ForegroundColorSpan(Color.rgb(r_deep, g_deep, b_deep)), i, (i + len), 0);
}
}
}
After changing the color of ssText as above I again call onDraw on the click of ChangeColor button to diaplay it back on the canvas. But, here what I see is that the position of the ssText on canvas is irregular.
I see this before the click of ChangeColor button:
And this is after click of ChangeColor button:
If the problem is in translate of canvas the I'm also using canvas.restore();
I don't understand what the problem is. Please help!
I am following the answer here: How get a string width in Libgdx?
The problem I am having is that the getBounds doesn't seem to return to correct width. My code below should place dots after the end of the string, but you can see dots bleeding through some of the text. How do I get correct width of the text?
for (HighScore s : this.scoresData){
font.draw(batch, s.name, x, y);
TextBounds nameBounds = font.getBounds(s.name);
font.draw(batch, s.value, (float) (KineticAssaultMain.WIDTH / 1.5f), y);
for (float i = (nameBounds.width + x); i < ((float)Screen.WIDTH / 1.5f);){
TextBounds dot = font.draw(batch, ".", i, y);
i += dot.width;
}
// go to the next line
y -= 32;
}
The TextBounds returned from getBounds isn't guaranteed to be valid after calling another BitmapFont method. The TextBounds instance is reused internally to avoid allocation. It's an unfortunate gotcha but is a result of libgdx avoiding GC.