How to disable the TextView maxLines programmatically? - android

I'm having a hard time reseting the maxLines attribute of a TextView programmatically.
Just tried setting to 0 and it doesn't work. -1 crashes the application. I could use a simpler workaround and set the maxLines to 5000 but I don't want to do that.
Any ideas how to do that?
UPDATED
Well, I've found one problem.. I've set the Ellipsize as well... I'm just going to use the following workaround:
TextView questionDetail = (TextView) mQuestionDetailHeader.findViewById(R.id.desc);
questionDetail.setText(mCurrentQuestion.getQuestion());
questionDetail.setMaxLines(Integer.MAX_VALUE); //As in the android sourcecode
questionDetail.setEllipsize(null);

As there isn't yet an approved answer - the proper way to reset the maxlines property of the TextView is:
textView.setMaxLines(Integer.MAX_VALUE);
As per Valdemar's comment and this stackoverflow answer. Using -1 will cause an ArrayIndexOutOfBoundsException.
Keep in mind only END and MARQEE setEllipsize() settings will be respected for maxlines >= 2 according to the documentation:
If setMaxLines(int) has been used to set two or more lines, END and
MARQUEE* are only supported (other ellipsizing types will not do
anything).

For setting the maxLines for a text use mTextView.setMaxLines(0) or you have to programmatic-ally measure of the height text and multiply with the number of max line
The result should set as the height of the textView

if you want to have just a single line , then why don't you use:
txtView.setSingleLine(true);

The -1 should not crash your application. This actually what is used inside TextView by default:
case com.android.internal.R.styleable.TextView_maxLines:
setMaxLines(a.getInt(attr, -1));
break;
This piece of code shows, that when android:maxLines is not specified, then code uses -1 to set the value via setMaxLines() function.
I also made test application to verify my conclusions. And it works fine without crashing:
public class HelloWorld extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
TextView text = (TextView)findViewById(R.id.text);
text.setMaxLines(-1);
}
}

Related

Measure TextView height - auto wrap

I've created an expandable cardview, which contains a title, subtitle and a detail textview.
Now I want to get the height dynamically depending on the height of the text in a textview with wrap_content. So I used the .measure method on my view in the onClickListener:
holder.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
holder.getDetailText().measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
Log.i("Project", ""+ holder.getDetailText().getMeasuredHeight());
}
});
I get a height of 57 with this text:
holder.getDetailText().setText("Test Test Test Test");
If I now add a longer text in there which automatically wraps to multiple lines I get the same height of 57
holder.getDetailText().setText("Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test ...");
As soon as I put a text with new line feed in there everything works as expected which results in a height of 155
holder.getDetailText().setText("Test\nTest\nTest");
So how is it possible to get the actually height of the textview with automatically wrapped text?
Update:
This is a very long text which is automatically wrapped, but I get the height of 57, so it will only display the first line, because the getMeasuredHeight only returns this (57) height.
What I expect, but doesn't work, because the getMeasuredHeight returns the wrong height:
If I manually put \n feed, so there is no auto-wrap getMeasuredHeight returns the right height and everything works as expected.
It seems that the .measure ignores the width, because even if I put there a constant value the height returned by getMeasuredHeight wont't change.
For long texts, TextView takes a certain amount of time to render. So before that we are calculating the height of that TextView
Use the following code:
view.setText("Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test Test ...");
view.getViewTreeObserver()
.addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
Toast.makeText(getApplicationContext(), "Height :- " + view.getMeasuredHeight(), Toast.LENGTH_SHORT).show();
}
});
Please refer to How to find the Text Area(Height/Width) of TextView programmatically in android
If you want to see exact behavior of TextView then create a CustomTextView and see how their lifecycle behaves.
Please let me know if it dosen't work for you.
I dont know whether you have figured out a solution for this,
the solution is when you are measuring height with definite width specified
you need to be very careful with the width (because the calculation is very precise) if you are not getting desired height that means you are missing something in your spec,
example is below
val widthMeasureSpec =
View.MeasureSpec.makeMeasureSpec(ceil(context.resources.getDimension(R.dimen.text_width)).toInt(), View.MeasureSpec.EXACTLY)
val heightMeasureSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED).
in the above code what you pass matter for the width parameter,
you need pass the exact width that you have passed for your textview
you need to pass measure spec as View.MeasureSpec.EXACTLY in my case

Xamarin: Setting text for a TextView programmatically

