I am new to android watch face development and facing some problem with alignment of time text! I am displaying time in two format, How can I display it one at top and one at bottom and center aligned as shown in image below? Is there any pattern or layout for this?
It's up to you to figure out the alignment yourself when calling Canvas.drawText in your onDraw handler. I use code something like this:
String msg = "Text to display";
float width = paint.measureText(msg);
canvas.drawText(msg, centerX - width / 2f, top, paint);
where paint, top, and centerX were all initialized previously. The first two will depend on your face design, while the third is a Point field that can be set in your onCreate handler like so:
Display display = ((WindowManager) getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
display.getSize(displaySize);
centerX = displaySize.x / 2f;
Alternately, you could do all of this in XML with a layout-based watch face, but this may or may not be a good fit for your needs.
Related
I'm trying to create the control, that allows moving view with the finger. To do this, I follow recommendations from this post.
But presented method needs some modification, to prevent my view from being moved beyond the screen. I found out how to get maxY and maxY coordinates - for my Samsung Galaxy A6 it's 1080x1920. But the problem is, that my maxY is deep beyond the visible bottom edge of the device.
So my control almost disappears, when reaches Y about 1650. What 300 more pixels go for. I can suppose, that this is NavigationBar height + my control view height, but this also doesn't place my control as expected.
I define max coordinates with this method.
private void setMaxCoordinates(int viewWidth, int viewHeight) {
Display display = getWindowManager().getDefaultDisplay();
Point displaySize = new Point();
display.getSize(displaySize);
maxX = displaySize.x - viewWidth;
maxY = displaySize.y - viewHeight;
}
Please, help me to define the correct formula to detect bottom edge coordinate.
I've found a solution. First of all I was wrong with getting bottom coordinates with the help of WindowManager. This just gives you height of your screen in pixels, that is not related to the container. So to detect your bottom coordinate this way, you have to consider:
StatusBar height.
NavigationBar height.
Height of all views that lokated higher, then your container.
Height of your own control view (ImageView in my case).
So formula will look like this.
maxY = windowHeight - (statusBarHeight + navBarheight + allUpperViewsHeight + yourViewHeight)
The correct way to define the bootom of your container, is to get the container's height and deduct your control height.
maxY = containerHeight - yourViewHeight;
I have looked all over, but nothing has worked. Every time I try finding the center of the screen for my watch face, it always end up slightly higher than the center, and to the far right of the screen (This is different than the problem that the original template from Android Studio has, where the Watch face is slightly up and all the way to the far left of the screen). What could I be doing wrong?
(First screenshot on Left: My problem. Second screenshot: Original form of the Android Wear template from Android Studio)
#Override
public void onDraw(Canvas canvas, Rect bounds) {
if (isInAmbientMode()) {canvas.drawColor(Color.BLACK);
}
else {
canvas.drawRect(0, 0, bounds.width(), bounds.height(), mBackgroundPaint);
}
// Draw H:MM in ambient mode or H:MM:SS in interactive mode.
long now = System.currentTimeMillis();
mCalendar.setTimeInMillis(now);
int width = bounds.width();
int height = bounds.height();
float centerX = width / 2f;
float centerY = height / 2f;
String text = mAmbient
? String.format("%d:%02d", mCalendar.get(Calendar.HOUR),
mCalendar.get(Calendar.MINUTE))
: String.format("%d:%02d", mCalendar.get(Calendar.HOUR),
mCalendar.get(Calendar.MINUTE));
canvas.drawText(text, centerX, centerY, mTextPaint);
}
Full code can be found here
As far as i can tell from a quick look on your sources you already calculate mTextXOffset and mTextYOffset but don't use it for your draw call, hence the texts origin is centered in your problematic screenshot instead of the texts center.
adjusting your centerX and centerY by mTextXOffset and mTextYOffset should do the trick.
If you're drawing text using a center point you can use
mTextPaint.setTextAlign(Paint.Align.CENTER);
which will draw from the center. Otherwise the x coordinte represents the left hand side of the text you are drawing.
Here is the situation. I am rendering to a canvas. Nothing else is being rendered, the canvas is effectively fullscreen. There is only one view.
I want to make a simple (text) button. This consists of two parts: visually drawing it, and checking bounds to see if it is pressed. These two things should be consistent.
Ideally the centering is optional, though I would expect switching it to be simple.
After far too many hours, I suspect it is not simply an alignment issue, but also a use of the API that is the source of the problem.
How can this be done?
Just use static layouts and then you can keep the text center aligned inside the canvas even if the size is increased by some limit by auto aligning the text.
Rect bounds = new Rect(x1, y1, x2, y2);// set the bounds
String textOnCanvas = "text to be wriiten";
StaticLayout sl = new StaticLayout(textOnCanvas, textPaint,
bounds.width(), Layout.Alignment.ALIGN_CENTER, 1, 1, true);
canvas.save();
float textHeight = getTextHeight(textOnCanvas, textPaint);
int numberOfTextLines = sl.getLineCount();
float textYCoordinate = bounds.exactCenterY() -
((numberOfTextLines * textHeight) / 2);
//text will be drawn from left
float textXCoordinate = bounds.left;
canvas.translate(textXCoordinate, textYCoordinate);
//draws static layout on canvas
sl.draw(canvas);
canvas.restore();
The parameter Layout.Alignment.ALIGN_CENTER of static layout will take care of the center alignment of the text in canvas.
I am currently working on rendering a Bitmap, that I then want to send to a mobile printer. However, I am struggling with measuring the height of my text, so I can advance the y position appropriately.
My basic bitmap/canvas/paint configuration is this (Font Size is 16 and the dimensions of the bitmap are 200x400 (width x height):
public MyRenderer() {
// Initialize bitmap
bitmap = Bitmap.createBitmap(200, 400, Bitmap.Config.ARGB_8888);
// Initialize canvas
canvas = new Canvas(bitmap);
// Initialize brush (Paint instance)
brush = new Paint();
brush.setTextSize(16);
brush.setTypeface(Typeface.SANS_SERIF);
brush.setColor(Color.BLACK);
brush.setStyle(Paint.Style.FILL);
brush.setAntiAlias(true);
brush.setTextAlign(Align.LEFT);
}
So far so good, now what I want to do is: If I use the Paint's method drawText I need to supply the x and y coordinates. As for x that's zero (assuming left aligned text) but as for y, I'd have to calculate the height of each text I print and add it up, so I can keep track of my current y position.
And this is where it gets odd: I am using the following method to determine the height of a text (using the Paint objected that I initialized previously - it's called "brush"):
public int measureHeight(String text) {
Rect result = new Rect();
// Measure the text rectangle to get the height
brush.getTextBounds(text, 0, text.length(), result);
return result.height();
}
The above method returns the following values for the following texts:
"Hello World" returns a height of 12
"A camera instance can be used to compute 3D transformations and generate a matrix." returns a height of 16
"Introducing Android Design: The place to learn about principles, building blocks, and patterns for creating world-class Android user interfaces. Whether you're a UI professional or a developer playing that role, these docs show you how to make good design decisions, big and small." returns a height of 16
It makes sense to me, that number 2 and 3 return a greater height than number 1 but if one line has a height of 12 (as number one does) - it makes no sense, that multiple lines have a height of 16 ?
Am I missing something here? There is a convenience method for measuring the width of a text (using an instance of paint and call measureText("myText") which works perfectly, however I am quite at a loss, when it comes to the height, as the above given results don't make any sense to me.
EDIT
I am aware, that getTextBounds probably does no auto-wrapping of multi-lined text, and that's ok, I already wrote a method for splitting text, but even if it just measures one line, the above given length values still seem unlikely.
I think it is because the "p" in "compute" extends below the baseline whereas "Hello World" only contains letters that are above the baseline.
Since the line distance should not depend on what specific letters your text happens to consist of you are probably looking for Paint.FontMetrics which can be obtained via Paint.getFontMetrics(). Compute descent - ascent + leading to get the recommended baseline distance (because ascent has a negative value).
There is a small error in the accepted answer. If you want the text height, you should use
Paint.FontMetrics fm = mTextPaint.getFontMetrics();
float textHeight = fm.descent - fm.ascent;
And if you want the line height, you should use
float lineHeight = fm.bottom - fm.top + fm.leading;
Leading is optional interline spacing, so if you need to get the line hight you can include it. But if you just want the text height, then you can leave it off.
Note
I've never actually seen leading be anything else than 0, and as far as I can tell it even seems to be ignored in the TextView source code (and its associated Layout, StaticLayout, etc.). Please correct me if I'm wrong. So it is probably safe it leave it out of line hight calulations, but I'm not completely sure about that.
See also
Getting text height from getTextBounds vs FontMetrics vs StaticLayout
Meaning of top, ascent, baseline, descent, bottom, and leading in Android's FontMetrics
I am programming an app that shows a lot of verses/poems so text wrapping is not an option for me. I would like the text to be as big as possible (doesn't have to recalculate each time a new text is shown, should just allow the biggest text to fit on the screen) without extending screen size. It should not visually scale or take longer for the text to appear.
Is this possible?
Thanks
I would suggest a simple search for the best point size using the largest text that you need to fit. This can be done once at start-up. (Well, maybe twice—once for landscape and once for portrait). The first step would be to initialize a Paint with the typeface you want to use for display. Then call this function to
public void setBestTextSize(String longestText, int targetWidth, Paint paint) {
float size = paint.getTextSize(); // initial size
float w = paint.meaasureText(longestText);
size = targetWidth * size / w;
paint.setTextSize(size);
// test if we overshot
w = paint.measureText(longestText);
while (w > targetWidth) {
--size;
paint.setTextSize(size);
w = paint.measureText(longestText);
}
A binary search in the loop might be theoretically faster, but this should do pretty well since text width does scale approximately linearly with font size and the first step before the loop should get the size pretty close.
An alternative approach, which deals nicely with view size changes, is shown in this thread.