It is a stupid question, probably...
However, what is the unit of measure of the measureText() value returned?
I need the width of a text in px.
I thought px, dp, sp would be integer values. So why measureText() in TextPaint returns a float value?
Why would the return type not be a float, representhing pixels?
Drawing on screen uses floats all the way down to the GPU level. This makes calculations more convenient and makes it possible to use antialiasing and subpixel rendering.
Dimensions such as px, dp, sp are floats too, though there are convenience methods to get an integer pixel size value.
as per the documentation.........
http://developer.android.com/reference/android/graphics/Paint.html#measureText(java.lang.String)
The returned value is the number of pixels (not dp or sp), thus is returned as you need it :) It is returned as a float probably due to the official Android classes finding a float value more useful for calculations and optimization, but I could be wrong on that.
Related
I'm using android studio to design a game.
I used the 'dp' unit in my 'xml' file for defining my elements.
In java code, I want to move those elements by function animate() like image_button_red1.animate().xBy(first value).yBy(second value);
this function only takes float value, But the animation is different in each device.
I want to use the 'dp' unit to solve this problem.
Is there a function that takes another unit like 'dp'?
I've found an answer.
I designed my layout in pixels and when I used, for example, animate().xby(10.0f), the translation was right in each device. The equation between the pixel and dp is: px = dp * (dpi / 160);
Then I used dp instead of px and used the function Resources.getSystem().getDisplayMetrics().density to get the density of each screen which is running the code.
So I used a float variable instead of 10.0f which this value calculated from the noted equation. My object size was 360 dp, and the equation changed to px = 2.25*dpi.
And in my case, I used a variable like this:
float House_size = 2.25f * 14.44f * Resources.getSystem().getDisplayMetrics().density;
then I used animate().xby(House_size);
And now the result works properly on every single device.
I started experimenting with Custom Gestures and the GestureOverlayView and noticed a few variables where I am not sure what they are for and what range of values can and should be assigned, the docs seem to be somewhat vague on those:
//Minimum curve angle a stroke must contain before it is recognized as a gesture.
android:gestureStrokeAngleThreshold
I assumed this is in degrees and when I add "25" here, a sharp edge must be contained in the gesture, but actually it still is detected ifI draw a circle or a perfect square.
//Minimum length of a stroke before it is recognized as a gesture.
android:gestureStrokeLengthThreshold
Is this in dp ? Because it seems like on smaller screens it is harder to trigger the gesture...
//Squareness threshold of a stroke before it is recognized as a gesture.
android:gestureStrokeSquarenessThreshold
what is this?
EDIT:
Ok I just realized that every prediction has a score value, which should be used to find out if the gesture performed actually meets the requirements, so I added a check if the prediction's score is greater than 1.
Still I am curious what those variables in GestureOverlayView are doing, so enlighten me :)
gestureStrokeLengthThreshold definitely is not density independent but apparently uses pixels. If you want to set a density independent threshold you can calculate the gestureStrokeLengthThreshold at runtime, like that:
DisplayMetrics metrics = getResources().getDisplayMetrics();
float normalizedScreenSize = (metrics.heightPixels + metrics.widthPixels) / 2.0f;
return normalizedScreenSize * GESTURE_LENGTH_THRESHOLD;
GESTURE_LENGTH_THRESHOLD would be a value representing how long the gesture should be. A value of 1.0 would roughly be the size of the screen (averaged from screen width and height)
Still I am interested in what those other variables in GestureOverlayView do, so if you know more - enlighten me :)
I have an app that currently needs to draw some circles on a surfaceview's canvas.
Everything works fine, however I'm no understanding how to scale the circles correctly on different devices.
What I mean by this is that on my Galaxy S4, the circles are an appropriate size.
When I load the same app on my wife's Galaxy S3, the circles are huge in comparison.
I'm sure this is due to screen resolution and density, but I'm struggling with how to take the screen resolution/density into account to scale the circles correctly depending on the phone size.
Example of how a circle is drawn in my app. crcl.radius is currently a fixed size of 100. I know this 100 needs to be scaled based on the device but I can't wrap my head around the formula to do so. I'm assuming the conversion is relatively simple.
Disclaimer: somewhat new to Android (about 6 months using it).
myCanvas.drawCircle(crcl.x, crcl.y, crcl.radius, selected_paint);
Thank you in advance for any help you can give.
You can use a dp value instead of pixel. I think this should do the trick.
myCanvas.drawCircle(crcl.x, crcl.y, dipToPixels(getApplicationContext(),crcl.radius), selected_paint);
This function converts dp to pixel.
public static float dipToPixels(Context context, float dipValue) {
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dipValue, metrics);
}
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 have been using the pt unit in XML files. Now I need to set the width of an element at runtime. How can I set the width by points so that it is the same unit I have been using everywhere else. I assume I will need to multiply by resolution and dpi. A code sample would be best.
First you should really read the following in-depth article from the Android Developer Documentation :
http://developer.android.com/guide/practices/screens_support.html
Right in the middle you'll find the following under the title :
Do not use hard-coded pixel values in your code
// Convert the dps to pixels
final float scale = getContext().getResources().getDisplayMetrics().density;
mGestureThreshold = (int) (GESTURE_THRESHOLD_DP * scale + 0.5f);
You can use dip instead of pt