This is probably a mistake or lack of comprehension on my part, but I am quite confused right now. I'm trying to set a TextView in my Xamarin Android application programmatically. Here's my code:
TextView currentCharacterName =
FindViewById(Resource.Id.characterName);
currentCharacterName.SetText("test");
Unfortunately, this does not work, as I get the error "Argument 1: cannot convert from 'string' to 'int'". After reading in the available methods for SetText, I noticed the method I'm trying to call demands a ResId. I don't really understand why I would need a ResId to modify the text of a TextView.
I tried searching on Google for answers, and I came across this answer from 2014 that had the exact same problem as I do. The solution was to use the Text() method instead to set the TextView. Unfortunately, when I try this solution, I get the error "Non-invocable member 'TextView.Text' cannot be used like a method". When I try to check the Text method description, I see "string TextView {get/set} To be added."
Does this mean there's no implementation yet to set the text of a TextView? I am really reluctant to believe this, as it baffles me that such a big framework like Xamarin wouldn't even have get/set functions for something as simple as setting the text of TextView. I feel like there's a very simple solution for my problem, but I can't seem to find it.
TextView.SetText(X) allows you to set the text from a Resource id:
currentCharacterName.SetText(Resources.Id.MyString);
You are looking for the Text property:
currentCharacterName.Text = "test";
Xamarin: TextView class
Android.Widget.TextView.Text Property
Syntax:
public String Text { get; set; }
Test this code:
TextView currentCharacterName = FindViewById<TextView>(Resource.Id.characterName);
currentCharacterName.Text = "Your Text";

Clear TextView from text and background

I have a TextView called choice1 that I use for both text and image.
I know that this:
choice1.setText("");
clears it from text and this
choice1.setBackgroundResource(0);
clears it from any image.
Is there a way to clear it completely in 1 command or do I always have to run both commands to clear the TextView?
If you reaaaaally wanted to I guess you could make your own method..
private void clearTextView(TextView tv) {
tv.setText("");
tv.setBackgroundResource(0);
}
And then
clearTextView(choice1);
There is no single command to do both together, you have to call it separately.

TextView.setMaxLines not working?

In my app I have a screen where I display some text and then a photo. The text is variable in length (sometimes none at all, sometimes a lot), so I wanted to have it set up so the text never takes up more than a few lines (but can be scrolled) leaving enough room for the image below.
My view component for this part is created programatically, and I've adjusted the code to have the following (currently in my text-setting method, but the same thing happens if it's in the initial view-create code)
public void SetDescription(String description)
{
mTxtDescription.setText(Html.fromHtml(description));
mTxtDescription.setClickable(true);
mTxtDescription.setMaxLines(5);
mTxtDescription.setLines(5); //this makes no difference either!
mTxtDescription.setSingleLine(false);
mTxtDescription.setScrollbarFadingEnabled(true);
mTxtDescription.setScrollBarStyle(VERTICAL);
mTxtDescription.setMovementMethod(ScrollingMovementMethod.getInstance());
mTxtDescription.invalidate(); //adding this made no difference...
}
However it doesn't work- long text still fills the whole screen and the image has vanished due to being pushed down to a height of 0. How can I get the text to never be more than 5 lines?
Try removing the call to setSingleLine. And use setInputType(InputType.TYPE_TEXT_FLAG_MULTI_LINE). It'd also put this call before the setMaxLines and setLines call to be sure.
Note: setLines overrides the settings of setMaxLines and setMinLines.
The TextView has many issues surrounding the various calls to how it should display multiple, ellipses, etc.
The setSingleLine(false) seemes to reset the setMaxLines command. Try to move the setSingleLine command before the setText. That worked for me.
The below code is working fine for me
txt = (TextView)findViewById(R.id.textview);
txt.setMaxLines(5);
txt.setMovementMethod(new ScrollingMovementMethod());
txt.setScrollContainer(true);
txt.setText("Example Text");
txt.setTextColor(Color.WHITE);
txt.setScrollbarFadingEnabled(true);
in xml inside textview
android:scrollbars="vertical"

Why setting text from onMeasure does not affect TextView?

