I have a TextView
with maxLines:5
and ellipsize:end
applied, I'm also using setMovementMethod(LinkMovementMethod.getInstance())
on the TextView
to make links clickable (HTML content).
The combination of all of the above disables the text being truncated and the '...' suffix to be appended.
Any idea what goes wrong and how to work around it?
Without setting the movement method, everything works as expected.
Update regarding bounty: looking for solution other than manually setting ellipses
Sorry I'm late on this one.
Here is little work around for this
MainActivity
public class MainActivity extends AppCompatActivity {
TextView htmlTextView;
CustomEllipsizeTextView customEllipsizeTextView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
htmlTextView = findViewById(R.id.htmlTextView1);
customEllipsizeTextView = findViewById(R.id.customEllipsizeTextView);
String value = "Hello this is a dummy textview";
String myText = "You can visit my Profile in <a href=\"https://stackoverflow.com/users/7666442/nilesh-rathod?tab=profile\">stackoverflow</a> Lorem ipsum dolor sit amet, consectetur adipiscing elit. Duis bibendum mattis risus eget pulvinar. Praesenttingd" +
" commodo erat enim, id564654 congue sem tristique vitae. Proin vitae accumsan justo, ut imperdiet Mauris neque nibh, hendrerit id tortor vel, congue sagittis odio. Morbi elementum lobortis maximus. Etiam sit amet porttitor massa. Fusce sed magna quis arcu tincidunt finibus vitae id erat. " +
"commodo erat enim, id54654 congue sem tristique vitae. Proin vitae accumsan commodo erat enim, id congue sem tristique vitae. Proin vitae accumsan Pellentesque massa mi, imperdiet eget accums ";
SpannableString spanText2 = new SpannableString(myText);
htmlTextView.setText(value);
if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
customEllipsizeTextView.setText(Html.fromHtml(spanText2.toString(), Html.FROM_HTML_MODE_LEGACY));
} else {
customEllipsizeTextView.setText(Html.fromHtml(spanText2.toString()));
}
htmlTextView.setMovementMethod(LinkMovementMethod.getInstance());
customEllipsizeTextView.setMovementMethod(LinkMovementMethod.getInstance());
customEllipsizeTextView.setOnTouchListener(new TouchTextView(spanText2));
}
}
layout.activity_main
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".FirstFragment">
<TextView
android:id="@+id/htmlTextView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="@string/link_text" />
<neel.com.demo.CustomEllipsizeTextView
android:id="@+id/customEllipsizeTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:linksClickable="true"
android:maxLines="5"
android:padding="5dp"
android:visibility="visible" />
</LinearLayout>
CustomEllipsizeTextView
public class CustomEllipsizeTextView extends android.support.v7.widget.AppCompatTextView {
public CustomEllipsizeTextView(Context context) {
super(context);
}
public CustomEllipsizeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomEllipsizeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
StaticLayout layout = null;
Field field = null;
try {
Field staticField = DynamicLayout.class.getDeclaredField("sStaticLayout");
staticField.setAccessible(true);
layout = (StaticLayout) staticField.get(DynamicLayout.class);
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
if (layout != null) {
try {
field = StaticLayout.class.getDeclaredField("mMaximumVisibleLineCount");
field.setAccessible(true);
field.setInt(layout, getMaxLines());
} catch (NoSuchFieldException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (layout != null && field != null) {
try {
field.setInt(layout, Integer.MAX_VALUE);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
TouchTextView
public class TouchTextView implements View.OnTouchListener {
Spannable spannable;
public TouchTextView (Spannable spannable){
this.spannable = spannable;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
if(!(v instanceof TextView)){
return false;
}
TextView textView = (TextView) v;
if (action == MotionEvent.ACTION_UP ||
action == MotionEvent.ACTION_DOWN) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= textView.getTotalPaddingLeft();
y -= textView.getTotalPaddingTop();
x += textView.getScrollX();
y += textView.getScrollY();
Layout layout = textView.getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
ClickableSpan[] link = spannable.getSpans(off, off, ClickableSpan.class);
if (link.length != 0) {
if (action == MotionEvent.ACTION_UP) {
link[0].onClick(textView);
} else if (action == MotionEvent.ACTION_DOWN) {
Selection.setSelection(spannable,
spannable.getSpanStart(link[0]),
spannable.getSpanEnd(link[0]));
}
return true;
} else {
Selection.removeSelection(spannable);
}
}
return false;
}
}
OUTPUT
Here is the explanation :
I have debugged TextView and found out the following :
So when you use LinkMovementMethod() actually text is acting as Spannable. In other case it is String.
There is one following condition inside TextView
if (mText instanceof Spannable) {
//executes incase of LinkMovementMethod
result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad,
mBreakStrategy, mHyphenationFrequency, mJustificationMode,
getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth);
} else {
//executes without any movementmethod
..create StaticLayout
}
So DynamicLayout internally calls StaticLayout to render text, but it is not setting mMaximumVisibleLineCount
inside StaticLayout when coming from DynamicLayout so it is default Integer.MAX_VALUE
. But when creating StaticLayout from String, it is actually setting mMaximumVisibleLineCount
as maxLines
. This mMaximumVisibleLineCount
is used for displaying ellipsize. That's why "..." is not displaying.
For displaying the number of lines, the following code works
if (mMaxMode == LINES && mLayout.getLineCount() > mMaximum) {
unpaddedHeight = Math.min(unpaddedHeight, mLayout.getLineTop(mMaximum));
}
mMaximum
will be set to maxLines in both case, but mLayout.getLineCount()
will be maxLines for one without MovementMethod and for with MovementMethod it will be number of lines of original string
Try this code.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textStyle="bold"
android:textSize="20dp"
android:id="@+id/txtTitle"
android:text="" />
String message="<font color='gray'>"+"YOUR CONTENT"+ "<br>" +"<font color='cyan'>"+"<font size='5'>"+" "+"</font>";
txtTitle.setBackgroundColor(Color.TRANSPARENT);
txtTitle.setText(message);
Try this code.
XML
<TextView
android:id="@+id/tvcondition1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/white"
android:textStyle="bold"
android:ellipsize="end"
android:maxLines="5"
android:textSize="15sp" />
Java file Code
String strTeamsCondition = "<b><u> Terms & Condition </u></b>";
String conditionlimit = "Terms & Condition";
String strConditionFirstLine = getString(R.string.condition_1);
String condition_1_1 = getString(R.string.condition_1_1);
String htmlAsString2 = strConditionFirstLine + strTeamsCondition + condition_1_1;
Spanned htmlAsSpannedCondition = Html.fromHtml(htmlAsString2);
tvcondition1.setText(htmlAsSpannedCondition);
Spannable spanText = new SpannableString(htmlAsSpannedCondition);
spanText.setSpan(new MyClickableSpan(htmlAsSpannedCondition), strConditionFirstLine.length(), strConditionFirstLine.length() + conditionlimit.length() + 1, 0);
tvcondition1.setText(spanText);
tvcondition1.setMovementMethod(LinkMovementMethod.getInstance());
Touch Selected Text
class MyClickableSpan extends ClickableSpan {
public MyClickableSpan(Spanned string) {
super();
}
public void onClick(View tv) {
Toast.makeText(getApplicationContext(), "Thanks for the click!",
Toast.LENGTH_SHORT).show();
}
public void updateDrawState(TextPaint ds) {
ds.setColor(getResources().getColor(R.color.black));
ds.setUnderlineText(true); // set to false to remove underline
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With