I'm coding an App for learning tones of Chinese characters. The test words contain anything from 1 to 12 Chinese characters, which I want to be evenly distributed over the width of the page, with shorter words having larger characters than longer words. In order to achieve this, I'm filling a horizontal linear layout with programmatically created textviews. Each of these textviews contains one single Chinese character and gets assigned a weight through layout parameters.
The width of the textviews gets applied correctly, however, I fail to auto-size the containing characters (again: only one Chinese character per textview) .
I've tried different versions of auto-size, but the text size doesn't change, as it only seems to take the height of the textview into account, not the width. This results in characters being cropped in width for longer words.
This is the Container in which I create single text views:
XML
<LinearLayout
android:id="#+id/hanziframe"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="20"
android:orientation="horizontal"
android:weightSum="100"
>
Here I iterate through the single characters of the test word, giving each the same weight in the above layout:
public void proceedToQuestion(String han) {
int nhanzi = han.length(); //number of characters
float widthperhanzi = 100 / nhanzi;
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
0, LayoutParams.MATCH_PARENT,widthperhanzi);
for (int i = 0; i < nhanzi; i++) {
TextView h = new TextView(getApplicationContext());
h.setLayoutParams(lp);
h.setText(han.substring(i, i + 1));
TextViewCompat.setAutoSizeTextTypeWithDefaults(h, AUTO_SIZE_TEXT_TYPE_UNIFORM);
hanziframe.addView(h);
hanzis.add(h);
}
A word with three characters gets displayed ok
A longer word - here the single characters get cropped in width.
How can I make the characters assume the correct size depending on how many there are? Is there any way to calculate the text size directly from the screen size? Or have I just set the layout parameters incorrectly?
Any help is greatly appreciated!
textview 新特性 Autosizing 了解一下 向下支持到 Android Level 14
Related
Consider this layout:
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="160dp"
android:textSize="20sp"
android:text="Apple Inc. is an American multinational technology company headquartered in Cupertino, California, that designs, develops, and sells consumer electronics, computer software, and online services."
android:ellipsize="end"
android:gravity="top"/>
What I want the TextView to do is to figure out how many lines it can fit in 160dp and places three dots if room is not enough. However the text is cut off instead:
How can I achieve the above behavior? I assume I can do it by just specifying some attribute on the layout resource file. Thanks.
There's no way to achieve this by simply specifying attributes on the layout resource file.
If you look at the setEllipsize() method in TextView, you'll see that it says:
Causes words in the text that are longer than the view's width to be ellipsized instead of broken in the middle. You may also want to setSingleLine() or setHorizontallyScrolling(boolean) to constrain the text to a single line. Use null to turn off ellipsizing. If setMaxLines(int) has been used to set two or more lines, only TextUtils.TruncateAt.END and TextUtils.TruncateAt.MARQUEE are supported (other ellipsizing types will not do anything).
From that documentation, you can see that ellipsis in TextView is actually highly dependent on two factors: view width and number of lines.
As such, I recall that an ellipsis might not show up for a TextView if it doesn't know the maximum number of lines that your TextView can have.
So for your intended behavior, this isn't possible because you're depending on the View's height instead. You want to use a specific height then have a TextView calculate the amount of lines that can be shown within that height. Finally, if the amount of lines needed is greater than the amount shown, you want to show ellipses.
That's... not possible with how the ellipses in TextView is coded. It simply wasn't meant for that.
Therefore, if you want this behavior, you need to either:
Create a custom TextView for your specific ellipses implementation
Manually calculate the amount of lines visible, then use setMaxLines() on that TextView
For the second option, you can calculate this by using the TextView methods getHeight() and getLineHeight().
For example:
textview = (TextView) findViewById(R.id.exampleTextView);
textview.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
final int maxLines = textview.getHeight() / textview.getLineHeight();
textview.setMaxLines(maxLines);
}
});
In my example, I'm doing this inside a ViewTreeObserver to avoid getHeight() calling a size of 0.
The second option should be able to get the behavior you want, but if you want this for every TextView, you should consider creating a custom TextView instead.
Set the layout_gravity on the TextView to fill
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.
I'm using AutoResizeTextView avaiable at this post in stackoverflow.
I have made a grid list with image on top and text on bottom, the way its being done is like this:
<ScrollView>
<LinearLayout>
<Programmed LinearLayout HORIZONTAL orientation>
<Programmed LinearLayout VERTICAL orientation>
<Programmed ImageView/>
<Programmed AutoResizeTextView/>
<LinearLayout/>
(Repeats till screen width complete)
<LinearLayout/>
(Repeats till screen heigth complete)
<LinearLayout/>
<ScrollView/>
Here is a image of how it looks and the "problem" i'm having:
As you can see Cassie Cage and Jacqui Briggs has the text autosized to fit in the space reserved for it, the thing is, it seems different fom the rest of the names, and what I want is to get the smallest text and resize all the other ones to that size.
Heres a sketch of the code I started making for it, but its not working.
ArrayList<TextView> TextViewList = new ArrayList<TextView>();
float smallerTextSize=999999f;
//Declaring one of the textViews:
AutoResizeTextView tv = new AutoResizeTextView(this);
//Correction for 4.0.4 android or lower
final String DOUBLE_BYTE_SPACE = "\u3000";
String fixString = "";
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.HONEYCOMB_MR1
&& android.os.Build.VERSION.SDK_INT <= android.os.Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
fixString = DOUBLE_BYTE_SPACE;
}
tv.setText(fixString + "The text" + fixString);
tv.setPadding(Math.round(gap / (qtd + 1)), 0, 0, 0);
tv.setText(name);
la.addView(tv);
TextViewList.add(tv);
if(tv.getTextSize()<smallerTextSize){
smallerTextSize=tv.getTextSize();
}
So in this code above I declare one of the TextViews that I use in the screen (any increments suggestions will be appreciated)
final int tvlSize = TextViewList.size();
for(int i=0;i<tvlSize;i++){
TextViewList.remove(0).setTextSize(smallerTextSize);
}
So I use the smallerTextSize found in the declaration of the textSizes so I can change the textSize of all the TextViews, but it doesn't work, and the result keeps being the same as the image shown.
How can it be done correctly so all the text assume the size of the smallest one so it keeps up a pattern?
Also, is there a way for those texts to be aligned in the center of that gap?
Thanks in advance!
Specify your textsize in sp. It will scale according to the screen size.
http://developer.android.com/training/multiscreen/screendensities.html
Use gravity to align the text in the center.
I am writing a reader under Android. Pages are separated into different View. How to calculate how many characters fit on the screen to correctly divide the text into different View? Maybe there is a simple method to do this?
The code for adding text to Views:
for (int i = 0; i < pages; i++) {
TextView textView = new TextView(getApplicationContext());
textView.setText(text);
realViewSwitcher.addView(textView);
}
you could probably make assumptions based on text size, maybe the default here would be 12dip. And then assuming that the average word length is 5 words. determining how many dips a 5 character words takes. and then taking the width and height and and divide it by that number.
Maybe Paint can help you. There is methods like "measureText", or "breakText". You can get a Paint object from a TextView with getPaint().
perhaps you could calculate the number of ems that fit in the textview somehow.
or perhaps you could create an algorithm that try's to fit the characters based on computerVerticalScrollRange(). Through trial and error, it could eventually find the perfect fit.
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