Reduce number of lines in multiline TextView if not all lines fit - android

I have a vertical LinearLayout with a multiline TextView set to have a layout_weight of 1, and below that a more complex layout that wraps its content. If there isn't enough room in the outer LinearLayout, I want the number of lines shown in the TextView to be reduced. Instead, the bottom half of the bottom line just gets clipped.
Is there any way around this? This is in an app widget, so I can't use custom views.
Simplified layout XML:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="start|bottom"
android:orientation="vertical" >
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="1"
android:ellipsize="end"
android:gravity="fill_vertical"
android:maxLines="2" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<!-- ... sized content ... -->
</LinearLayout>
</LinearLayout>

A time ago I had to mess with this too, basically within an IRC client which receives hundreds of lines per tab, and sadly, the response is no, there's not any mechanism to automatically control the buffer of the TextView.
So I had to implement an infinite loop Thread (which I would later convert into a Service) which takes the number of lines of the current TextView and if it exceeds a certain amount, it's truncated from the beggining of the buffer (the oldest lines get removed).
I'll attach what have I done, maybe it might help you.
static void FreeTextBuffer() {
final TextView tv = (TextView) findViewById(R.id.your_textview);
final Spanned sptxt = (Spanned) tv.getText();
// This is a workaround, seems that Html.toHtml() passing directly the text makes the system stale - this works
final SoftReference<String> texto = new SoftReference<String>(Html.toHtml((Spanned) sptxt.subSequence(0, sptxt.length())));
final int matches = StringUtils.countMatches(texto.get(), "<br>");
if (matches > (3 * maxLines) / 2) {
// Here I'm counting the amount of \n to cut off (the 0.5 * matches is a tuned solution that works for me)
int cutpoint = -1;
for (int i = 0; i < 0.5 * matches; i++)
cutpoint = texto.get().indexOf("<br>", (cutpoint < 0)? 0 : cutpoint + 1);
// Just for the Thread...
final int forUIth = cutpoint;
if (cutpoint > -1) {
((Activity) globvars.getContext()).runOnUiThread(new Runnable() {
public void run() {
synchronized(tv) {
tv.setText(Html.fromHtml(texto.get().substring(forUIth)));
}
}
});
}
}
}

Related

Android: how to measure whether textview having enough space to render textview content

Please find layout
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="12345678901234567890"/>
<TextView
android:id="#+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="12345678901234567890"/>
<TextView
android:id="#+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:text="12345678901234567890"/>
Screenshot:
What I want is to move textview at textview or add new textview at runtime? but more importantly how I can measure whether textview have enough space in right side to render my content.
titleTxt.post {
val lineCount = titleTxt.lineCount
if (lineCount > 1) {
// Already break in 2 lines, update the content
// Do whatever you want with your content here
val textBuilder = StringBuilder()
for (line in 0 until lineCount) {
val layout = titleTxt.layout
textBuilder.append(titleTxt.text.subSequence(layout.getLineStart(line), layout.getLineEnd(line)))
.append("\n")
}
titleTxt.text = textBuilder
// Here I lower my font size so the overall height is not too big
titleTxt.setTextSize(TypedValue.COMPLEX_UNIT_PX, resources.getDimensionPixelSize(R.dimen.font_large).toFloat())
}
}
Here is what I do in my project:
I check the line count
If it's more than 2 then it doesn't have enough space in 1 line for your content.
Use the layout instance to get the substring of any line you want. Do whatever you want with your content
Suppose your wrapper layou is a LinearLayout, you may use this code to make sure whether have enought space in horizontal:
wrapperLayout.post{
if(wrapperLayout.width > textView1.width + textView2.width + textView3.width) {
//the space is enougth to contain three textview
} else {
//the sapce is not enougth
}
}

Xamarin Android - How prevent text move during textview animation

