Home | History | Annotate | Download | only in ui
      1 /*
      2  * Copyright (C) 2013 Google Inc.
      3  * Licensed to The Android Open Source Project.
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mail.ui;
     19 
     20 import android.content.Context;
     21 import android.text.Layout;
     22 import android.text.Layout.Alignment;
     23 import android.text.SpannableStringBuilder;
     24 import android.text.Spanned;
     25 import android.text.StaticLayout;
     26 import android.text.TextUtils;
     27 import android.util.AttributeSet;
     28 import android.widget.TextView;
     29 
     30 /**
     31  * A special MultiLine TextView that will apply ellipsize logic to only the last
     32  * line of text, such that the last line may be shorter than any previous lines.
     33  */
     34 public class EllipsizedMultilineTextView extends TextView {
     35 
     36     public static final int ALL_AVAILABLE = -1;
     37     private int mMaxLines;
     38 
     39     public EllipsizedMultilineTextView(Context context) {
     40         this(context, null);
     41     }
     42 
     43     public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
     44         super(context, attrs);
     45     }
     46 
     47     @Override
     48     public void setMaxLines(int maxlines) {
     49         super.setMaxLines(maxlines);
     50         mMaxLines = maxlines;
     51     }
     52 
     53     /**
     54      * Ellipsize just the last line of text in this view and set the text to the
     55      * new ellipsized value.
     56      * @param text Text to set and ellipsize
     57      * @param avail available width in pixels for the last line
     58      * @param paint Paint that has the proper properties set to measure the text
     59      *            for this view
     60      * @return the {@link CharSequence} that was set on the {@link TextView}
     61      */
     62     public CharSequence setText(final CharSequence text, int avail) {
     63         if (text == null || text.length() == 0) {
     64             return text;
     65         }
     66 
     67         setEllipsize(null);
     68         setText(text);
     69 
     70         if (avail == ALL_AVAILABLE) {
     71             return text;
     72         }
     73         Layout layout = getLayout();
     74 
     75         if (layout == null) {
     76             final int w = getWidth() - getCompoundPaddingLeft() - getCompoundPaddingRight();
     77             layout = new StaticLayout(text, 0, text.length(), getPaint(), w, Alignment.ALIGN_NORMAL,
     78                     1.0f, 0f, false);
     79         }
     80 
     81         // find the last line of text and chop it according to available space
     82         final int lastLineStart = layout.getLineStart(mMaxLines - 1);
     83         final CharSequence remainder = TextUtils.ellipsize(text.subSequence(lastLineStart,
     84                 text.length()), getPaint(), avail, TextUtils.TruncateAt.END);
     85 
     86         // assemble just the text portion, without spans
     87         final SpannableStringBuilder builder = new SpannableStringBuilder();
     88 
     89         builder.append(text.toString(), 0, lastLineStart);
     90 
     91         if (!TextUtils.isEmpty(remainder)) {
     92             builder.append(remainder.toString());
     93         }
     94 
     95         // Now copy the original spans into the assembled string, modified for any ellipsizing.
     96         //
     97         // Merely assembling the Spanned pieces together would result in duplicate CharacterStyle
     98         // spans in the assembled version if a CharacterStyle spanned across the lastLineStart
     99         // offset.
    100         if (text instanceof Spanned) {
    101             final Spanned s = (Spanned) text;
    102             final Object[] spans = s.getSpans(0, s.length(), Object.class);
    103             final int destLen = builder.length();
    104             for (int i = 0; i < spans.length; i++) {
    105                 final Object span = spans[i];
    106                 final int start = s.getSpanStart(span);
    107                 final int end = s.getSpanEnd(span);
    108                 final int flags = s.getSpanFlags(span);
    109                 if (start <= destLen) {
    110                     builder.setSpan(span, start, Math.min(end, destLen), flags);
    111                 }
    112             }
    113         }
    114 
    115         setText(builder);
    116 
    117         return builder;
    118     }
    119 }