I am writing an app, in which part of it displays a line of text. there are certain scenarios where that line of text will take up more than one line. the problem is that I only want it to take up one line, and when I set it up either in java or in xml to only take up one line, the text is cut off. how would I make it so that it automatically adjusts the font size of the text so that it will only take up one line without being cut off?
Use proportions along with Paint.measureText():
(text size / measureText width) = (perfectSize / screenWidth)
Solving for the perfect text size:
perfectSize = (text size / measureText width) * screenWidth;
You can find the screen width with getWindowManager().getDefaultDisplay().getWidth() from the Display class.
Turned it into a math problem!
This isn't too hard to do if you use Paint#measureText.
Basically you would start with a font height smaller than the height of your TextView and then iterate (or do a binary search) through fonts of varying sizes until you find one that measures to smaller than the width of your TextView.
It requires some implementation, as there is no "automatic" way provided by the framework.
Related
I have a textview where I want to show text in vertical (just like rotated 90º). With android:rotation="-90" you can get this.
If the text would have no rotation, and you would like to fit a sentence, you would first look at the width of the textview, since text will try to be in 1 line. Since I rotate the text, here I expect to see the height of the object. However, Android is still considering the width. I need to fix the size of the width, but this causes me a problem for the text: Although the textview has a big vertical dimension and text would fit, Android thinks it needs to use the width to calculate the available space. At the end this causes me overflow of the text, appearing for example, just one word as multiple lines of 1 char.
How can I make android to understand that the space for the lenght to fit the text is now the vertical, instead of the horizontal?
I'm trying to draw text using Canvas and have found that using StaticLayout would take care of the line breaks automatically. I also want to limit its height so that when text is too long it would be ellipsized, but the size of text container is dynamic. I can easily apply the width to StaticLayout, but cant find a way to do height.
I tried to utilize TextUtils.ellipsize(), but having issue to get the spacing between lines.
PerracoLabs has the right answer but, as CheokYanCheng stated, the calculation of the maximum number of lines is off (although it may yield the correct result many if not most of the time).
A maximum height cannot be specified for a StaticLayout except indirectly by specifying the maximum number of lines. Ellipsis is tied to the maximum line count anyway, so determining the maximum number of lines for a specific height to back into a solution is the way to go. So, how do we determine the appropriate maximum line count so that a fixed-size StaticLayout with ellipsis can be created as PerracoLabs has explained.?
If the text has no spans that effect the height any of the lines of text then a simply calculation can determine the maximum number of lines that will fit into a StaticLayout before ellipsis.
The following Kotlin function will determine how many lines of text will fit into a view that has a fixed height and width. It is assumed that each line of a StaticLayout has a set height (no height-effecting spans). The top line has the same height of other lines but it is augmented by a top padding. The bottom line has a bottom padding added to it.
private fun getMaxLines(maxHeight: Int): Int {
// Build a dummy StaticLayout to get the internal measurements.
return makeStaticLayout("", width, 1).run {
val lineHeight = getLineBottom(0) - getLineTop(0) + topPadding - bottomPadding
(maxHeight - topPadding - bottomPadding) / lineHeight
}
However, if the text contains a span that changes the height of one or more lines then the only way to calculate the maximum number of lines is through the creation of the static layout that holds the entire text (no ellipsis) followed by an inspection of the lines within the layout to determine how many complete lines have fit. A new StaticLayout can then be created with the calculated maximum lines determined from the inspection.
The following Kotlin function will calculate the maximum lines by inspecting the StaticLayout for the last full line that is present.
private fun getMaxLinesByInspection(staticLayout: StaticLayout, maxHeight: Int): Int {
var line = staticLayout.lineCount - 1
while (line >= 0 && staticLayout.getLineBottom(line) >= maxHeight) {
line--
}
return line + 1
}
I have posted a small project on GitHub as a demonstration.
Here is a screen shot of the app.
You (and #Cheok Yan Cheng) might try to make use of PagedTextView. The view is intended for Paginating text in Android.
The view partially solves the problem, i.e. reacts to dynamic size changes. As to text ellipsizing, you might achieve this by customising the algorithm I've used for measuring text in height.
I need to make "pages" (most likely for use ViewPager) which will contain images and text.
For example first will have image, at image download will get image dimensions. After image on layout will be X space left where i can display Y lenght text. Then for next page i will split rest of text into new string to be displayed. TextSize is in dp units.
I had idea to get how much pixels avarage letter takes and then calculate approx how many lines i can show in one page.
What would be a best way to make these calculations ?
And for starter i did letter calculation
final float densityMult = ctx.getResources().getDisplayMetrics().density;
final float scaledPx = 20 * densityMult; //i guess its same as 20dp
paint.setTextSize(scaledPx);
final float size = paint.measureText("a");
Where on 480x800 3.7" screen it returns value 16.0 and on 540x960 4.0" 17.0
Is these values pixels ?
I didn't really understand why you want to measure your text, but just like you have:
paint.measureText("a");
you can measure any string, not just characters. If you want to split your text manually (I shouldn't recommend that), you can check whenever the measure of your text is higher than the available width.
And yes, measureText returns the measure in px
You can control the height of a text character, drawn into the canvas, with setTextSize(). For example, if you want text of height 8 pixels, you would write:
mPaint.setTextSize(8);
This works fairly well. However, I cannot seem to find a way to set the width. Hence a 8x12 pixel character looks exactly the same as an 12x12 character. I am using monospace font and want to carefully control the size of each character in a canvas.
Any ideas on how to set character width in pixels?
I believe the vertical height of the text is considered the text's "size", while the horizontal width is considered the "scale". For example, if you are drawing text onto a canvas using a Paint, the Paint has methods called getTextScaleX and setTextScaleX which you can use to:
Get [and set] the paint's horizontal scale factor for text.
Depending on how you are using the canvas these may be the methods you want to use - most other methods for text deal with horizontal text spacing rather than modifying the size of the actual characters themselves.
I have built a ListView and my items - at least in part - contain titles of various (text) lengths.
In order to enable the user to read as much of the title as possible, I'm trying to change my adapter to auto-pick a feasible font size for my texts.
So I'm working with the TextView's paint object to measure the text in a basline font size (14dp) and try to compare against the available space. If the text is too big, I reduce the font size to 12dp (later I might think about reducing it even further).
// Note: vh.filmTitleTextView is my TextView, filmText contains the title I want to display
filmTitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 14);
float textWidth = vh.filmTitleTextView.getPaint().measureText(filmText);
if (textWidth > vh.filmTitleTextView.getWidth())
vh.filmTitleTextView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 12);
The issue is that on first run, vh.filmTitleTextView.getWidth() always returns zero. I guess this is because the layout has not been rendered before and the size is not yet known.
I can't just go with the full size of the ListView because the textView doesn't have the same width (despite the fact that it is set to layout_width="fill_parent") - there are some elements around it.
Any ideas?
Had a similar problem that was my bane for a long time - this might help ya: Auto Scale TextView Text to Fit within Bounds