I have to do some animation in my application but i have a problem with textview.
I need to animate a textview and make it compare from right corner.
this is my layout:
<RelativeLayout
android:id="#+id/ThirdPartBottomLayout"
android:layout_width="2000dp"
android:layout_height="250dp"
android:layout_alignParentBottom="true"
android:background="#color/RedTA"
android:paddingTop="20dp"
android:paddingBottom="80dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/ThirdPartText1"
android:textSize="20dp"
android:textColor="#ffffff"
android:lines="1"
android:text="#string/Onboarding_Page3_Text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center" />
<TextView
android:id="#+id/ThirdPartText2"
android:textSize="16dp"
android:textColor="#ffffff"
android:layout_below="#+id/ThirdPartText1"
android:text="#string/Onboarding_Page3_Text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center" />
</RelativeLayout>
</RelativeLayout>
and this is where I inizialize variable:
int widhtR1 = 0;
if (ThirdPartText1.Width > WidthPixel - PixelsToDp(50))
widhtR1 = WidthPixel - PixelsToDp(50);
else
widhtR1 = ThirdPartText1.Width;
lp = new RelativeLayout.LayoutParams(widhtR1,
ThirdPartText1.Height);
lp.LeftMargin = WidthPixel;
ThirdPartText1LeftMargin = (WidthPixel - widhtR1) / 2;
ThirdPartText1.LayoutParameters = lp;
int widhtR2 = 0;
if (ThirdPartText2.Width > WidthPixel - PixelsToDp(50))
widhtR2 = WidthPixel - PixelsToDp(50);
else
widhtR2 = ThirdPartText2.Width;
lp = new RelativeLayout.LayoutParams(widhtR2,
ThirdPartText2.Height);
lp.LeftMargin = WidthPixel;
lp.TopMargin = PixelsToDp(10);
lp.AddRule(LayoutRules.Below, Resource.Id.ThirdPartText1);
ThirdPartText2LeftMargin = (WidthPixel - widhtR2) / 2;
ThirdPartText2.LayoutParameters = lp;
To animate i use a ValueAnimator that move LeftMargin from WidhtPixel to the minium left margin of textview.
And I do with this code.
ThirdPartText1Animator = ValueAnimator.OfInt(1);
ThirdPartText1Animator.SetDuration(
ThirdPartText1AnimatorDuration);
ThirdPartText1Animator.SetInterpolator(new
AccelerateDecelerateInterpolator());
var lpTxt1 =
(RelativeLayout.LayoutParams)ThirdPartText1.LayoutParameters;
ThirdPartText1Animator.Update += (sender, e) =>
{
int val = (int)e.Animation.AnimatedValue;
Console.WriteLine("VAL TXT1:" + val);
lpTxt1.LeftMargin = WidthPixel - (int)((WidthPixel -
ThirdPartText1LeftMargin) * (val / 100f));
ThirdPartText1.LayoutParameters = lpTxt1;
};
ThirdPartText2Animator = ValueAnimator.OfInt(1);
ThirdPartText2Animator.SetDuration(
ThirdPartText2AnimatorDuration);
ThirdPartText2Animator.SetInterpolator(new
LinearInterpolator());
var lpTxt2 =
(RelativeLayout.LayoutParams)ThirdPartText2.LayoutParameters;
ThirdPartText2Animator.Update += (sender, e) =>
{
int val = (int)e.Animation.AnimatedValue;
Console.WriteLine("VAL TXT2:" + val);
lpTxt2.LeftMargin = WidthPixel - (int)((WidthPixel -
ThirdPartText2LeftMargin) * (val / 100f));
ThirdPartText2.LayoutParameters = lpTxt2;
};
/*** START WITH ****/
ThirdPartText1Animator.SetIntValues(0, 100);
ThirdPartText1Animator.Start();
ThirdPartText2Animator.SetIntValues(0, 100);
ThirdPartText2Animator.Start();
And here comes the problem when the animation start, text view compare from right but text will move to fit the textview dimension on screen instead of stay blocked on textview real dimension.
How could I avoid to make text move inside a textview.
Hope my information is enough and sorry for my bad english.
EDIT
WidthPixel = Resources.DisplayMetrics.WidthPixels;
AccelerateDecelerateInterpolator is an Interpolator Android.Views.Animation
Full classes
OnboardingPage.cs
OnboardingPageLayout.axml
Thanks in advance.
Matteo.
For everyone have the same problem i figured out a solution.
In a first time i use left margin to make textview compare, so when application start i set left margin to width of screen and when i need to make it appear i reduce left margin.
It seems that if you change somethings of textview it been forced to redraw everythings so also widht and height change.
To avoid this problem I create a layout and put textview inside it and use the same trick of left margin to layout instead on textview and everythings work.
Sorry for my bad english.
Hope that should be helpful for someone.

