├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── cn │ │ └── dreamtobe │ │ └── emoji │ │ └── ellipsize │ │ └── ApplicationTest.java │ └── main │ ├── AndroidManifest.xml │ ├── java │ └── cn │ │ └── dreamtobe │ │ └── emoji │ │ └── ellipsize │ │ ├── MainActivity.java │ │ └── helper │ │ └── SpanEllipsizeEndHelper.java │ └── res │ ├── drawable-nodpi │ ├── emoji_0.png │ ├── emoji_1.png │ ├── emoji_10.png │ ├── emoji_11.png │ ├── emoji_12.png │ ├── emoji_13.png │ ├── emoji_14.png │ ├── emoji_15.png │ ├── emoji_16.png │ ├── emoji_17.png │ ├── emoji_18.png │ ├── emoji_19.png │ ├── emoji_2.png │ ├── emoji_20.png │ ├── emoji_3.png │ ├── emoji_4.png │ ├── emoji_5.png │ ├── emoji_6.png │ ├── emoji_7.png │ ├── emoji_8.png │ └── emoji_9.png │ ├── layout │ └── activity_main.xml │ ├── menu │ └── menu_main.xml │ ├── mipmap-hdpi │ └── ic_launcher.png │ ├── mipmap-mdpi │ └── ic_launcher.png │ ├── mipmap-xhdpi │ └── ic_launcher.png │ ├── mipmap-xxhdpi │ ├── ic_launcher.png │ └── spandrawable.png │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradlew ├── gradlew.bat ├── imgs ├── algorithm.jpg └── demo.jpg └── settings.gradle /README.md: -------------------------------------------------------------------------------- 1 | # Span省略号处理 2 | 3 | > 由于Android TextView最大宽度尾部省略号的场景,在内容含有Span的时候,并未对Span处理,导致达到最大宽度尾部无省略号,甚至Span绘制不完全等异常现象,就该问题进行处理。 4 | 5 | 6 | ## 伤心的故事 7 | 8 | 虽然我发现包括,手Q、脉脉、网易云音乐等在这块处理都有问题,或者是都没有处理吧。。。因此欣然的写了这个算法。抱着造福一方的心态开源。。 9 | 10 | 最终的解决是猛然回头发现`android.text.TextUtils#ellipsize(...):CharSequence`已经很好的封装了类似的功能。 11 | 12 | #### 看图理解 13 | 14 | > 还是没有明白解决啥问题?不多废话,上图! 15 | 16 | ![](https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/master/imgs/demo.jpg) 17 | 18 | 19 | ## I. 基本算法 20 | 21 | ![](https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/master/imgs/algorithm.jpg) 22 | 23 | ## II. 使用方法 24 | 25 | 简单如下: 26 | 27 | ``` 28 | mDemoTv.setText(SpanEllipsizeEndHelper.matchMaxWidth(demoSS, mDemoTv)); 29 | ``` 30 | 31 | ## III. License 32 | 33 | ``` 34 | Copyright (c) 2015 LingoChamp Inc. 35 | 36 | Licensed under the Apache License, Version 2.0 (the "License"); 37 | you may not use this file except in compliance with the License. 38 | You may obtain a copy of the License at 39 | 40 | http://www.apache.org/licenses/LICENSE-2.0 41 | 42 | Unless required by applicable law or agreed to in writing, software 43 | distributed under the License is distributed on an "AS IS" BASIS, 44 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 45 | See the License for the specific language governing permissions and 46 | limitations under the License. 47 | ``` 48 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 22 5 | buildToolsVersion "22.0.1" 6 | 7 | defaultConfig { 8 | applicationId "cn.dreamtobe.emoji.ellipsize" 9 | minSdkVersion 15 10 | targetSdkVersion 22 11 | versionCode 1 12 | versionName "1.0" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(dir: 'libs', include: ['*.jar']) 24 | compile 'com.android.support:appcompat-v7:22.2.1' 25 | } 26 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/Jacksgong/Develop/sdk/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/cn/dreamtobe/emoji/ellipsize/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package cn.dreamtobe.emoji.ellipsize; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /app/src/main/java/cn/dreamtobe/emoji/ellipsize/MainActivity.java: -------------------------------------------------------------------------------- 1 | package cn.dreamtobe.emoji.ellipsize; 2 | 3 | import android.graphics.drawable.Drawable; 4 | import android.os.Bundle; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.text.SpannableString; 7 | import android.text.Spanned; 8 | import android.text.TextUtils; 9 | import android.text.style.DynamicDrawableSpan; 10 | import android.text.style.ImageSpan; 11 | import android.view.View; 12 | import android.widget.SeekBar; 13 | import android.widget.TextView; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | import java.util.Random; 18 | 19 | import cn.dreamtobe.emoji.ellipsize.helper.SpanEllipsizeEndHelper; 20 | 21 | public class MainActivity extends AppCompatActivity { 22 | 23 | private final static String demoText = "一二34567892abcde3⑤4⑥七89123四五五六七3829八九十六7891七八九⑥⑤eddsll"; 24 | 25 | @Override 26 | protected void onCreate(Bundle savedInstanceState) { 27 | super.onCreate(savedInstanceState); 28 | setContentView(R.layout.activity_main); 29 | 30 | // Drawable drawable = getDrawable(R.drawable.ic_l) 31 | // SpannableString demoS = new SpannableString(String.format("", )) 32 | 33 | assignViews(); 34 | 35 | } 36 | 37 | private SeekBar mSpansizeSeekbar; 38 | private TextView mOriginTv; 39 | private TextView mNormalTv; 40 | private TextView mDemoTv; 41 | 42 | private void assignViews() { 43 | mSpansizeSeekbar = (SeekBar) findViewById(R.id.spansize_seekbar); 44 | mOriginTv = (TextView) findViewById(R.id.origin_tv); 45 | mNormalTv = (TextView) findViewById(R.id.normal_tv); 46 | mDemoTv = (TextView) findViewById(R.id.demo_tv); 47 | } 48 | 49 | 50 | private final static int MAX_SPAN_SIZE = 4; 51 | private final static int MAX_SPAN_NUMS = demoText.length() / MAX_SPAN_SIZE; 52 | private final static int MIN_SPAN_NUMS = 3; 53 | 54 | public void onClickRandom(final View view) { 55 | final float percent = (float) mSpansizeSeekbar.getProgress() / (float) mSpansizeSeekbar.getMax(); 56 | 57 | SpannableString demoSS = new SpannableString(demoText); 58 | 59 | 60 | int spanNums = (int) (percent * MAX_SPAN_NUMS); 61 | spanNums = Math.max(spanNums, MIN_SPAN_NUMS); 62 | Random random = new Random(System.currentTimeMillis()); 63 | 64 | final List spans = new ArrayList<>(); 65 | 66 | while (spanNums > 0) { 67 | int spanSize = random.nextInt(MAX_SPAN_SIZE - 1) + 1; 68 | 69 | int start = createStart(spanSize); 70 | int end = start + spanSize; 71 | 72 | 73 | boolean isAvaliable = true; 74 | for (Span span : spans) { 75 | if (span.start <= start && span.end >= end) { 76 | isAvaliable = false; 77 | break; 78 | } 79 | 80 | if (span.start <= start && span.end > start && span.end <= end) { 81 | isAvaliable = false; 82 | break; 83 | } 84 | 85 | if (span.start >= start && span.start < end && span.end >= end) { 86 | isAvaliable = false; 87 | break; 88 | } 89 | } 90 | 91 | 92 | if (isAvaliable) { 93 | int drawableResIndex = random.nextInt(19); 94 | int drawableResId = getResources().getIdentifier("emoji_" + drawableResIndex, "drawable", "cn.dreamtobe.emoji.ellipsize"); 95 | Drawable emojiDra = getResources().getDrawable(drawableResId); 96 | emojiDra.setBounds(0, 0, (int) (mDemoTv.getTextSize() * 1.3f), (int) (mDemoTv.getTextSize() * 1.3f)); 97 | ImageSpan imageSpan = new ImageSpan(emojiDra, DynamicDrawableSpan.ALIGN_BOTTOM); 98 | 99 | Span span = new Span(); 100 | span.start = start; 101 | span.end = end; 102 | spans.add(span); 103 | 104 | demoSS.setSpan(imageSpan, start, end, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE); 105 | spanNums--; 106 | } 107 | 108 | } 109 | 110 | 111 | // complete random Span 112 | mOriginTv.setText(demoSS); 113 | mNormalTv.setText(demoSS); 114 | mDemoTv.setText(SpanEllipsizeEndHelper.matchMaxWidth(demoSS, mDemoTv)); 115 | // long start = System.currentTimeMillis(); 116 | // CharSequence s = TextUtils.ellipsize(demoSS, mDemoTv.getPaint(), mDemoTv.getMaxWidth(), TextUtils.TruncateAt.END); 117 | // Log.d("SpanEllipsizeEnd", String.valueOf(System.currentTimeMillis() - start)); 118 | // mDemoTv.setText(s); 119 | } 120 | 121 | private int createStart(final int spanSize) { 122 | return new Random(System.currentTimeMillis()).nextInt(demoText.length() - spanSize); 123 | } 124 | 125 | private class Span { 126 | private int start; 127 | private int end; 128 | } 129 | } 130 | 131 | -------------------------------------------------------------------------------- /app/src/main/java/cn/dreamtobe/emoji/ellipsize/helper/SpanEllipsizeEndHelper.java: -------------------------------------------------------------------------------- 1 | package cn.dreamtobe.emoji.ellipsize.helper; 2 | 3 | import android.annotation.TargetApi; 4 | import android.os.Build; 5 | import android.support.v4.util.LruCache; 6 | import android.text.Spannable; 7 | import android.text.SpannableString; 8 | import android.text.TextPaint; 9 | import android.text.TextUtils; 10 | import android.text.style.ClickableSpan; 11 | import android.text.style.ImageSpan; 12 | import android.util.Log; 13 | import android.widget.TextView; 14 | 15 | /** 16 | * Copyright (c) 2015 LingoChamp Inc. 17 | *

