I really needed a clean solution for the project, so after searching and searching for any solutions that I thought I liked, I spent some time writing this.
Here is a TextView implementation with improved ellipsis control. The way this works is to use the Android Spanned interface. It defines an enumeration that you can use to mark a specific section of text that you need to make an ellipsis, if necessary.
Limitations:
- Does not support ellipsis on MIDDLE. This should be easy to add if it is really necessary (I did not).
- This class will always display text on one line, since it only supports one line of text. Others can expand it if necessary (but it is much more complicated).
Here's a usage example:
FooActivity.java
class FooActivity extends Activity { CharSequence ellipsizeText(String text) { SpannableString s = new SpannableString(text); s.setSpan(TrimmedTextView.EllipsizeRange.ELLIPSIS_AT_END, 0, s.length(), Spannable.SPAN_INCLUSIVE_EXCLUSIVE); return s; } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.foo_layout); TextView textView = (TextView) findViewById(R.id.textView4); SpannableStringBuilder text = new SpannableStringBuilder(); text.append(ellipsizeText("This is a long string of text which has important information ")); text.append("AT THE END"); textView.setText(text); } }
Res / Layouts / foo_layout.xml
<com.example.text.TrimmedTextView android:id="@+id/textView4" android:layout_width="match_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge"/>
What he
Here is an example of the result:

Implementation
package com.example.text; import android.content.Context; import android.text.Editable; import android.text.Layout; import android.text.SpannableStringBuilder; import android.text.Spanned; import android.text.TextUtils; import android.text.TextUtils.TruncateAt; import android.text.TextWatcher; import android.util.AttributeSet; import android.util.Log; import android.widget.TextView; public class TrimmedTextView extends TextView { public static enum EllipsizeRange { ELLIPSIS_AT_START, ELLIPSIS_AT_END; } private CharSequence originalText; private SpannableStringBuilder builder = new SpannableStringBuilder(); private final TextWatcher textCacheInvalidator = new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) { originalText = null; } @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) { } @Override public void afterTextChanged(Editable s) { } }; public TrimmedTextView(Context context) { this(context, null, 0); } public TrimmedTextView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public TrimmedTextView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); addTextChangedListener(textCacheInvalidator); Log.v("TEXT", "Set!"); } public CharSequence getText() { if (originalText == null) { return super.getText(); } return originalText; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); Layout layout = getLayout(); CharSequence text = layout.getText(); if (text instanceof Spanned) { Spanned spanned = (Spanned) text; int ellipsisStart; int ellipsisEnd; TruncateAt where = null; ellipsisStart = spanned.getSpanStart(EllipsizeRange.ELLIPSIS_AT_START); if (ellipsisStart >= 0) { where = TruncateAt.START; ellipsisEnd = spanned.getSpanEnd(EllipsizeRange.ELLIPSIS_AT_START); } else { ellipsisStart = spanned.getSpanStart(EllipsizeRange.ELLIPSIS_AT_END); if (ellipsisStart >= 0) { where = TruncateAt.END; ellipsisEnd = spanned.getSpanEnd(EllipsizeRange.ELLIPSIS_AT_END); } else {
Mark renouf
source share