Android: Measured width of dynamically created TextView sometimes wrong

I'm stuck on a strange little problem.
The goal with this activity is to display two texts, one is the original the second the answer text. The answer text contains errors and the user has to find and mark those errors.
The solution we came up with is to split the text into its words and display each word as its own in a TextView. All these TextViews are created dynamically at runtime, because there are many different texts to display.
There are two instances, where we need a 'line break': a) the text contains a linebreak () and b) the width of the display wouldn't fit any more text.
This solution works most of the time but each text has 2-4 words, which don't fit the line width and are therefore broken up into multiple lines visually.
Here's the code:
String[] questionSplit = exercise.exerciseQuestion.split(" ");
ids = new Integer[questionSplit.length];
int displayWidth = getResources().getDisplayMetrics().widthPixels;
int currentLineWidth = 0;
Integer lastIdInRow = 0;
int counter = 0;
for(String bit : questionSplit) {
TextView tv = new TextView(this);
tv.setId(generateViewId());
ids[counter] = tv.getId();
//Exception for <br>
if(bit.equals("<br>")) {
lastIdInRow = ids[counter - 1];
currentLineWidth = 0;
} else {
tv.setText(bit);
tv.setPadding(dpToPx(3), dpToPx(3), dpToPx(2), dpToPx(2));
tv.measure(0, 0);
currentLineWidth += tv.getMeasuredWidth();
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
if(currentLineWidth <= displayWidth && counter == 0) {
// move along, nothing to see here
} else if(currentLineWidth <= displayWidth && counter != 0) {
p.addRule(RelativeLayout.RIGHT_OF, ids[counter - 1]);
} else {
lastIdInRow = ids[counter - 1];
currentLineWidth = 0;
}
if(lastIdInRow != 0 && lastIdInRow != tv.getId()) {
p.addRule(RelativeLayout.BELOW, lastIdInRow);
}
rlTextComparisonOriginal.addView(tv,p);
}
counter++;
}
To explain the layout rules of the TextViews: if the measuredWidth fits into the line, a RIGHT_OF the last id rule is added. If it would overflow, a BELOW the last id in the line rule is added.
As I mentioned earlier, for most of the text that works perfectly. But there are some words which do not fit. If I change the displayWidth to be only 80% of the display width, the error persists just the word changes, so I think it's not the specific text / word.
And here is the relevant part of the view's xml
<ScrollView
android:id="#+id/svTextComparisonDesc"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/tvTextComparisonHeaderMiddle"
android:paddingBottom="55dp"
>
<RelativeLayout
android:id="#+id/rlTextComparison"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tvTextComparisonDescription"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#color/text_background"
android:padding="10dp"
android:text="#string/text_exercise_desription"
android:textSize="#dimen/activity_text_description_size"
android:scrollHorizontally="false"
/>
<RelativeLayout
android:id="#+id/rlTextComparisonOriginal"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="#color/text_background"
android:layout_below="#id/tvTextComparisonDescription"
/>
<RelativeLayout
android:id="#+id/rlTextComparisonAnswer"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="#id/rlTextComparisonOriginal"
/>
</RelativeLayout>
</ScrollView>
The last bit of information: right now the code above resides in the activity's onCreate Method. If I log the measuredWidth and displayWidth and currentWidth, the logic isn't broken, the measuredWidth fits into the line, but after rendering, it doesn't.
Any ideas what the problem actually might be? Thanks in advance!
After using the mentioned lib the code is much cleaner and looks like this:
//Exception for <br>
if(bit.equals("<br>")) {
FlowLayout.LayoutParams lp = new FlowLayout.LayoutParams(0,0);
lp.setNewLine(true);
flTextComparisonOriginal.addView(tv,lp);
} else {
tv.setText(bit);
tv.setPadding(dpToPx(3), dpToPx(3), dpToPx(2), dpToPx(2));
RelativeLayout.LayoutParams p = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
flTextComparisonOriginal.addView(tv, p);
}
In case using a library is acceptable instead of coding it yourself, you will find two projects on github under the keyword "FlowLayout". These sound like they solve the layout you need:
https://github.com/ApmeM/android-flowlayout
https://github.com/blazsolar/FlowLayout
To get width and height of the text view,
Rect bounds = new Rect();
Paint textPaint = textView.getPaint();
textPaint.getTextBounds(text,0,text.length(),bounds);
int height = bounds.height();
int width = bounds.width();