There is a TextView of a certain size and whenever text set to it is too long I'd like to re-set it to some preset value.
To accomplish this I am overriding onMeasure and calling setText. However this does not affect the TextView contents.
What am I missing?
EDIT: if you think that it should be done in a completely different way - please feel free to suggest
onMeasure() is usually called during layout phase. Besides as far as I know onMeasure() is where YOU have to measure your view. It receives 2 values which are the size of the parent of your view which may be constant.
Why don't you just check the length of the text you're setting and if it's too long just replace it with your default one?
From the View documentation page.
Measure the view and its content to determine the measured width and
the measured height. This method is invoked by measure(int, int)
and should be overriden by subclasses to provide accurate and
efficient measurement of their contents.
If you want the textview to have limited width or height call setMaxWidth() setMaxHeight() setMaxLines() or check it manualy and change is the way you like from a custom method
In your onMeasure(int,int)
CALL super(int,int)
Use getMeasuredWidth() and getMeasuredHeight
something like this:
void onMeasure(int a, int b){
super(a,b);
if(getMeasuredWidth()>bla)
setText("default");
}
This is what I do to reduce the text font in case the text content is large enough to goto the next line. The problem is pretty much the same the only difference in your case is that you want to replace it with some other default text.
ViewTreeObserver vto = ((TextView)findViewById(R.id.myTextView)).getViewTreeObserver();
vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (null != ((TextView)findViewById(R.id.myTextView))) {
if (1 < ((TextView)findViewById(R.id.myTextView)).getLineCount()) {
((TextView)findViewById(R.id.myTextView)).setTextSize(TypedValue.COMPLEX_UNIT_PX,
((TextView)findViewById(R.id.myTextView)).getTextSize() - 2);
}
}
}
});
This would take care of your length dynamically. Now the text size or length cannot be fixed as based on different form factors it would change. For instance a 20 character text in 3.2 inch screen would be decidedly large however it would be too small for 4.1 inch screen. So I would suggest you to use line count as the reference and if the text wraps then you can either reduce the font like I do or replace it by something else.
You could register a custom TextWatcher with addTextChangedListener (TextWatcher watcher).
Inside the TextWatcher you would override the afterTextChanged(Editable s) function, mesure the lenght of the text, and if too long, set it back to your default text.
EDIT: I'm not too sure about this, but this is why I think your solution with onMeasure is not working: onMeasure is called on the initial layout phase, or when the UI needs to be resized, so once your UI is set, if you change the text of the UI afterwards, and the text becomes too long, this doesn't affect the size of the TextView, and onMeasure is not called again...
AnyWay better to just look out for text changes, and do what you want when it's over a limit, and let Android do the dirty TextView measurement work
You will want to implement Textwatcher and in the onTextChanged(...) method, call TextView's setEms(...) method, which will set the width of the TextView to the width of (n m's), where n is the input for setEms(...).
...
TextView tv = (TextView) findViewById(R.id.my_text_view);
tv.addTextChangedListener(this);
...
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
tv.setEms(count);
}
This is my idea:
You can measure the text trivially with .length() if its length is bigger than your desired limit, than cut put only the substring. But if you want to display entire text you can implement onClickListener() on that TextView which envoking will show entire text as Dialog or whatever.
Another approach which I think can be suitable is to create different layouts for different density screens and then add in your xml android:maxLines="your limit" android:textSize="some value" and android:maxLength="#integer/mylimit" (different limit and size for different density). So what remains is to measure the length and only show the substring (or default text)
Something like:
String myText = "bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla bla";
String clickForMore="...Click for more";
int limit = myActivity.this.getResources().getInteger(R.integer.limit); //dont forget to declare it in your string.xml
Imagine we have 3 lines of text and we permitted only 2 (with total length value of int limit). Slice of our text fits inside the TextView, but it is not entirely shown. We need to inform the user that there is more to read...
myTextView.setVisibility(View.INVISIBLE);// <--- takes the space in the container
if(clickForMore.length() < myText.length() && myText.length() > limit)// <--- avoiding negative place and checking if the entire text is inside the box or there are characters leftover.
{
String temp = myText.substring(0, limit-clickForMore.length());// <-- we cutting space from currently displayed (not original string) to append the `clickForMore` string information.
myTextView.setText(temp+clickForMore);
myTextView.setVisibility(View.VISIBLE);
myTextView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
// display the dialog with full text....
return false;
}
}); //<--- instantiate the click listener
}
else
{
//the text fits the box and no need to slice it, just display it straight forward
}
myText.length() > limit - Test whether the string overlaps the limit.
Implement .addTextChangeListener() (TextWatcher) and in afterTextChange() override method implement all these I mentioned above.
Then you create onClickListener() for the TextView where you make dialog and then show your text as it is.
I hope you find this idea, reasonable. If you need more explanation what I had in mind feel free to write me.

Categories

Resources