• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
EllipsizedMultilineTextView(Context context)39     public EllipsizedMultilineTextView(Context context) {
40         this(context, null);
41     }
42 
EllipsizedMultilineTextView(Context context, AttributeSet attrs)43     public EllipsizedMultilineTextView(Context context, AttributeSet attrs) {
44         super(context, attrs);
45     }
46 
47     @Override
setMaxLines(int maxlines)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      */
setText(final CharSequence text, int avail)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 }