How to make my toy android calculator UI fits with the device screen

I want to create a simple calculator as my first android project (instead of Helloworld), the calculaotr UI is shown below, which I uses GridLayout to arrange the result display TextView and the control Buttons, the GridLayout takes 6 rows and 4 columns. ( I simplified the xml layout file here)
<GridLayout android:rowCount="6"
android:columnCount="4"
android:orientation="horizontal">
<TextView android:id="#+id/expressionTextView"
android:layout_columnSpan="4"/>
<LinearLayout
android:layout_columnSpan="4">
<Button android:id="#+id/btnClearText"/>
<Button android:id="#+id/btnDeleteText"/>
</LinearLayout>
<GridLayout>
For creating the +/-/x//0-9. buttons, I use the following code:
String[] buttonTexts = new String[]
{
"7", "8", "9", "/",
"4", "5", "6", "x",
"1", "2", "3", "-",
".", "0", "=", "+"
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Point size = new Point();
getWindowManager().getDefaultDisplay().getSize(size);
int screenWidth = size.x;
int screenHeight = size.y;
int oneQuarterWidth = (int) (screenWidth * 0.25);
for (int ii = 0; ii < buttonTexts.length; ii++) {
Button btn = new Button(this);
btn.setText(buttonTexts[ii]);
btn.setTextSize(40);
GridLayout.Spec rowSpec = GridLayout.spec(ii/4 + 2);
GridLayout.Spec columnSpec = GridLayout.spec(ii % 4 );
GridLayout.LayoutParams params = new GridLayout.LayoutParams(rowSpec, columnSpec);
params.setGravity(Gravity.FILL);
params.width = oneQuarterWidth;
gridLayout.addView(btn, params);
}
However, the last row stretches on the vertical direction, and when running the calculator on my android machine, some of the last row may be hidden!
What I want is to make the UI fits with the device height, do you have any ideas? Thanks!
P.S.
I even tried to set the heights of the result display TextView and LinearLayout that contains Clear/Del by using:
expressionTextView = (TextView)findViewById(R.id.expressionTextView);
clearDelLinearLayout = (LinearLayout)findViewById(R.id.linearLayout1);
GridLayout.LayoutParams expressionTextViewParams =
(GridLayout.LayoutParams)expressionTextView.getLayoutParams();
expressionTextViewParams.height = ...; // some calculated height
expressionTextView.setLayoutParams(expressionTextViewParams);
But this doesn't work :(
from documentation:
Limitations:
GridLayout does not provide support for the principle of
weight, as defined in weight. In general, it is not therefore possible
to configure a GridLayout to distribute excess space between multiple
components. Some common use-cases may nevertheless be accommodated as
follows. To place equal amounts of space around a component in a cell
group; use CENTER alignment (or gravity). For complete control over
excess space distribution in a row or column; use a LinearLayout
subview to hold the components in the associated cell group. When
using either of these techniques, bear in mind that cell groups may be
defined to overlap.
Source
try using LinearLayouts for the children, like:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" android:orientation="horizontal">
<Space
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btnClearText" />
Or you can simply use another layout, like e.g. LinearLayout, which is pretty consistent when it comes to rendering
P.S.
from docs:
a call to setLayoutParams(ViewGroup.LayoutParams) must be made to
notify GridLayout of the change
Maybe after changing layout parameters, you should call GridLayout.invalidate(); ?

Android Multi Column Text

Is there any way in which I can display a long text on multiple columns?
For example I need to display an article, I have the entire String and I want to place it on 3 columns. How can I achieve this? Are there any libraries that can help me or do you know how should I approach this problem?
Any suggestion is appreciated. Thanks.
EDIT: The issue is the splitting of the string not the layout. I know I can use TableLayout... or weights... to distribute column evenly and so on. The problem is how do I split the String properly. Maybe 2 column would get filled and the 3rd just half of it? I don't know how to approach this, not the actual layout.
Check the TableLayout. To split your text you could split your text after 1/3 of your count of chars. Of cource you have to split the text at one whitespace charecter. UPDATE: See also my example code at the end of my posting.
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:text="#string/table_layout_4_open"
android:padding="3dip" />
<TextView
android:text="#string/table_layout_4_open_shortcut"
android:gravity="right"
android:padding="3dip" />
</TableRow>
<TableRow>
<TextView
android:text="#string/table_layout_4_save"
android:padding="3dip" />
<TextView
android:text="#string/table_layout_4_save_shortcut"
android:gravity="right"
android:padding="3dip" />
</TableRow>
</TableLayout>
For splitting you can test this code. The algorithem could maybe a little better be nearer to the split boundaries, but it works.
public static String[] getRows(String text, int rows) {
// some checks
if(text==null)
throw new NullPointerException("text was null!");
if(rows<0 && rows > 10)
throw new IllegalArgumentException("rows must be between 1 and 10!");
if(rows==1)
return new String[] { text };
// some init stuff
int len=text.length();
int splitOffset=0;
String[] ret=new String[rows];
Pattern whitespace = Pattern.compile("\\w+");
// do the work
for(int row=1;row<rows;row++) {
int end;
int searchOffset=len/rows*row;
// search next white space
Matcher matcher = whitespace.matcher(text.substring(searchOffset));
if(matcher.find() && !matcher.hitEnd()) {
// splitting on white space
end=matcher.end()+searchOffset;
} else {
// hard splitting if there are no white spaces
end=searchOffset;
}
ret[row-1]=text.substring(splitOffset, end);
splitOffset=end;
}
// put the remaing into the last element
ret[rows-1]=text.substring(splitOffset);
return ret;
}
Are you looking for a 'newspaper column' style? Once the first (fixed size) text-box fills, the text should continue into the other and so on?
If so, why not display your long string into a WebView where you can more easily achieve this using HTML and CSS?
use (Calculate text size according to width of text area)
Paint paint = new Paint();
Rect bounds = new Rect();
int text_height = 0;
int text_width = 0;
paint.setTypeface(Typeface.DEFAULT);// your preference here
paint.setTextSize(25);// have this the same as your text size
String text = "Some random text";
paint.getTextBounds(text, 0, text.length(), bounds);
text_height = bounds.height();
text_width = bounds.width();
to calculate how much is your text going to take space.
and calculate view.getWidth() about how much is your view width...
Then do the needful by dividing the text accordingly.

Categories

Resources