├── .gitignore ├── LICENSE ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ ├── Nutso2.otf │ ├── Ruthie.ttf │ └── icomoon.ttf │ ├── java │ └── com │ │ └── sqisland │ │ └── android │ │ └── advanced_textview │ │ ├── AboutActivity.java │ │ ├── AlignmentSpanActivity.java │ │ ├── AnimatedCompoundDrawableActivity.java │ │ ├── AnimatedRainbowSpanActivity.java │ │ ├── ClickableSpanActivity.java │ │ ├── CustomFontActivity.java │ │ ├── EmojiActivity.java │ │ ├── FractionActivity.java │ │ ├── FromHtmlActivity.java │ │ ├── GradientTextActivity.java │ │ ├── LinedPaperActivity.java │ │ ├── MainActivity.java │ │ ├── NonBreakingSpaceActivity.java │ │ ├── PatternedTextActivity.java │ │ ├── RainbowSpanActivity.java │ │ ├── ShadowTextActivity.java │ │ ├── StyledStringActivity.java │ │ └── widget │ │ └── LinedEditText.java │ └── res │ ├── anim-v21 │ ├── hours_rotation.xml │ └── minutes_rotation.xml │ ├── color │ └── go_to_settings.xml │ ├── drawable-hdpi │ ├── cheetah_tile.png │ ├── ic_launcher.png │ ├── ic_loading.png │ ├── ic_wifi_0.png │ ├── ic_wifi_1.png │ ├── ic_wifi_2.png │ └── ic_wifi_3.png │ ├── drawable-mdpi │ ├── cheetah_tile.png │ ├── ic_launcher.png │ ├── ic_loading.png │ ├── ic_wifi_0.png │ ├── ic_wifi_1.png │ ├── ic_wifi_2.png │ └── ic_wifi_3.png │ ├── drawable-nodpi │ └── octopus.png │ ├── drawable-v21 │ ├── animated_clock.xml │ └── clock.xml │ ├── drawable-xhdpi │ ├── cheetah_tile.png │ ├── ic_launcher.png │ ├── ic_loading.png │ ├── ic_wifi_0.png │ ├── ic_wifi_1.png │ ├── ic_wifi_2.png │ └── ic_wifi_3.png │ ├── drawable-xxhdpi │ ├── cheetah_tile.png │ ├── ic_launcher.png │ ├── ic_loading.png │ ├── ic_wifi_0.png │ ├── ic_wifi_1.png │ ├── ic_wifi_2.png │ └── ic_wifi_3.png │ ├── drawable-xxxhdpi │ ├── cheetah_tile.png │ └── ic_launcher.png │ ├── drawable │ ├── animated_wifi.xml │ └── rotating_loading.xml │ ├── layout-v21 │ └── activity_animated_compound_drawable.xml │ ├── layout │ ├── activity_about.xml │ ├── activity_alignment_span.xml │ ├── activity_animated_compound_drawable.xml │ ├── activity_animated_rainbow_span.xml │ ├── activity_clickable_span.xml │ ├── activity_custom_font.xml │ ├── activity_emoji.xml │ ├── activity_fraction.xml │ ├── activity_from_html.xml │ ├── activity_gradient_text.xml │ ├── activity_lined_paper.xml │ ├── activity_non_breaking_space.xml │ ├── activity_patterned_text.xml │ ├── activity_rainbow_span.xml │ ├── activity_shadow_text.xml │ └── activity_styled_string.xml │ ├── values-v11 │ └── styles.xml │ ├── values-v14 │ └── styles.xml │ ├── values-v21 │ ├── strings.xml │ └── styles.xml │ ├── values-w820dp │ └── dimens.xml │ └── values │ ├── colors.xml │ ├── dimens.xml │ ├── strings.xml │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea/ 3 | .gradle 4 | local.properties 5 | /*/out 6 | build 7 | *.iml 8 | *.iws 9 | *.ipr 10 | *.swp 11 | *.apk 12 | *.ap_ 13 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright 2014 Chiu-Ki Chan 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Advanced Android TextView 2 | ------------------------- 3 | 4 | Companion app for my [Advanced Android TextView talk][1], demostrating: 5 | 6 | * Animated `CompoundDrawable` 7 | * Text shadow 8 | * Custom font 9 | * Non-breaking space 10 | * Gradient text 11 | * Patterned text 12 | * `HTML.fromHtml()` 13 | * `setFontFeatureSettings()` 14 | * Styled string 15 | * `AlignmentSpan` 16 | * `ClickableSpan` 17 | * Rainbow span, animated 18 | * Lined paper 19 | * Emoji 20 | 21 | [1]: http://chiuki.github.io/advanced-android-textview 22 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion '28.0.3' 6 | 7 | defaultConfig { 8 | applicationId "com.sqisland.android.advanced_textview" 9 | minSdkVersion 4 10 | targetSdkVersion 28 11 | versionCode 3 12 | versionName '1.2.0' 13 | } 14 | 15 | buildTypes { 16 | release { 17 | minifyEnabled false 18 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 19 | } 20 | } 21 | } 22 | 23 | dependencies { 24 | implementation fileTree(dir: 'libs', include: ['*.jar']) 25 | implementation 'com.nineoldandroids:library:2.4.0' 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 /home/chiuki/dev/android-studio/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/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 10 | 13 | 14 | 15 | 16 | 17 | 18 | 22 | 26 | 30 | 34 | 38 | 42 | 46 | 50 | 54 | 58 | 63 | 67 | 71 | 75 | 79 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /app/src/main/assets/Nutso2.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/assets/Nutso2.otf -------------------------------------------------------------------------------- /app/src/main/assets/Ruthie.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/assets/Ruthie.ttf -------------------------------------------------------------------------------- /app/src/main/assets/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/assets/icomoon.ttf -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/AboutActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.content.pm.PackageManager; 7 | import android.content.pm.ResolveInfo; 8 | import android.net.Uri; 9 | import android.os.Bundle; 10 | import android.text.Html; 11 | import android.text.method.LinkMovementMethod; 12 | import android.widget.TextView; 13 | 14 | import java.util.List; 15 | 16 | public class AboutActivity extends Activity { 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_about); 21 | 22 | TextView textView = (TextView) findViewById(R.id.text); 23 | CharSequence html = Html.fromHtml(getString(R.string.about_text)); 24 | 25 | if (canOpenLinks()) { 26 | textView.setText(html); 27 | textView.setMovementMethod(LinkMovementMethod.getInstance()); 28 | } else { 29 | textView.setText(html.toString()); 30 | } 31 | } 32 | 33 | public boolean canOpenLinks() { 34 | Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse("http://d.android.com")); 35 | final PackageManager packageManager = getPackageManager(); 36 | List list = packageManager.queryIntentActivities( 37 | intent, PackageManager.MATCH_DEFAULT_ONLY); 38 | return list.size() > 0; 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/AlignmentSpanActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.text.Layout; 7 | import android.text.SpannableString; 8 | import android.text.style.AlignmentSpan; 9 | import android.view.View; 10 | import android.view.inputmethod.InputMethodManager; 11 | import android.widget.ScrollView; 12 | import android.widget.TextView; 13 | 14 | public class AlignmentSpanActivity extends Activity { 15 | private ScrollView scroll; 16 | private TextView textView; 17 | private TextView input; 18 | 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_alignment_span); 23 | 24 | scroll = (ScrollView) findViewById(R.id.scroll); 25 | textView = (TextView) findViewById(R.id.text); 26 | input = (TextView) findViewById(R.id.input); 27 | 28 | for (int id : new int[] { R.id.add_to_left_button, R.id.add_to_right_button }) { 29 | View button = findViewById(id); 30 | button.setOnClickListener(new View.OnClickListener() { 31 | @Override 32 | public void onClick(View view) { 33 | // input.getText() returns a CharSequence, which contains formatting such as underline 34 | // when the text contains a typo. Use toString() to get the plain text. 35 | String text = input.getText().toString(); 36 | appendText(text, view.getId() == R.id.add_to_right_button ? 37 | Layout.Alignment.ALIGN_OPPOSITE : Layout.Alignment.ALIGN_NORMAL); 38 | 39 | hideSoftKeyboard(); 40 | input.setText(null); 41 | scroll.fullScroll(View.FOCUS_DOWN); 42 | } 43 | }); 44 | } 45 | 46 | appendText(getString(R.string.alignment_span_line_1), Layout.Alignment.ALIGN_NORMAL); 47 | appendText(getString(R.string.alignment_span_line_2), Layout.Alignment.ALIGN_OPPOSITE); 48 | } 49 | 50 | private void appendText(CharSequence text, Layout.Alignment align) { 51 | if (text == null || text.toString().trim().length() == 0) { 52 | return; 53 | } 54 | 55 | AlignmentSpan span = new AlignmentSpan.Standard(align); 56 | SpannableString spannableString = new SpannableString(text); 57 | spannableString.setSpan(span, 0, text.length(), 0); 58 | 59 | if (textView.length() > 0) { 60 | textView.append("\n\n"); 61 | } 62 | textView.append(spannableString); 63 | } 64 | 65 | private void hideSoftKeyboard() { 66 | InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE); 67 | imm.hideSoftInputFromWindow(input.getWindowToken(), 0); 68 | } 69 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/AnimatedCompoundDrawableActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.graphics.drawable.Animatable; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Bundle; 7 | import android.widget.TextView; 8 | 9 | public class AnimatedCompoundDrawableActivity extends Activity { 10 | private TextView textView; 11 | 12 | private enum Operation { 13 | START, STOP 14 | } 15 | 16 | @Override 17 | protected void onCreate(Bundle savedInstanceState) { 18 | super.onCreate(savedInstanceState); 19 | setContentView(R.layout.activity_animated_compound_drawable); 20 | textView = (TextView) findViewById(R.id.text); 21 | } 22 | 23 | @Override 24 | protected void onStart() { 25 | super.onStart(); 26 | changeAnimation(Operation.START); 27 | } 28 | 29 | @Override 30 | protected void onStop() { 31 | super.onStop(); 32 | changeAnimation(Operation.STOP); 33 | } 34 | 35 | private void changeAnimation(Operation operation) { 36 | Drawable[] drawables = textView.getCompoundDrawables(); 37 | for (Drawable drawable : drawables) { 38 | if (drawable != null && drawable instanceof Animatable) { 39 | Animatable animatable = ((Animatable) drawable); 40 | switch (operation) { 41 | case START: 42 | animatable.start(); 43 | break; 44 | case STOP: 45 | animatable.stop(); 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/AnimatedRainbowSpanActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.LinearGradient; 6 | import android.graphics.Matrix; 7 | import android.graphics.Paint; 8 | import android.graphics.Shader; 9 | import android.os.Bundle; 10 | import android.text.SpannableString; 11 | import android.text.TextPaint; 12 | import android.text.format.DateUtils; 13 | import android.text.style.CharacterStyle; 14 | import android.text.style.UpdateAppearance; 15 | import android.view.animation.LinearInterpolator; 16 | import android.widget.TextView; 17 | 18 | import com.nineoldandroids.animation.FloatEvaluator; 19 | import com.nineoldandroids.animation.ObjectAnimator; 20 | import com.nineoldandroids.animation.ValueAnimator; 21 | import com.nineoldandroids.util.Property; 22 | 23 | public class AnimatedRainbowSpanActivity extends Activity { 24 | @Override 25 | protected void onCreate(Bundle savedInstanceState) { 26 | super.onCreate(savedInstanceState); 27 | setContentView(R.layout.activity_animated_rainbow_span); 28 | 29 | final TextView textView = (TextView) findViewById(R.id.text); 30 | String text = textView.getText().toString(); 31 | 32 | AnimatedColorSpan span = new AnimatedColorSpan(this); 33 | 34 | final SpannableString spannableString = new SpannableString(text); 35 | String substring = getString(R.string.animated_rainbow_span).toLowerCase(); 36 | int start = text.toLowerCase().indexOf(substring); 37 | int end = start + substring.length(); 38 | spannableString.setSpan(span, start, end, 0); 39 | 40 | ObjectAnimator objectAnimator = ObjectAnimator.ofFloat( 41 | span, ANIMATED_COLOR_SPAN_FLOAT_PROPERTY, 0, 100); 42 | objectAnimator.setEvaluator(new FloatEvaluator()); 43 | objectAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { 44 | @Override 45 | public void onAnimationUpdate(ValueAnimator animation) { 46 | textView.setText(spannableString); 47 | } 48 | }); 49 | objectAnimator.setInterpolator(new LinearInterpolator()); 50 | objectAnimator.setDuration(DateUtils.MINUTE_IN_MILLIS * 3); 51 | objectAnimator.setRepeatCount(ValueAnimator.INFINITE); 52 | objectAnimator.start(); 53 | } 54 | 55 | private static final Property ANIMATED_COLOR_SPAN_FLOAT_PROPERTY 56 | = new Property(Float.class, "ANIMATED_COLOR_SPAN_FLOAT_PROPERTY") { 57 | @Override 58 | public void set(AnimatedColorSpan span, Float value) { 59 | span.setTranslateXPercentage(value); 60 | } 61 | @Override 62 | public Float get(AnimatedColorSpan span) { 63 | return span.getTranslateXPercentage(); 64 | } 65 | }; 66 | 67 | private static class AnimatedColorSpan extends CharacterStyle implements UpdateAppearance { 68 | private final int[] colors; 69 | private Shader shader = null; 70 | private Matrix matrix = new Matrix(); 71 | private float translateXPercentage = 0; 72 | 73 | public AnimatedColorSpan(Context context) { 74 | colors = context.getResources().getIntArray(R.array.rainbow); 75 | } 76 | 77 | public void setTranslateXPercentage(float percentage) { 78 | translateXPercentage = percentage; 79 | } 80 | 81 | public float getTranslateXPercentage() { 82 | return translateXPercentage; 83 | } 84 | 85 | @Override 86 | public void updateDrawState(TextPaint paint) { 87 | paint.setStyle(Paint.Style.FILL); 88 | float width = paint.getTextSize() * colors.length; 89 | if (shader == null) { 90 | shader = new LinearGradient(0, 0, 0, width, colors, null, 91 | Shader.TileMode.MIRROR); 92 | } 93 | matrix.reset(); 94 | matrix.setRotate(90); 95 | matrix.postTranslate(width * translateXPercentage, 0); 96 | shader.setLocalMatrix(matrix); 97 | paint.setShader(shader); 98 | } 99 | } 100 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/ClickableSpanActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Intent; 5 | import android.os.Bundle; 6 | import android.text.SpannableString; 7 | import android.text.method.LinkMovementMethod; 8 | import android.text.style.ClickableSpan; 9 | import android.view.View; 10 | import android.widget.TextView; 11 | 12 | public class ClickableSpanActivity extends Activity { 13 | @Override 14 | protected void onCreate(Bundle savedInstanceState) { 15 | super.onCreate(savedInstanceState); 16 | setContentView(R.layout.activity_clickable_span); 17 | 18 | TextView textView = (TextView) findViewById(R.id.text); 19 | String text = textView.getText().toString(); 20 | 21 | String goToSettings = getString(R.string.go_to_settings); 22 | int start = text.indexOf(goToSettings); 23 | int end = start + goToSettings.length(); 24 | 25 | SpannableString spannableString = new SpannableString(text); 26 | spannableString.setSpan(new GoToSettingsSpan(), start, end, 0); 27 | textView.setText(spannableString); 28 | 29 | textView.setMovementMethod(new LinkMovementMethod()); 30 | } 31 | 32 | private static class GoToSettingsSpan extends ClickableSpan { 33 | @Override 34 | public void onClick(View view) { 35 | view.getContext().startActivity(new Intent(android.provider.Settings.ACTION_SETTINGS)); 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/CustomFontActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Typeface; 6 | import android.os.Bundle; 7 | import android.widget.TextView; 8 | import android.view.View; 9 | import android.util.AttributeSet; 10 | 11 | public class CustomFontActivity extends Activity { 12 | 13 | private Typeface typeface; 14 | 15 | @Override 16 | protected void onCreate(Bundle savedInstanceState) { 17 | super.onCreate(savedInstanceState); 18 | typeface = Typeface.createFromAsset(getAssets(), "Ruthie.ttf"); 19 | setContentView(R.layout.activity_custom_font); 20 | } 21 | 22 | @Override 23 | public View onCreateView(View parent, String name, Context context, AttributeSet attrs) { 24 | //this would apply to all textviews in the app 25 | if (name.equals("TextView")) { 26 | TextView view = new TextView(this, attrs); 27 | view.setTypeface(typeface); 28 | return view; 29 | } 30 | 31 | return super.onCreateView(parent, name, context, attrs); 32 | } 33 | 34 | @Override 35 | public View onCreateView(String name, Context context, AttributeSet attrs) { 36 | //this would apply to all textviews in the app 37 | if (name.equals("TextView")) { 38 | TextView view = new TextView(this, attrs); 39 | view.setTypeface(typeface); 40 | return view; 41 | } 42 | 43 | return super.onCreateView(name, context, attrs); 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/EmojiActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.Bitmap; 6 | import android.graphics.BitmapFactory; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.ColorFilter; 10 | import android.graphics.Paint; 11 | import android.graphics.Typeface; 12 | import android.graphics.drawable.Drawable; 13 | import android.os.Bundle; 14 | import android.text.SpannableString; 15 | import android.text.TextPaint; 16 | import android.text.style.ForegroundColorSpan; 17 | import android.text.style.ImageSpan; 18 | import android.text.style.MetricAffectingSpan; 19 | import android.view.View; 20 | import android.widget.TextView; 21 | 22 | import java.util.regex.Matcher; 23 | import java.util.regex.Pattern; 24 | 25 | public class EmojiActivity extends Activity { 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_emoji); 30 | 31 | final TextView textView = (TextView) findViewById(R.id.text); 32 | findViewById(R.id.render_emoji_button).setOnClickListener(new View.OnClickListener() { 33 | @Override 34 | public void onClick(View view) { 35 | renderEmoji(textView); 36 | view.setEnabled(false); 37 | } 38 | }); 39 | } 40 | 41 | private void renderEmoji(TextView textView) { 42 | String text = textView.getText().toString(); 43 | SpannableString spannableString = new SpannableString(text); 44 | 45 | // Icon font 46 | Pattern pattern = Pattern.compile("\u26F7"); // skier 47 | Matcher matcher = pattern.matcher(text); 48 | while (matcher.find()) { 49 | ForegroundColorSpan foregroundColorSpan = new ForegroundColorSpan( 50 | getResources().getColor(R.color.blue)); 51 | IconFontSpan iconFontSpan = new IconFontSpan(textView.getContext()); 52 | spannableString.setSpan(iconFontSpan, matcher.start(), matcher.end(), 0); 53 | spannableString.setSpan(foregroundColorSpan, matcher.start(), matcher.end(), 0); 54 | } 55 | 56 | // ImageSpan from resource 57 | pattern = Pattern.compile(":octopus:"); 58 | matcher = pattern.matcher(text); 59 | 60 | Bitmap octopus = null; 61 | int size = (int) (-textView.getPaint().ascent()); 62 | while (matcher.find()) { 63 | if (octopus == null) { 64 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.octopus); 65 | octopus = Bitmap.createScaledBitmap(bitmap, size, size, true); 66 | bitmap.recycle(); 67 | } 68 | ImageSpan span = new ImageSpan(this, octopus, ImageSpan.ALIGN_BASELINE); 69 | spannableString.setSpan(span, matcher.start(), matcher.end(), 0); 70 | } 71 | 72 | // ImageSpan with dynamic drawable 73 | pattern = Pattern.compile(":speed_(\\d\\d\\d?):"); 74 | matcher = pattern.matcher(text); 75 | 76 | while (matcher.find()) { 77 | SpeedSignDrawable drawable = new SpeedSignDrawable(textView, matcher.group(1)); 78 | ImageSpan span = new ImageSpan(drawable, ImageSpan.ALIGN_BASELINE); 79 | spannableString.setSpan(span, matcher.start(), matcher.end(), 0); 80 | } 81 | 82 | textView.setText(spannableString); 83 | } 84 | 85 | private static class IconFontSpan extends MetricAffectingSpan { 86 | private static Typeface typeface = null; 87 | 88 | public IconFontSpan(Context context) { 89 | if (typeface == null) { 90 | typeface = Typeface.createFromAsset( 91 | context.getAssets(), "icomoon.ttf"); 92 | } 93 | } 94 | 95 | @Override 96 | public void updateMeasureState(TextPaint textPaint) { 97 | textPaint.setTypeface(typeface); 98 | } 99 | 100 | @Override 101 | public void updateDrawState(TextPaint textPaint) { 102 | textPaint.setTypeface(typeface); 103 | } 104 | } 105 | 106 | private static class SpeedSignDrawable extends Drawable { 107 | private final float ascent; 108 | private final float descent; 109 | private final float textSize; 110 | private final float strokeWidth; 111 | private final String number; 112 | private Paint paint; 113 | 114 | public SpeedSignDrawable(TextView textView, String number) { 115 | this.ascent = textView.getPaint().ascent(); 116 | this.descent = textView.getPaint().descent(); 117 | this.textSize = textView.getTextSize(); 118 | this.strokeWidth = textView.getPaint().getStrokeWidth(); 119 | 120 | this.number = number; 121 | 122 | int size = (int) -ascent; 123 | this.setBounds(0, 0, size, size); 124 | 125 | this.paint = new Paint(Paint.ANTI_ALIAS_FLAG); 126 | } 127 | 128 | @Override 129 | public void draw(Canvas canvas) { 130 | int size = (int) -ascent; 131 | 132 | // Draw the circle 133 | paint.setStyle(Paint.Style.FILL); 134 | paint.setColor(Color.YELLOW); 135 | canvas.drawCircle(size/2, size/2, size/2 , paint); 136 | 137 | // Draw the ring 138 | paint.setStyle(Paint.Style.STROKE); 139 | paint.setColor(Color.RED); 140 | float ringWidth = size / 10; 141 | paint.setStrokeWidth(ringWidth); 142 | canvas.drawCircle(size/2, size/2, size/2 - ringWidth/2, paint); 143 | 144 | // Draw the text 145 | float ratio = 0.4f; 146 | paint.setStyle(Paint.Style.FILL); 147 | paint.setColor(Color.BLACK); 148 | paint.setTextAlign(Paint.Align.CENTER); 149 | paint.setTextSize(textSize * ratio); 150 | paint.setStrokeWidth(strokeWidth); 151 | paint.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD)); 152 | int x = size / 2; 153 | int y = (int) (size/2 - ((descent + ascent)/2) * ratio); 154 | canvas.drawText(number, x, y, paint); 155 | } 156 | 157 | @Override 158 | public void setAlpha(int alpha) { 159 | } 160 | 161 | @Override 162 | public void setColorFilter(ColorFilter cf) { 163 | } 164 | 165 | @Override 166 | public int getOpacity() { 167 | return 0; 168 | } 169 | } 170 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/FractionActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.annotation.TargetApi; 4 | import android.app.Activity; 5 | import android.graphics.Typeface; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.text.Editable; 9 | import android.text.Html; 10 | import android.text.Spannable; 11 | import android.text.TextPaint; 12 | import android.text.style.MetricAffectingSpan; 13 | import android.widget.TextView; 14 | 15 | import org.xml.sax.XMLReader; 16 | 17 | @TargetApi(Build.VERSION_CODES.LOLLIPOP) 18 | public class FractionActivity extends Activity { 19 | @Override 20 | protected void onCreate(Bundle savedInstanceState) { 21 | super.onCreate(savedInstanceState); 22 | setContentView(R.layout.activity_fraction); 23 | 24 | TextView textView = (TextView) findViewById(R.id.text); 25 | Typeface typeface = Typeface.createFromAsset(getAssets(), "Nutso2.otf"); 26 | textView.setTypeface(typeface); 27 | 28 | String html = getString(R.string.fraction_text); 29 | textView.setText(Html.fromHtml(html, null, new FractionTagHandler())); 30 | } 31 | 32 | // http://stackoverflow.com/questions/4044509/android-how-to-use-the-html-taghandler 33 | private static class FractionTagHandler implements Html.TagHandler { 34 | @Override 35 | public void handleTag(boolean opening, String tag, Editable output, XMLReader xmlReader) { 36 | if (!"afrc".equalsIgnoreCase(tag)) { 37 | return; 38 | } 39 | 40 | int len = output.length(); 41 | if (opening) { 42 | output.setSpan(new FractionSpan(), len, len, Spannable.SPAN_MARK_MARK); 43 | } else { 44 | Object obj = getLast(output, FractionSpan.class); 45 | int where = output.getSpanStart(obj); 46 | 47 | output.removeSpan(obj); 48 | 49 | if (where != len) { 50 | output.setSpan(new FractionSpan(), where, len, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 51 | } 52 | } 53 | } 54 | 55 | private Object getLast(Editable text, Class kind) { 56 | Object[] objs = text.getSpans(0, text.length(), kind); 57 | 58 | if (objs.length == 0) { 59 | return null; 60 | } else { 61 | for (int i = objs.length - 1; i >= 0; --i) { 62 | if(text.getSpanFlags(objs[i]) == Spannable.SPAN_MARK_MARK) { 63 | return objs[i]; 64 | } 65 | } 66 | return null; 67 | } 68 | } 69 | } 70 | 71 | private static class FractionSpan extends MetricAffectingSpan { 72 | private static final String FONT_FEATURE_SETTINGS = "afrc"; 73 | 74 | @Override 75 | public void updateMeasureState(TextPaint textPaint) { 76 | textPaint.setFontFeatureSettings(FONT_FEATURE_SETTINGS); 77 | } 78 | 79 | @Override 80 | public void updateDrawState(TextPaint textPaint) { 81 | textPaint.setFontFeatureSettings(FONT_FEATURE_SETTINGS); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/FromHtmlActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.drawable.Drawable; 6 | import android.os.Bundle; 7 | import android.text.Html; 8 | import android.text.method.LinkMovementMethod; 9 | import android.widget.TextView; 10 | 11 | public class FromHtmlActivity extends Activity { 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_from_html); 16 | 17 | TextView textView = (TextView) findViewById(R.id.text); 18 | String html = getString(R.string.from_html_text); 19 | textView.setText(Html.fromHtml(html, new ResouroceImageGetter(this), null)); 20 | textView.setMovementMethod(LinkMovementMethod.getInstance()); 21 | } 22 | 23 | private static class ResouroceImageGetter implements Html.ImageGetter { 24 | private final Context context; 25 | 26 | public ResouroceImageGetter(Context context) { 27 | this.context = context; 28 | } 29 | 30 | @Override 31 | public Drawable getDrawable(String source) { 32 | int path = context.getResources().getIdentifier(source, "drawable", 33 | BuildConfig.APPLICATION_ID); 34 | Drawable drawable = context.getResources().getDrawable(path); 35 | drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), 36 | drawable.getIntrinsicHeight()); 37 | return drawable; 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/GradientTextActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Color; 5 | import android.graphics.LinearGradient; 6 | import android.graphics.Shader; 7 | import android.os.Bundle; 8 | import android.widget.TextView; 9 | 10 | public class GradientTextActivity extends Activity { 11 | @Override 12 | protected void onCreate(Bundle savedInstanceState) { 13 | super.onCreate(savedInstanceState); 14 | setContentView(R.layout.activity_gradient_text); 15 | 16 | TextView textView = (TextView) findViewById(R.id.text); 17 | Shader shader = new LinearGradient(0, 0, 0, textView.getTextSize(), Color.RED, Color.BLUE, 18 | Shader.TileMode.CLAMP); 19 | textView.getPaint().setShader(shader); 20 | } 21 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/LinedPaperActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class LinedPaperActivity extends Activity { 7 | @Override 8 | protected void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | setContentView(R.layout.activity_lined_paper); 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.ListActivity; 4 | import android.content.Context; 5 | import android.content.Intent; 6 | import android.os.Build; 7 | import android.os.Bundle; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.widget.AdapterView; 11 | import android.widget.SimpleAdapter; 12 | import android.widget.TextView; 13 | 14 | import java.util.ArrayList; 15 | import java.util.HashMap; 16 | 17 | public class MainActivity extends ListActivity { 18 | @Override 19 | protected void onCreate(Bundle savedInstanceState) { 20 | super.onCreate(savedInstanceState); 21 | 22 | TextView footer = (TextView) LayoutInflater.from(this).inflate( 23 | android.R.layout.simple_list_item_1, getListView(), false); 24 | footer.setText(R.string.about); 25 | getListView().addFooterView(footer); 26 | 27 | final ArrayList demos = new ArrayList(); 28 | 29 | demos.add(new Demo(this, AnimatedCompoundDrawableActivity.class, 30 | R.string.animated_compound_drawable, R.string.animated_compound_drawable_desc)); 31 | demos.add(new Demo(this, ShadowTextActivity.class, 32 | R.string.shadow_text, R.string.shadow_text_desc)); 33 | demos.add(new Demo(this, CustomFontActivity.class, 34 | R.string.custom_font, R.string.custom_font_desc)); 35 | demos.add(new Demo(this, NonBreakingSpaceActivity.class, 36 | R.string.non_breaking_space, R.string.non_breaking_space_desc)); 37 | demos.add(new Demo(this, GradientTextActivity.class, 38 | R.string.gradient_text, R.string.gradient_text_desc)); 39 | demos.add(new Demo(this, PatternedTextActivity.class, 40 | R.string.patterned_text, R.string.patterned_text_desc)); 41 | demos.add(new Demo(this, FromHtmlActivity.class, 42 | R.string.from_html, R.string.from_html_desc)); 43 | 44 | if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { 45 | demos.add(new Demo(this, FractionActivity.class, 46 | R.string.fraction, R.string.fraction_desc)); 47 | } 48 | 49 | demos.add(new Demo(this, StyledStringActivity.class, 50 | R.string.styled_string, R.string.styled_string_desc)); 51 | demos.add(new Demo(this, AlignmentSpanActivity.class, 52 | R.string.alignment_span, R.string.alignment_span_desc)); 53 | demos.add(new Demo(this, RainbowSpanActivity.class, 54 | R.string.rainbow_span, R.string.rainbow_span_desc)); 55 | demos.add(new Demo(this, AnimatedRainbowSpanActivity.class, 56 | R.string.animated_rainbow_span, R.string.animated_rainbow_span_desc)); 57 | demos.add(new Demo(this, ClickableSpanActivity.class, 58 | R.string.clickable_span, R.string.clickable_span_desc)); 59 | demos.add(new Demo(this, LinedPaperActivity.class, 60 | R.string.lined_paper, R.string.lined_paper_desc)); 61 | demos.add(new Demo(this, EmojiActivity.class, 62 | R.string.emoji, R.string.emoji_desc)); 63 | 64 | SimpleAdapter adapter = new SimpleAdapter( 65 | this, 66 | demos, 67 | android.R.layout.simple_list_item_2, 68 | new String[]{Demo.KEY_TITLE, Demo.KEY_SUBTITLE}, 69 | new int[]{android.R.id.text1, android.R.id.text2}); 70 | getListView().setAdapter(adapter); 71 | 72 | getListView().setOnItemClickListener(new AdapterView.OnItemClickListener() { 73 | @Override 74 | public void onItemClick(AdapterView adapterView, View view, int position, long id) { 75 | if (position < demos.size()) { 76 | Demo demo = demos.get(position); 77 | startActivity(new Intent(MainActivity.this, demo.activityClass)); 78 | } else { 79 | startActivity(new Intent(MainActivity.this, AboutActivity.class)); 80 | } 81 | } 82 | }); 83 | } 84 | 85 | public static class Demo extends HashMap { 86 | public static final String KEY_TITLE = "title"; 87 | public static final String KEY_SUBTITLE = "subtitle"; 88 | 89 | public final Class activityClass; 90 | 91 | public Demo(Context context, Class activityClass, int titleId, int subtitleId) { 92 | this.activityClass = activityClass; 93 | 94 | put(KEY_TITLE, context.getString(titleId)); 95 | put(KEY_SUBTITLE, context.getString(subtitleId)); 96 | } 97 | } 98 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/NonBreakingSpaceActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class NonBreakingSpaceActivity extends Activity { 7 | @Override 8 | protected void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | setContentView(R.layout.activity_non_breaking_space); 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/PatternedTextActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.graphics.Bitmap; 5 | import android.graphics.BitmapFactory; 6 | import android.graphics.BitmapShader; 7 | import android.graphics.Shader; 8 | import android.os.Bundle; 9 | import android.widget.TextView; 10 | 11 | public class PatternedTextActivity extends Activity { 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_patterned_text); 16 | 17 | TextView textView = (TextView) findViewById(R.id.text); 18 | 19 | Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.cheetah_tile); 20 | Shader shader = new BitmapShader(bitmap, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT); 21 | textView.getPaint().setShader(shader); 22 | } 23 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/RainbowSpanActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.graphics.LinearGradient; 6 | import android.graphics.Matrix; 7 | import android.graphics.Paint; 8 | import android.graphics.Shader; 9 | import android.graphics.Typeface; 10 | import android.os.Bundle; 11 | import android.text.Editable; 12 | import android.text.SpannableString; 13 | import android.text.TextPaint; 14 | import android.text.TextWatcher; 15 | import android.text.style.CharacterStyle; 16 | import android.text.style.StyleSpan; 17 | import android.text.style.UpdateAppearance; 18 | import android.widget.TextView; 19 | 20 | import java.util.regex.Matcher; 21 | import java.util.regex.Pattern; 22 | 23 | public class RainbowSpanActivity extends Activity { 24 | private TextView textView; 25 | 26 | @Override 27 | protected void onCreate(Bundle savedInstanceState) { 28 | super.onCreate(savedInstanceState); 29 | setContentView(R.layout.activity_rainbow_span); 30 | 31 | textView = (TextView) findViewById(R.id.text); 32 | TextView input = (TextView) findViewById(R.id.input); 33 | 34 | input.addTextChangedListener(new TextWatcher() { 35 | @Override 36 | public void beforeTextChanged(CharSequence s, int start, int count, int after) { 37 | } 38 | 39 | @Override 40 | public void onTextChanged(CharSequence s, int start, int before, int count) { 41 | } 42 | 43 | @Override 44 | public void afterTextChanged(Editable s) { 45 | highlight(s.toString()); 46 | } 47 | }); 48 | 49 | highlight(input.getText().toString()); 50 | } 51 | 52 | private void highlight(String query) { 53 | String text = textView.getText().toString(); 54 | SpannableString spannableString = new SpannableString(text); 55 | 56 | Pattern pattern = Pattern.compile(query.toLowerCase()); 57 | Matcher matcher = pattern.matcher(text.toLowerCase()); 58 | while (matcher.find()) { 59 | spannableString.setSpan(new StyleSpan(Typeface.BOLD), matcher.start(), matcher.end(), 0); 60 | spannableString.setSpan(new RainbowSpan(this), matcher.start(), matcher.end(), 0); 61 | } 62 | 63 | textView.setText(spannableString); 64 | } 65 | 66 | private static class RainbowSpan extends CharacterStyle implements UpdateAppearance { 67 | private final int[] colors; 68 | 69 | public RainbowSpan(Context context) { 70 | colors = context.getResources().getIntArray(R.array.rainbow); 71 | } 72 | 73 | @Override 74 | public void updateDrawState(TextPaint paint) { 75 | paint.setStyle(Paint.Style.FILL); 76 | Shader shader = new LinearGradient(0, 0, 0, paint.getTextSize() * colors.length, colors, null, 77 | Shader.TileMode.MIRROR); 78 | Matrix matrix = new Matrix(); 79 | matrix.setRotate(90); 80 | shader.setLocalMatrix(matrix); 81 | paint.setShader(shader); 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/ShadowTextActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.os.Bundle; 5 | 6 | public class ShadowTextActivity extends Activity { 7 | @Override 8 | protected void onCreate(Bundle savedInstanceState) { 9 | super.onCreate(savedInstanceState); 10 | setContentView(R.layout.activity_shadow_text); 11 | } 12 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/StyledStringActivity.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview; 2 | 3 | import android.app.Activity; 4 | import android.content.Context; 5 | import android.os.Bundle; 6 | import android.text.SpannableString; 7 | import android.text.SpannableStringBuilder; 8 | import android.text.style.TextAppearanceSpan; 9 | import android.widget.TextView; 10 | 11 | public class StyledStringActivity extends Activity { 12 | @Override 13 | protected void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | setContentView(R.layout.activity_styled_string); 16 | 17 | SpannableStringBuilder builder = new SpannableStringBuilder() 18 | .append(formatString(this, R.string.big_red, R.style.BigRedTextAppearance)) 19 | .append("\n") 20 | .append(formatString(this, R.string.medium_green, R.style.MediumGreenTextAppearance)) 21 | .append("\n") 22 | .append(formatString(this, R.string.small_blue, R.style.SmallBlueTextAppearance)); 23 | CharSequence styledString = builder.subSequence(0, builder.length()); 24 | 25 | TextView textView = (TextView) findViewById(R.id.text); 26 | textView.setText(styledString); 27 | } 28 | 29 | private static SpannableString formatString(Context context, int textId, int style) { 30 | String text = context.getString(textId); 31 | SpannableString spannableString = new SpannableString(text); 32 | spannableString.setSpan(new TextAppearanceSpan(context, style), 0, text.length(), 0); 33 | return spannableString; 34 | } 35 | } -------------------------------------------------------------------------------- /app/src/main/java/com/sqisland/android/advanced_textview/widget/LinedEditText.java: -------------------------------------------------------------------------------- 1 | package com.sqisland.android.advanced_textview.widget; 2 | 3 | import android.content.Context; 4 | import android.graphics.Canvas; 5 | import android.graphics.Paint; 6 | import android.util.AttributeSet; 7 | import android.widget.EditText; 8 | 9 | import com.sqisland.android.advanced_textview.R; 10 | 11 | public class LinedEditText extends EditText { 12 | private Paint paint = new Paint(); 13 | 14 | public LinedEditText(Context context) { 15 | super(context); 16 | init(); 17 | } 18 | 19 | public LinedEditText(Context context, AttributeSet attrs) { 20 | super(context, attrs); 21 | init(); 22 | } 23 | 24 | public LinedEditText(Context context, AttributeSet attrs, int defStyleAttr) { 25 | super(context, attrs, defStyleAttr); 26 | init(); 27 | } 28 | 29 | private void init() { 30 | paint = new Paint(); 31 | paint.setStyle(Paint.Style.STROKE); 32 | paint.setColor(getResources().getColor(R.color.paper_line)); 33 | paint.setStrokeWidth(getLineHeight() / 10); 34 | paint.setStrokeCap(Paint.Cap.ROUND); 35 | } 36 | 37 | @Override 38 | protected void onDraw(Canvas canvas) { 39 | float startX = getPaddingLeft(); 40 | float stopX = getWidth() - getPaddingRight(); 41 | float offsetY = getPaddingTop() - getPaint().getFontMetrics().top + paint.getStrokeWidth() / 2; 42 | 43 | for (int i = 0; i < getLineCount(); ++i) { 44 | float y = offsetY + getLineHeight() * i; 45 | canvas.drawLine(startX, y, stopX, y, paint); 46 | } 47 | 48 | super.onDraw(canvas); 49 | } 50 | } -------------------------------------------------------------------------------- /app/src/main/res/anim-v21/hours_rotation.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/anim-v21/minutes_rotation.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/color/go_to_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/cheetah_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/cheetah_tile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/ic_loading.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_wifi_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/ic_wifi_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_wifi_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/ic_wifi_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_wifi_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/ic_wifi_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/ic_wifi_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-hdpi/ic_wifi_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/cheetah_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/cheetah_tile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/ic_loading.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_wifi_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/ic_wifi_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_wifi_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/ic_wifi_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_wifi_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/ic_wifi_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/ic_wifi_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-mdpi/ic_wifi_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-nodpi/octopus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-nodpi/octopus.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/animated_clock.xml: -------------------------------------------------------------------------------- 1 | 4 | 7 | 10 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-v21/clock.xml: -------------------------------------------------------------------------------- 1 | 7 | 12 | 17 | 18 | 23 | 28 | 29 | 33 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/cheetah_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/cheetah_tile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/ic_loading.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_wifi_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/ic_wifi_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_wifi_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/ic_wifi_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_wifi_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/ic_wifi_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/ic_wifi_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xhdpi/ic_wifi_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/cheetah_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/cheetah_tile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_loading.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/ic_loading.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_wifi_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/ic_wifi_0.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_wifi_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/ic_wifi_1.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_wifi_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/ic_wifi_2.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxhdpi/ic_wifi_3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxhdpi/ic_wifi_3.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/cheetah_tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxxhdpi/cheetah_tile.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/chiuki/advanced-textview/238af738cb8ecc4377b1748a2ed724ad82b67793/app/src/main/res/drawable-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/animated_wifi.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/rotating_loading.xml: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /app/src/main/res/layout-v21/activity_animated_compound_drawable.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_about.xml: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_alignment_span.xml: -------------------------------------------------------------------------------- 1 | 8 | 13 | 22 | 23 | 24 | 29 | 30 |