I can't find anywhere there is a mention of how the getTextSize() in Textview is measured. From visual tests it doesn't seem to be including descenders, but seems to include ascenders. It doesn't appear to start exactly from the baseline also.
http://en.wikipedia.org/wiki/Descender
This is the closest mention of it but Romain Guy from Google just ignores that part of the question.
http://www.mail-archive.com/android-developers#googlegroups.com/msg08514.html
As I need this as I am using Compound Drawables and I need to be able to align the drawable to the text on different devices.
Here is code I used to test on a compound drawable a circle that touches edges
tvData.setTextSize(TypedValue.COMPLEX_UNIT_PX, 100);
tvData.setText("agB5ãÂ");
int size = (int)tvData.getTextSize();
Drawable img = getResources().getDrawable(R.drawable.circle_white_full_72 ).mutate();
img.setBounds( 0 , 0 , size , size);
tvData.setCompoundDrawables( null, null, img, null );
Here is the result
as you can see it doesn't seem to use the descenders and ascenders.
Here is the drawable image, if others want to test
http://i.imgur.com/Yhf8b.png
When changing the image to 80% of the text size using
int size = (int)tvData.getTextSize() *80/100;
Here is the result, with the image transposed on top of the 100% image. Maybe setCompoundrawables is doing it's own scaling
I tried measuring midpoints of the font and drawable and it is off. Here is an image highlighting it
Finally I moved the drawable 50pixels to the left, and then measured the output and it was half the height of the font text baseline to baseline, as the setTextSize was set at 100px.
Android must be using some other layout to scale and position the compound drawable. Maybe I should create another question for this.
Here is an image highlighting baseline to baseline.
From some light testing, it appears to be from ascent to descent (text size = descent - ascent). Did some debugging with a TextPaint to verify, and just to be a little more specific, I did:
Paint.FontMetricsInt metrics;
for(int i = 1; i < 100; i++) {
mTextPaint.setTextSize(i);
metrics = mTextPaint.getFontMetricsInt();
if((metrics.descent - metrics.ascent) != i) Log.v("type", "Not equal");
}
And it remained true for each value.
Related
I would like to be able to set the text in a TextView depending on the available space, in order to avoid ellipsizing.
For example:
if there is enough space set the text "The red fox jumps"
if there is not enough space (and consequently "The red fox jumps" would be ellipsized) set the text "jumps"
Please how can I achive that?
You could use Paint.measureText(String) to determine the width of the whole string, when drawn with your Paint object. If that value is greater than the TextView's width, then we know that the text will be ellipsised.
float totalLength = myPaint.measureText("The red fox jumps");
float tvWidth = myTextView.getWidth(); // get current width of TextView
if (tvWidth < totalLength) {
// TextView will display text with an ellipsis
}
Once we know the text will be truncated, we can use trial and error to determine what the minimum text that can be displayed on screen is. This step will depend on your business logic, but should use the same Paint calculations as the first step.
calculateStringWidth("The red fox jumps"); // too large
calculateStringWidth("red fox jumps"); // still too large
calculateStringWidth("fox jumps"); // width is less than TextView, will fit without ellipsis
One method would be to calculate the neede size for given text.
textView.setText("The red fox jumps");
// call measure is important here
textView.measure(0, 0);
int height = textView.getMeasuredHeight();
int width = textView.getMeasuredWidth();
if (height > availableHeight || width > availableWidth) {
textView.setText("jumps");
}
The call of measure() "determines the size requirements for this view and all of its children". Referring to Androids View doc. Documentation
I'm using the following method which programatically creates a linearlayout and populates it with two textviews, adds text and then turns it into a drawable that I later use a layer above a shape. However, I've noticed that I can't create a small fontsize - it seems stuck at a minimum size which is relatively.. large, and anything that I specify below that value just seems to make it look increasingly blurry (but still the same size). What could be the reason for this?
This behavior occurs whether or not I used TypedValue.COMPLEX_UNIT_SP.
Edit: This size stays the same even if I specify something ridiculous like:
.setTextSize(TypedValue.COMPLEX_UNIT_SP, 60);
it doesn't get any bigger - it just gets "sharper".
Edit 2: If I specify the top textview as having a very large size, then the smaller I set the second textview, the smaller it becomes - as a ratio (for example, if I set the top at 100 and the bottom at 50 it looks exactly the same as the top at 10 and the bottom at 5). However, in no way can I reduce the size of the top textview.
Edit 3: If I remove one of the textviews, and leave only the other one as a single textview in the layout - I can't change the size at all. I can only make it more or less blurry depending how low I set the number but it will always appear the exact same size on screen.
private Drawable createTextLayer() {
LinearLayout newLinearLayout = new LinearLayout(getContext());
newLinearLayout.setOrientation(LinearLayout.VERTICAL);
newLinearLayout.setGravity(Gravity.CENTER);
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
newLinearLayout.setBackgroundColor(getColor(R.color.somecolor));
newLinearLayout.setLayoutParams(layoutParams);
TextView headlinetv = new TextView(getContext());
TextView bodytv = new TextView(getContext());
headlinetv.setText(headlineText);
headlinetv.setTextSize(7);
headlinetv.setGravity(Gravity.CENTER);
bodytv.setText(bodyText);
bodytv.setTextSize(6);
bodytv.setGravity(Gravity.CENTER);
newLinearLayout.addView(headlinetv);
newLinearLayout.addView(bodytv);
newLinearLayout.setDrawingCacheEnabled(true);
newLinearLayout.measure(MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
newLinearLayout.layout(0, 0, newLinearLayout.getMeasuredWidth(), newLinearLayout.getMeasuredHeight());
newLinearLayout.buildDrawingCache(true);
Bitmap b = Bitmap.createBitmap(newLinearLayout.getDrawingCache());
newLinearLayout.setDrawingCacheEnabled(false);
return new BitmapDrawable(getResources(), b);
}
Change your code to:
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 7);
Change the font size of 7 to any font size as you wish.
This turned out to be quite tricky and took me several hours to solve, but I'm posting the answer for anyone else who ends up in a similar situation.
The cause:
The entire drawable layer was the uppermost layer above a shape, and was therefore stretched to the size of the shape no matter what. In order to stretch it to the size of the shape, the largest view was stretched (essentially, the longest textview) and therefore couldn't be enlarged or reduced in size.
The solution:
I defined two more textviews, gave them the longest string by 1 character, and made the textcolor transparent. I would have preferred to give them blank space as content but android refused to measure a textview containing whitespace, so it had to be actual text. I put one textview at the top and one at the bottom and thus the visible text appears perfectly centered and in the correct font size.
Edit:
There turns out to be a better way of doing this. Define the textview as the same size as the shape on which it sits as a layer and define padding around the textview (play around with it to find the minimal amount of padding needed). If you have several textviews, divide the size of the shape by the number of textviews and give each one a fraction.
Hey I'm trying to write text to a bitmap, And i have no idea how to set the text size relative.
I've found some who set text size as DIP using the density, which i assume is OK
but if i try and write text on a smaller image text becomes huge.
This is the code i have :
private static final float GESTURE_THRESHOLD_DIP = 95.0f;
Resources resources = gContext.getResources();
float scale = resources.getDisplayMetrics().density;
paint.setTextSize((int) (GESTURE_THRESHOLD_DIP * scale + 0.5f));
How can i set text size to be relative to the paint size?
Well in the end i just checked what was the resolution of the images it looks good and calculated that height-width result divided to X gave me the number i wanted,
so i just divided the font by 6.
It will fail on several occasions but it's better then what i had.
Please someone answer a better solution.
Is there a way to know how many characters of font size 10sp can fit into a TextView of a fixed width (let's say 100dp)?
I need to track if the whole string will fit (be visible) into a TextView of predefined width.
I haven't been able to find a way to track or calculate this.
Since Android uses proportional fonts, each character occupies a different width. Also if you take kerning into account the width of a string may be shorter than the sum of the widths of individual characters.
So it's easiest to measure the whole string by adding one character at a time until (a) the entire string if found to fit within the limit, or (b) the width of the string exceeds the limit.
The following code shows how to find how many characters of size 11px fits into TextView 100px wide. (You can find formulas to convert between px & dp on the Web).
We'll do it by using measureText(String text, int start, int end) in a loop incrementing the value of end until it it no longer fits the TextView's width.
String text = "This is my string";
int textViewWidth = 100;
int numChars;
Paint paint = textView.getPaint();
for (numChars = 1; numChars <= text.length; ++numChars) {
if (paint.measureText(text, 0, numChars) > textViewWidth) {
break;
}
}
Log.d("tag", "Number of characters that fit = " + (numChars - 1));
If performance is poor, you may try using a binary search method instead of a linear loop.
The best way to determine this would just be to test it. Since you are using DP it will be relatively device-independent. Unless you are using a fixed-width font there isn't really a way to determine it theoretically unless you want to actually try and measure the width of each letter, the kerning, etc. and compare to the width of the TextView
Is there a way to tell precisely where an image is placed on the parent?
ImageView layout returns some frame around the actual image.
One can see the difference when working with images that are very different from the screen proportions. For example a square image on a long screen.
Worse: devices like Edge have their bottom bar that consumes parts of the screen and affect the layouting calculations. In that case it's hard to do reverse calculations.
I can't test this now, but try something like the following, I believe this should do what you need:
ImageView iv = (ImageView)findViewById(R.id.image_view);
Rect rect = iv.getDrawable().getRect();
int xOffset = rect.left;
int yOffset = rect.top;
That should give you the exact pixel location of the top left corner. I'm pretty sure that the top-left is considered the origin...test this and see if it gives you the expected results, though.