18 | * Licensed under the Apache License, Version 2.0 (the "License"); 19 | * you may not use this file except in compliance with the License. 20 | * You may obtain a copy of the License at 21 | *

22 | * http://www.apache.org/licenses/LICENSE-2.0 23 | *

24 | * Unless required by applicable law or agreed to in writing, software 25 | * distributed under the License is distributed on an "AS IS" BASIS, 26 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 27 | * See the License for the specific language governing permissions and 28 | * limitations under the License. 29 | *

30 | * Created by Jacksgong on 9/16/15. 31 | *

32 | * 33 | * README: https://github.com/lingochamp/SpanEllipsizeEnd 34 | */ 35 | public class SpanEllipsizeEndHelper { 36 | 37 | private static final String TAG = "SpanEllipsizeEnd"; 38 | private static final int SPANNABLE_MAXWIDTH_CACHE_SIZE = 100; 39 | private static final LruCache SPAN_MAXWIDTH_CACHE = new LruCache<>(SPANNABLE_MAXWIDTH_CACHE_SIZE); 40 | 41 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 42 | private static String getMaxWidthKey(CharSequence targetText, TextView textView) { 43 | String key = String.format("%s@%d@%d", targetText, textView.getMaxWidth(), (int) textView.getTextSize()); 44 | if (targetText instanceof SpannableString) { 45 | Object[] spans = ((SpannableString) targetText).getSpans(0, targetText.length(), Object.class); 46 | for (Object span : spans) { 47 | key += ((SpannableString) targetText).getSpanStart(span); 48 | key += ((SpannableString) targetText).getSpanEnd(span); 49 | } 50 | } 51 | 52 | return key; 53 | } 54 | 55 | @TargetApi(Build.VERSION_CODES.JELLY_BEAN) 56 | public static CharSequence matchMaxWidth(SpannableString targetText, TextView textView) { 57 | if (targetText.length() <= 0) { 58 | return targetText; 59 | } 60 | 61 | if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { 62 | return targetText; 63 | } 64 | 65 | if (textView == null) { 66 | return targetText; 67 | } 68 | 69 | final int maxWidth = textView.getMaxWidth(); 70 | 71 | if (maxWidth <= 0 || maxWidth >= Integer.MAX_VALUE) { 72 | return targetText; 73 | } 74 | 75 | 76 | if (textView.getEllipsize() != TextUtils.TruncateAt.END) { 77 | return targetText; 78 | } 79 | 80 | //TODO Multi-lines support 81 | if (textView.getMaxLines() != 1) { 82 | return targetText; 83 | } 84 | 85 | final String maxWidthKey = getMaxWidthKey(targetText, textView); 86 | SpannableString tmpText = SPAN_MAXWIDTH_CACHE.get(maxWidthKey); 87 | if (tmpText != null) { 88 | removeClickableSpan(tmpText); 89 | return tmpText; 90 | } 91 | 92 | TextPaint textPaint = textView.getPaint(); 93 | if (textPaint == null) { 94 | return targetText; 95 | } 96 | 97 | final int totalWidth = (int) textPaint.measureText(targetText, 0, targetText.length()); 98 | if (totalWidth <= maxWidth) { 99 | return targetText; 100 | } 101 | 102 | final long startTime = System.currentTimeMillis(); 103 | // deal maxwitdh 104 | 105 | final int dotWidth = (int) textPaint.measureText("..."); 106 | 107 | tmpText = targetText; 108 | 109 | 110 | int start = 0; 111 | int end = targetText.length(); 112 | 113 | // targetX is maxWidth - "...".length 114 | int targetX = maxWidth - dotWidth; 115 | 116 | //dichotomy: get x most touch targetX 117 | int middle = targetText.length(); 118 | int x = 0; 119 | while (start <= end) { 120 | // tx = targetX, tl = targetLength 121 | 122 | // width: 0 x 123 | // length: 0 middle end 124 | // -------------|------------- 125 | middle = (start + end) / 2; 126 | 127 | 128 | int emojiDraW = 0; 129 | int emojiStrW = 0; 130 | 131 | 132 | int emojiExcursion = 1; 133 | 134 | final Object[] tmpSpans = tmpText.getSpans(0, middle, Object.class); 135 | if (tmpSpans != null) { 136 | for (Object tmpSpan : tmpSpans) { 137 | final int tmpStart = tmpText.getSpanStart(tmpSpan); 138 | final int tmpEnd = tmpText.getSpanEnd(tmpSpan); 139 | 140 | //middle in (tmpStart, tmpEnd) 141 | if (tmpStart < middle && tmpEnd > middle) { 142 | middle = tmpEnd; 143 | emojiExcursion = tmpEnd - tmpStart; 144 | } 145 | } 146 | 147 | // TextPaint#measure do not attention span, so adjust by ourselves 148 | for (Object tmpSpan : tmpSpans) { 149 | final int tmpStart = tmpText.getSpanStart(tmpSpan); 150 | final int tmpEnd = tmpText.getSpanEnd(tmpSpan); 151 | 152 | // TODO support other span 153 | if (tmpStart < middle && tmpSpan instanceof ImageSpan) { 154 | emojiDraW += ((ImageSpan) tmpSpan).getDrawable().getBounds().width(); 155 | emojiStrW += textPaint.measureText(tmpText, tmpStart, tmpEnd); 156 | } 157 | } 158 | 159 | } 160 | 161 | x = (int) textPaint.measureText(tmpText, 0, middle); 162 | x = x - emojiStrW + emojiDraW; 163 | 164 | // x = (int) (textPaint.measureText(pureStr, 0, pureStr.length()) + emojiWidth); 165 | 166 | // Log.d(TAG, String.format("targetX: %d, currentX: %d, currentLength: %d, totalLength: %d, emojiStrW[%d], emojiDraW[%d]", targetX, x, middle, targetText.length(), emojiStrW, emojiDraW)); 167 | 168 | if (x > targetX) { 169 | // width: 0 tx x 170 | // length: start tl middle end 171 | // ----|---------|------------- 172 | // TO: start | *end 173 | // ----|--------|-------------- 174 | end = middle - emojiExcursion; 175 | } else if (x < targetX) { 176 | // width: 0 x tx 177 | // length: start middle tl end 178 | // --------------|-------|------ 179 | // TO: *start | end 180 | // ---------------|------|------ 181 | start = middle + 1; 182 | } else { 183 | break; 184 | } 185 | } 186 | 187 | // adjust x larger targetX 188 | while (x > targetX && middle > 0) { 189 | x = (int) textPaint.measureText(tmpText, 0, --middle); 190 | } 191 | 192 | // adjust x middle emoji span 193 | final Object[] ajustSpans = tmpText.getSpans(0, tmpText.length(), Object.class); 194 | for (Object adjustSpan : ajustSpans) { 195 | final int adjustStart = tmpText.getSpanStart(adjustSpan); 196 | final int adjustEnd = tmpText.getSpanEnd(adjustSpan); 197 | 198 | //[adjustStart, adjustEnd) 199 | if (middle >= adjustStart && middle < adjustEnd) { 200 | middle = adjustStart - 1; 201 | break; 202 | } 203 | } 204 | 205 | // finnal middle 206 | 207 | // sub sequence [0, middle + 1) & remove [middle +1, length] spans 208 | tmpText = (SpannableString) tmpText.subSequence(0, middle + 1); 209 | // Log.d(TAG, String.format("sub Sequence[0, %d), [%s] to [%s]", middle + 1, targetText, tmpText)); 210 | 211 | // add ... 212 | final SpannableString maxWidthSS = new SpannableString(tmpText + "..."); 213 | 214 | final Object[] maxWidthSpans = tmpText.getSpans(0, tmpText.length(), Object.class); 215 | if (maxWidthSpans != null) { 216 | for (Object maxWidthSpan : maxWidthSpans) { 217 | final int mwSpanStart = tmpText.getSpanStart(maxWidthSpan); 218 | final int mwSpanEnd = tmpText.getSpanEnd(maxWidthSpan); 219 | final int mwSpanFlag = tmpText.getSpanFlags(maxWidth); 220 | 221 | maxWidthSS.setSpan(maxWidthSpan, mwSpanStart, mwSpanEnd, mwSpanFlag); 222 | } 223 | } 224 | 225 | 226 | targetText = maxWidthSS; 227 | 228 | SPAN_MAXWIDTH_CACHE.put(maxWidthKey, targetText); 229 | Log.d(TAG, String.format("deal maxWidth %d", System.currentTimeMillis() - startTime)); 230 | 231 | return targetText; 232 | } 233 | 234 | private static void removeClickableSpan(Spannable content) { 235 | ClickableSpan[] clickSpans = content.getSpans(0, content.length(), ClickableSpan.class); 236 | for (int i = 0; i < clickSpans.length; i++) { 237 | content.removeSpan(clickSpans[i]); 238 | } 239 | } 240 | 241 | 242 | } 243 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_10.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_11.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_12.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_13.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_14.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_15.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_16.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_17.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_17.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_18.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_18.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_19.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_19.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_20.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_4.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_5.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_6.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_7.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_8.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/emoji_9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lingochamp/SpanEllipsizeEnd/4ab9f0f90f96d287d481afa5b38038d06ea895b5/app/src/main/res/drawable-nodpi/emoji_9.png -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_main.xml: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | 19 | 25 | 26 |