├── .gitignore ├── .idea ├── .name ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml ├── runConfigurations.xml └── vcs.xml ├── README.md ├── app ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── la │ │ └── juju │ │ └── android │ │ └── yanwenzilayoutdemo │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── la │ │ │ └── juju │ │ │ └── android │ │ │ └── yanwenzilayoutdemo │ │ │ ├── FaceLayoutDemoActivity.java │ │ │ └── source │ │ │ ├── MemorySource.java │ │ │ └── RawSource.java │ └── res │ │ ├── layout │ │ └── activity_face_text_demo.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── indicator_select.png │ │ └── indicator_unselect.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── raw │ │ └── face_text.json │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── la │ └── juju │ └── android │ └── yanwenzilayoutdemo │ └── ExampleUnitTest.java ├── build.gradle ├── design ├── View层级.png ├── face_text_layout_demo.gif └── 玄学曲线.gif ├── face_text_layout ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── la │ │ └── juju │ │ └── android │ │ └── ftil │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── java │ │ └── la │ │ │ └── juju │ │ │ └── android │ │ │ └── ftil │ │ │ ├── adapters │ │ │ └── FaceTextInputLineAdapter.java │ │ │ ├── entities │ │ │ └── FaceText.java │ │ │ ├── source │ │ │ └── FaceTextProvider.java │ │ │ ├── utils │ │ │ ├── DensityUtil.java │ │ │ └── ScreenUtil.java │ │ │ └── widgets │ │ │ ├── DotViewLayout.java │ │ │ └── FaceTextInputLayout.java │ └── res │ │ ├── drawable │ │ └── item_face_text.xml │ │ ├── layout │ │ ├── layout_face_text_input.xml │ │ ├── listitem_face_text_input.xml │ │ ├── view_face_text.xml │ │ └── wrapper_face_text.xml │ │ ├── raw │ │ └── face_text.json │ │ └── values │ │ ├── ftil_attrs.xml │ │ └── strings.xml │ └── test │ └── java │ └── la │ └── juju │ └── android │ └── yanwenzilayout │ └── ExampleUnitTest.java ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### OSX template 3 | .DS_Store 4 | .AppleDouble 5 | .LSOverride 6 | 7 | # Icon must end with two \r 8 | Icon 9 | 10 | # Thumbnails 11 | ._* 12 | 13 | # Files that might appear in the root of a volume 14 | .DocumentRevisions-V100 15 | .fseventsd 16 | .Spotlight-V100 17 | .TemporaryItems 18 | .Trashes 19 | .VolumeIcon.icns 20 | 21 | # Directories potentially created on remote AFP share 22 | .AppleDB 23 | .AppleDesktop 24 | Network Trash Folder 25 | Temporary Items 26 | .apdisk 27 | ### Android template 28 | # Built application files 29 | *.apk 30 | *.ap_ 31 | 32 | # Files for the Dalvik VM 33 | *.dex 34 | 35 | # Java class files 36 | *.class 37 | 38 | # Generated files 39 | bin/ 40 | gen/ 41 | 42 | # Gradle files 43 | .gradle/ 44 | build/ 45 | 46 | # Local configuration file (sdk path, etc) 47 | local.properties 48 | 49 | # Proguard folder generated by Eclipse 50 | proguard/ 51 | 52 | # Log Files 53 | *.log 54 | 55 | ### JetBrains template 56 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio 57 | 58 | *.iml 59 | 60 | ## Directory-based project format: 61 | .idea/ 62 | # if you remove the above rule, at least ignore the following: 63 | 64 | # User-specific stuff: 65 | # .idea/workspace.xml 66 | # .idea/tasks.xml 67 | # .idea/dictionaries 68 | 69 | # Sensitive or high-churn files: 70 | # .idea/dataSources.ids 71 | # .idea/dataSources.xml 72 | # .idea/sqlDataSources.xml 73 | # .idea/dynamic.xml 74 | # .idea/uiDesigner.xml 75 | 76 | # Gradle: 77 | # .idea/gradle.xml 78 | # .idea/libraries 79 | 80 | # Mongo Explorer plugin: 81 | # .idea/mongoSettings.xml 82 | 83 | ## File-based project format: 84 | *.ipr 85 | *.iws 86 | 87 | ## Plugin-specific files: 88 | 89 | # IntelliJ 90 | /out/ 91 | 92 | # mpeltonen/sbt-idea plugin 93 | .idea_modules/ 94 | 95 | # JIRA plugin 96 | atlassian-ide-plugin.xml 97 | 98 | # Crashlytics plugin (for Android Studio and IntelliJ) 99 | com_crashlytics_export_strings.xml 100 | crashlytics.properties 101 | crashlytics-build.properties 102 | 103 | ### Gradle template 104 | # Ignore Gradle GUI config 105 | gradle-app.setting 106 | 107 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 108 | !gradle-wrapper.jar 109 | 110 | # Android Studio Navigation editor temp files 111 | .navigation/ 112 | 113 | -------------------------------------------------------------------------------- /.idea/.name: -------------------------------------------------------------------------------- 1 | FaceTextLayout -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 24 | 25 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 12 | 13 | 14 | 26 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | Android 46 | 47 | 48 | Android Lint 49 | 50 | 51 | 52 | 53 | Android 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 75 | 76 | 77 | 78 | 79 | 1.8 80 | 81 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /.idea/runConfigurations.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 11 | 12 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## FaceTextInputLayout 2 | 3 | [![](https://jitpack.io/v/HelloVass/FaceTextLayout.svg)](https://jitpack.io/#HelloVass/FaceTextLayout) 4 | ### 问题 5 | 如何对长度不一的颜文字进行**整齐**的排版呢! 6 | 7 | 8 | ### Thanks 9 | > 真的非常感谢这位童鞋提供的思路,大家有兴趣可以看看 [FaceText](https://github.com/songhanghang/FaceText)! 10 | 11 | ### 功能 12 | 1. 模仿 **QQ输入法颜文字** 的排版 13 | 2. 为颜文字设置 **selector** 14 | 3. 提供颜文字**点击回调**接口 15 | 4. ... 16 | 17 | 18 | ### 效果演示 19 | 20 | 21 | 22 | ### 小瑕疵 23 | 24 | 1.虽然改进版的排版算法已经可以排版排的很漂亮了,但是,如果要让用户体验更棒,建议还是辛苦下**产品童鞋**给颜文字排下版吧! 25 | 26 | 2.尽可能地降低 View 层级 27 | 28 | 29 | 30 | 3.玄学曲线 31 | 32 | 33 | 34 | 35 | 36 | ### 用法 37 | 38 | ```java 39 | public class FaceLayoutDemoActivity extends AppCompatActivity { 40 | 41 | @Override protected void onCreate(Bundle savedInstanceState) { 42 | super.onCreate(savedInstanceState); 43 | setContentView(R.layout.activity_face_text_demo); 44 | 45 | FaceTextInputLayout faceTextInputLayout = 46 | (FaceTextInputLayout) findViewById(R.id.tv_face_text_input_layout); 47 | 48 | // 设置“颜文字source” 49 | faceTextInputLayout.setFaceTextSource(new RawSource(this,R.raw.face_text)); 50 | 51 | // 设置点击事件 52 | faceTextInputLayout.setOnFaceTextClickListener(new OnFaceTextClickListener() { 53 | @Override public void onFaceTextClick(FaceText faceText) { 54 | Toast.makeText(FaceLayoutDemoActivity.this, faceText.content, Toast.LENGTH_SHORT).show(); 55 | } 56 | }); 57 | } 58 | } 59 | 60 | ``` 61 | 62 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 23 5 | buildToolsVersion "23.0.2" 6 | 7 | defaultConfig { 8 | applicationId "la.juju.android.yanwenzilayoutdemo" 9 | minSdkVersion 16 10 | targetSdkVersion 23 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(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile project(':face_text_layout') 26 | compile 'com.google.code.gson:gson:2.6.1' 27 | compile 'com.android.support:appcompat-v7:23.2.0' 28 | } 29 | -------------------------------------------------------------------------------- /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/HelloVass/Library/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/la/juju/android/yanwenzilayoutdemo/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.yanwenzilayoutdemo; 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 | 5 | 6 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /app/src/main/java/la/juju/android/yanwenzilayoutdemo/FaceLayoutDemoActivity.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.yanwenzilayoutdemo; 2 | 3 | import android.os.Bundle; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.support.v7.widget.SwitchCompat; 6 | import android.view.View; 7 | import android.widget.CompoundButton; 8 | import android.widget.Toast; 9 | import la.juju.android.ftil.entities.FaceText; 10 | import la.juju.android.ftil.widgets.FaceTextInputLayout; 11 | import la.juju.android.yanwenzilayoutdemo.source.RawSource; 12 | 13 | /** 14 | * Created by HelloVass on 15/12/31. 15 | */ 16 | public class FaceLayoutDemoActivity extends AppCompatActivity { 17 | 18 | private static final String TAG = FaceLayoutDemoActivity.class.getSimpleName(); 19 | 20 | private FaceTextInputLayout mFaceTextInputLayout; 21 | 22 | private SwitchCompat mEmotionTrigger; 23 | 24 | @Override protected void onCreate(Bundle savedInstanceState) { 25 | super.onCreate(savedInstanceState); 26 | 27 | setContentView(R.layout.activity_face_text_demo); 28 | initViews(); 29 | } 30 | 31 | private void initViews() { 32 | 33 | // setup FaceTextInputLayout 34 | mFaceTextInputLayout = (FaceTextInputLayout) findViewById(R.id.tv_face_text_input_layout); 35 | mFaceTextInputLayout.setFaceTextSource(new RawSource(this, R.raw.face_text)); 36 | mFaceTextInputLayout.setOnFaceTextClickListener( 37 | new FaceTextInputLayout.OnFaceTextClickListener() { 38 | @Override public void onFaceTextClick(FaceText faceText) { 39 | Toast.makeText(FaceLayoutDemoActivity.this, faceText.content, Toast.LENGTH_SHORT) 40 | .show(); 41 | } 42 | }); 43 | mFaceTextInputLayout.render(); 44 | 45 | // setup Switch 46 | mEmotionTrigger = (SwitchCompat) findViewById(R.id.sw_trigger); 47 | mEmotionTrigger.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { 48 | @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { 49 | if (isChecked) { 50 | mFaceTextInputLayout.setVisibility(View.VISIBLE); 51 | } else { 52 | mFaceTextInputLayout.setVisibility(View.GONE); 53 | } 54 | } 55 | }); 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/la/juju/android/yanwenzilayoutdemo/source/MemorySource.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.yanwenzilayoutdemo.source; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import la.juju.android.ftil.entities.FaceText; 6 | import la.juju.android.ftil.source.FaceTextProvider; 7 | 8 | /** 9 | * Created by HelloVass on 16/2/26. 10 | */ 11 | public class MemorySource implements FaceTextProvider { 12 | 13 | String[] FACE_TEXT_SOURCE = new String[] { 14 | "(*^ω^*)", "(o^^o)♪", "(*゚∀゚*)", "\(☆o☆)/", "(*´∀`)", "(✪ω✪)", "(✧◡✧)", "o(*≧▽≦)ツ┏━┓[拍桌狂笑!] ", 15 | "ヽ(´▽`)/", "→_→", "←w←", "(。´∀`)ノ", "_(•̀ω•́ 」∠)_", "_(:з」∠)_ ", "(#゚Д゚)", "( ゚皿゚)", 16 | "щ(゜ロ゜щ)", "(╬⁽⁽ ⁰ ⁾⁾ Д ⁽⁽ ⁰ ⁾⁾)", "(>д<)", "(ÒωÓױ)", "(((╹д╹;)))", "ヾ(◍’౪`◍)ノ゙♡", 17 | "ლ(́◉◞౪◟◉‵ლ)", "(´ε` )♡", "✧*。٩(ˊωˋ*)و✧*。", "ヾ(´▽`;)ゝ", "(o˘д˘)o", "( ̄^ ̄)", "(;¬_¬)", 18 | "(╯‵□′)╯︵┻━┻", "╰(‵□′)╯", "(▼皿▼#)", "( TДT)", "(=xェx=)", "(T^T)", "( •̥́ ˍ •̀ू )嘤嘤嘤~", 19 | "(๑ó﹏ò๑)", "m(._.)m", "m(¬0¬)m", "(‘◇’)?", "[(--)]zzz", "☆~(ゝ。∂)", "_(:з)∠)_", 20 | "ꉂ ೭(˵¯̴͒ꇴ¯̴͒˵)౨”", "(⌯¤̴̶̷̀ω¤̴̶̷́)✧", "(▭-▭)✧", "(●—●)", "(づ ●─● )づ", "。゚+.ღ(ゝ◡ ⚈᷀᷁ღ)", 21 | "(›´ω`‹ )", "( ・᷄ ᵌ・᷅ )", "(๑°3°๑)", " =͟͟͞͞(꒪⌓꒪*)", "( ˘•ω•˘ )", "~( ´•︵•` )~", "o(〃'▽'〃)o", 22 | "(๑Ő௰Ő๑)", "(˶‾᷄ ⁻̫ ‾᷅˵)", "(•ૢ⚈͒⌄⚈͒•ૢ)", "(σ▰˃̶̀ꇴ˂̶́)σ✧", "((٩(//̀Д/́/)۶))", "(⁄ ⁄•⁄ω⁄•⁄ ⁄)", 23 | "ᕙ(⇀‸↼‵‵)ᕗ", "(῀( ˙᷄ỏ˙᷅ )῀)ᵒᵐᵍᵎᵎᵎ", "눈_눈", "Ծ‸Ծ", "(ノಥ益ಥ)", "( •̥́ ˍ •̀ू )", "(๑‾᷅⍨‾᷅๑)", 24 | "( ✘_✘ )↯", "( ̄ε(# ̄)", "(๑ʘ̅ д ʘ̅๑)!!!", "✧\\\ ٩( 'ω' )و //✧" 25 | }; 26 | 27 | @Override public List provideFaceTextList() { 28 | List faceTextList = new ArrayList<>(); 29 | for (String content : FACE_TEXT_SOURCE) { 30 | FaceText faceText = new FaceText(); 31 | faceText.content = content; 32 | faceTextList.add(faceText); 33 | } 34 | return faceTextList; 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /app/src/main/java/la/juju/android/yanwenzilayoutdemo/source/RawSource.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.yanwenzilayoutdemo.source; 2 | 3 | import android.content.Context; 4 | import com.google.gson.Gson; 5 | import com.google.gson.reflect.TypeToken; 6 | import java.io.BufferedReader; 7 | import java.io.IOException; 8 | import java.io.InputStreamReader; 9 | import java.util.List; 10 | import la.juju.android.ftil.entities.FaceText; 11 | import la.juju.android.ftil.source.FaceTextProvider; 12 | 13 | /** 14 | * Created by HelloVass on 16/2/26. 15 | */ 16 | public class RawSource implements FaceTextProvider { 17 | 18 | private Context mContext; 19 | 20 | private int mResId; 21 | 22 | public RawSource(Context context, int resId) { 23 | mContext = context; 24 | mResId = resId; 25 | } 26 | 27 | @Override public List provideFaceTextList() { 28 | 29 | String tempLine; 30 | StringBuilder result = new StringBuilder(); 31 | 32 | BufferedReader bufferedReader = 33 | new BufferedReader(new InputStreamReader(mContext.getResources().openRawResource(mResId))); 34 | 35 | try { 36 | while ((tempLine = bufferedReader.readLine()) != null) result.append(tempLine); 37 | 38 | return new Gson().fromJson(result.toString(), new TypeToken>() { 39 | }.getType()); 40 | } catch (IOException e) { 41 | e.printStackTrace(); 42 | return null; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_face_text_demo.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 14 | 15 | 16 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/indicator_select.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-xxhdpi/indicator_select.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxhdpi/indicator_unselect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-xxhdpi/indicator_unselect.png -------------------------------------------------------------------------------- /app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /app/src/main/res/raw/face_text.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","content":"-0-"},{"id":"2","content":"- -"},{"id":"3","content":"(^人^)"},{"id":"4","content":"0.0"},{"id":"5","content":"-。-"},{"id":"6","content":"→_→"},{"id":"7","content":"(*´艸`)、"},{"id":"8","content":"+w+"},{"id":"9","content":"_(:3」∠)_"},{"id":"10","content":"٩(๑`^´๑)۶"},{"id":"11","content":" ˋ( ° ▽、° )"},{"id":"12","content":"ヾ(′▽`*)ゝ"},{"id":"13","content":" QAQ "},{"id":"14","content":">///////< "},{"id":"15","content":">w< "},{"id":"16","content":"(ˉ▽ˉ;) "},{"id":"17","content":"Σ( ° △ °|||)︴ "},{"id":"18","content":"+w+~~~~~~~ "},{"id":"19","content":"(╯‵□′)╯···*~●"},{"id":"20","content":"(╯‵□′)╯︵┻━┻"},{"id":"21","content":"╭(╯^╰)╮"},{"id":"22","content":"=.=||"},{"id":"23","content":"Σ(`д′*ノ)ノ"},{"id":"24","content":"(〃 ̄︶ ̄)人( ̄︶ ̄〃)"},{"id":"25","content":"(′д`σ)σ"},{"id":"26","content":"-_-!"},{"id":"27","content":"⊙﹏⊙‖∣"},{"id":"28","content":" o(>﹏<)o "},{"id":"29","content":"(#‵′)凸"},{"id":"30","content":"●▂●"},{"id":"31","content":"(#`-_ゝ-)"},{"id":"32","content":"@_@"},{"id":"33","content":"?_?"},{"id":"34","content":"-3-"},{"id":"35","content":"=3333="},{"id":"36","content":"“”\\( ̄ー ̄) ( ̄ー ̄)//“”"},{"id":"37","content":"~( TロT)σ"},{"id":"38","content":"<(_ _)>"},{"id":"39","content":"凸(゜皿゜メ)"},{"id":"40","content":"ヾ( ̄▽ ̄)"},{"id":"41","content":"Σ(っ °Д °;)っ"},{"id":"42","content":"(。•ˇ‸ˇ•。)"},{"id":"44","content":"눈_눈"},{"id":"45","content":"( •̥́ ˍ •̀ू )"},{"id":"46","content":"( *・ω・)✄╰ひ╯ 剪叽叽"},{"id":"47","content":"(;´༎ຶД༎ຶ` 【爽】"},{"id":"48","content":"(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞(๑•̀д•́ ٩"},{"id":"49","content":"_(´ཀ`」 ∠)_"},{"id":"50","content":"(๑╹∀╹๑)萌"},{"id":"51","content":"(๑╹ڡ╹)╭ ~ ♡~~耐你~~"},{"id":"52","content":"(ง •̀_•́)ง"},{"id":"53","content":"( ͡° ͜ʖ ͡°)•ॢ"},{"id":"54","content":"( ꒪⌓꒪)"},{"id":"55","content":"ԅ(≖‿≖ԅ)"},{"id":"56","content":"_(¦3」∠)_"},{"id":"57","content":"✧*。٩(ˊᗜˋ*)و✧*。lalala"},{"id":"58","content":"( ̄ε(# ̄) Σ"},{"id":"59","content":" =͟͟͞͞ =͟͟͞͞ ヘ( ´Д`)ノ 放我去死一死啊"},{"id":"60","content":"(∩•̀ω•́)⊃-*⋆妖孽,看我不收了你!"},{"id":"61","content":"ლ(́◉◞౪◟◉‵ლ)哟吼"},{"id":"62","content":"(́ಢ.◞౪◟ಢ‵)哟哟哟"},{"id":"63","content":"(๑>ᴗ<๑)"},{"id":"64","content":"(’;°;ё;°;)"},{"id":"65","content":"\\//٩('ω')و////酱酱酱"},{"id":"66","content":"(✌゚∀゚)☞他是二逼"},{"id":"67","content":"(≖ω≖✿)面带桃花"},{"id":"68","content":"(o´ω`o)ノ"},{"id":"69","content":"(ヾ(´・ω・`)"},{"id":"67","content":"☆ミ(o*・ω・)ノ"},{"id":"70","content":"(੭ु˶˭̵̴⃙⃚⃘᷄ᗢ˭̴̵⃙⃚⃘᷅˶)੭ु⁾"},{"id":"71","content":"٩(˃̶͈̀௰˂̶͈́)و"},{"id":"72","content":"( ・᷄ὢ・᷅ )"},{"id":"73","content":"∠( ᐛ 」∠)_"},{"id":"74","content":"(⁄ ⁄•⁄ω⁄•⁄ ⁄)"},{"id":"75","content":"(⁍̥̥̥᷄д⁍̥̥̥᷅ ू )"},{"id":"76","content":"(˶‾᷄ །། ‾᷅˵)"},{"id":"77","content":"o(*≧▽≦)ツ┏━┓"},{"id":"78","content":"|・ω・`)"},{"id":"79","content":"๛ก(ー̀ωー́ก)"},{"id":"80","content":"٩۹(๑•̀ω•́ ๑)۶"},{"id":"81","content":"。*:★(´・ω・人・ω・`)。:゜★。"},{"id":"82","content":"(。・ω・。)ノ♡"},{"id":"83","content":"ψ(`∇´)ψ"},{"id":"84","content":"o(´^`)o"},{"id":"85","content":"(。-`ω´-)"},{"id":"86","content":"ヽ(・ω・ゞ)"}] -------------------------------------------------------------------------------- /app/src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /app/src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | YanWenZiLayoutDemo 3 | 4 | -------------------------------------------------------------------------------- /app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/test/java/la/juju/android/yanwenzilayoutdemo/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.yanwenzilayoutdemo; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test public void addition_isCorrect() throws Exception { 12 | assertEquals(4, 2 + 2); 13 | } 14 | } -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | repositories { 5 | jcenter() 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:2.1.2' 9 | classpath 'com.github.dcendents:android-maven-gradle-plugin:1.3' 10 | } 11 | } 12 | 13 | allprojects { 14 | repositories { 15 | jcenter() 16 | maven { url "https://jitpack.io" } 17 | } 18 | } 19 | 20 | task clean(type: Delete) { 21 | delete rootProject.buildDir 22 | } 23 | -------------------------------------------------------------------------------- /design/View层级.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/design/View层级.png -------------------------------------------------------------------------------- /design/face_text_layout_demo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/design/face_text_layout_demo.gif -------------------------------------------------------------------------------- /design/玄学曲线.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/design/玄学曲线.gif -------------------------------------------------------------------------------- /face_text_layout/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | apply plugin: 'com.github.dcendents.android-maven' 3 | 4 | android { 5 | compileSdkVersion 23 6 | buildToolsVersion "23.0.2" 7 | 8 | defaultConfig { 9 | minSdkVersion 14 10 | targetSdkVersion 23 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(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.android.support:recyclerview-v7:23.2.0' 26 | compile 'com.android.support:support-v4:23.2.0' 27 | } 28 | -------------------------------------------------------------------------------- /face_text_layout/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/HelloVass/Library/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 | -------------------------------------------------------------------------------- /face_text_layout/src/androidTest/java/la/juju/android/ftil/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil; 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 | } -------------------------------------------------------------------------------- /face_text_layout/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/adapters/FaceTextInputLineAdapter.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.adapters; 2 | 3 | import android.content.Context; 4 | import android.support.v7.widget.RecyclerView; 5 | import android.view.LayoutInflater; 6 | import android.view.View; 7 | import android.view.ViewGroup; 8 | import android.widget.LinearLayout; 9 | import android.widget.TextView; 10 | import java.util.List; 11 | import la.juju.android.ftil.R; 12 | import la.juju.android.ftil.entities.FaceText; 13 | import la.juju.android.ftil.widgets.FaceTextInputLayout; 14 | 15 | /** 16 | * Created by HelloVass on 16/1/1. 17 | */ 18 | public class FaceTextInputLineAdapter 19 | extends RecyclerView.Adapter { 20 | 21 | private List> mPageFaceTextList; 22 | 23 | private LayoutInflater mInflater; 24 | 25 | private LinearLayout.LayoutParams mFaceTextContainerLayoutParams; 26 | 27 | private FaceTextInputLayout.OnFaceTextClickListener mOnFaceTextClickListener; 28 | 29 | public FaceTextInputLineAdapter(Context context) { 30 | mInflater = LayoutInflater.from(context); 31 | } 32 | 33 | public void setOnFaceTextClickListener(FaceTextInputLayout.OnFaceTextClickListener onFaceTextClickListener) { 34 | mOnFaceTextClickListener = onFaceTextClickListener; 35 | } 36 | 37 | public void setFaceTextContainerLayoutParams( 38 | LinearLayout.LayoutParams faceTextContainerLayoutParams) { 39 | mFaceTextContainerLayoutParams = faceTextContainerLayoutParams; 40 | } 41 | 42 | public void setPageFaceTextList(List> pageFaceTextList) { 43 | mPageFaceTextList = pageFaceTextList; 44 | } 45 | 46 | @Override public FaceTextViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { 47 | return new FaceTextViewHolder(LayoutInflater.from(parent.getContext()) 48 | .inflate(R.layout.listitem_face_text_input, parent, false)); 49 | } 50 | 51 | @Override public void onBindViewHolder(FaceTextViewHolder holder, int position) { 52 | 53 | List lineFaceTextList = mPageFaceTextList.get(position); 54 | 55 | for (int i = 0; i < lineFaceTextList.size(); i++) { 56 | final FaceText faceText = lineFaceTextList.get(i); 57 | TextView faceTextView = (TextView) mInflater.inflate(R.layout.view_face_text, null); 58 | holder.mLineContainer.addView(faceTextView, mFaceTextContainerLayoutParams); 59 | faceTextView.setText(faceText.content); 60 | faceTextView.setOnClickListener(new View.OnClickListener() { 61 | @Override public void onClick(View v) { 62 | if (mOnFaceTextClickListener != null) { 63 | mOnFaceTextClickListener.onFaceTextClick(faceText); 64 | } 65 | } 66 | }); 67 | } 68 | } 69 | 70 | @Override public int getItemCount() { 71 | return mPageFaceTextList.size(); 72 | } 73 | 74 | public static class FaceTextViewHolder extends RecyclerView.ViewHolder { 75 | 76 | public LinearLayout mLineContainer; 77 | 78 | public FaceTextViewHolder(View itemView) { 79 | super(itemView); 80 | mLineContainer = (LinearLayout) itemView.findViewById(R.id.ll_line_container); 81 | } 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/entities/FaceText.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.entities; 2 | 3 | import android.os.Parcel; 4 | import android.os.Parcelable; 5 | 6 | /** 7 | * Created by HelloVass on 15/11/25. 8 | * 9 | * 颜文字实体类 10 | */ 11 | public class FaceText implements Parcelable { 12 | 13 | public int id; 14 | 15 | public String content; 16 | 17 | public FaceText() { 18 | 19 | } 20 | 21 | @Override public int describeContents() { 22 | return 0; 23 | } 24 | 25 | @Override public void writeToParcel(Parcel dest, int flags) { 26 | dest.writeInt(this.id); 27 | dest.writeString(this.content); 28 | } 29 | 30 | protected FaceText(Parcel in) { 31 | this.id = in.readInt(); 32 | this.content = in.readString(); 33 | } 34 | 35 | public static final Creator CREATOR = new Creator() { 36 | public FaceText createFromParcel(Parcel source) { 37 | return new FaceText(source); 38 | } 39 | 40 | public FaceText[] newArray(int size) { 41 | return new FaceText[size]; 42 | } 43 | }; 44 | } 45 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/source/FaceTextProvider.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.source; 2 | 3 | import java.util.List; 4 | import la.juju.android.ftil.entities.FaceText; 5 | 6 | /** 7 | * Created by HelloVass on 15/12/31. 8 | */ 9 | public interface FaceTextProvider { 10 | List provideFaceTextList(); 11 | } 12 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/utils/DensityUtil.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.utils; 2 | 3 | import android.content.Context; 4 | 5 | /** 6 | * Created by HelloVass on 16/1/1. 7 | */ 8 | public class DensityUtil { 9 | 10 | private DensityUtil() { 11 | 12 | } 13 | 14 | /** 15 | * 根据手机的分辨率从 dp 的单位 转成为 px(像素) 16 | */ 17 | public static int dip2px(Context context, float dpValue) { 18 | final float scale = context.getResources().getDisplayMetrics().density; 19 | return (int) (dpValue * scale + 0.5f); 20 | } 21 | 22 | /** 23 | * 根据手机的分辨率从 px(像素) 的单位 转成为 dp 24 | */ 25 | public static int px2dip(Context context, float pxValue) { 26 | final float scale = context.getResources().getDisplayMetrics().density; 27 | return (int) (pxValue / scale + 0.5f); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/utils/ScreenUtil.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.utils; 2 | 3 | import android.content.Context; 4 | import android.util.DisplayMetrics; 5 | 6 | /** 7 | * Created by HelloVass on 16/1/1. 8 | */ 9 | public class ScreenUtil { 10 | 11 | private ScreenUtil() { 12 | 13 | } 14 | 15 | public static int getScreenWidth(Context context) { 16 | DisplayMetrics displayMetrics = context.getResources().getDisplayMetrics(); 17 | return displayMetrics.widthPixels; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/widgets/DotViewLayout.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.widgets; 2 | 3 | import android.content.Context; 4 | import android.support.v4.view.ViewPager; 5 | import android.util.AttributeSet; 6 | import android.view.Gravity; 7 | import android.view.ViewGroup; 8 | import android.widget.ImageView; 9 | import android.widget.LinearLayout; 10 | 11 | /** 12 | * Created by HelloVass on 15/11/25. 13 | * 14 | * 管理小圆点的 ViewGroup,继承自 LinearLayout 15 | */ 16 | public class DotViewLayout extends LinearLayout implements ViewPager.OnPageChangeListener { 17 | 18 | // 被选中的 indicator 的 index 19 | private int mSelectedIndex = -1; 20 | 21 | // indicator 之间的间隔 22 | private int mIndicatorSpacing; 23 | 24 | // 被选中的 indicator 图片资源 ID 25 | private int mSelectedIndicatorResId; 26 | 27 | // 未选中的 indicator 图片资源 ID 28 | private int mUnSelectedIndicatorResId; 29 | 30 | private ViewPager mViewPager; 31 | 32 | public DotViewLayout(Context context) { 33 | this(context, null); 34 | } 35 | 36 | public DotViewLayout(Context context, AttributeSet attrs) { 37 | super(context, attrs); 38 | init(); 39 | } 40 | 41 | private void init() { 42 | setOrientation(HORIZONTAL); 43 | setGravity(Gravity.CENTER); 44 | } 45 | 46 | public void setViewPager(ViewPager viewPager) { 47 | mViewPager = viewPager; 48 | mViewPager.addOnPageChangeListener(this); 49 | // 添加 indicator 到 DotViewLayout 中 50 | if (mViewPager != null) { 51 | addIndicator(mViewPager.getAdapter().getCount()); 52 | } 53 | } 54 | 55 | private void addIndicator(int count) { 56 | 57 | clearIndicator(); 58 | 59 | // 如果 count 为零,不需要添加 indicator 60 | if (count <= 0) { 61 | return; 62 | } 63 | 64 | for (int i = 0; i < count; i++) { 65 | ImageView imageView = new ImageView(getContext()); 66 | LayoutParams layoutParams = new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 67 | ViewGroup.LayoutParams.WRAP_CONTENT); 68 | layoutParams.leftMargin = mIndicatorSpacing; 69 | layoutParams.rightMargin = mIndicatorSpacing; 70 | // 设置未选中时的图片 71 | imageView.setImageResource(mUnSelectedIndicatorResId); 72 | addView(imageView, layoutParams); 73 | } 74 | 75 | // 更新 Indicator 76 | updateIndicator(mViewPager.getCurrentItem()); 77 | } 78 | 79 | private void updateIndicator(int position) { 80 | 81 | // 如果 mSelectedIndex 不等于 position 82 | if (mSelectedIndex != position) { 83 | // 如果 mSelectedIndex 还未初始化 84 | if (mSelectedIndex == -1) { 85 | ImageView selectedIndicator = (ImageView) getChildAt(position); 86 | selectedIndicator.setImageResource(mSelectedIndicatorResId); 87 | mSelectedIndex = position; 88 | return; 89 | } 90 | 91 | ImageView selectedIndicator = (ImageView) getChildAt(position); 92 | ImageView unselectedIndicator = (ImageView) getChildAt(mSelectedIndex); 93 | selectedIndicator.setImageResource(mSelectedIndicatorResId); 94 | unselectedIndicator.setImageResource(mUnSelectedIndicatorResId); 95 | mSelectedIndex = position; 96 | } 97 | } 98 | 99 | // 移除“DotViewLayout”中的 indicator 100 | private void clearIndicator() { 101 | removeAllViews(); 102 | } 103 | 104 | @Override 105 | public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { 106 | 107 | } 108 | 109 | @Override public void onPageSelected(int position) { 110 | updateIndicator(position); 111 | } 112 | 113 | @Override public void onPageScrollStateChanged(int state) { 114 | 115 | } 116 | 117 | public int getIndicatorSpacing() { 118 | return mIndicatorSpacing; 119 | } 120 | 121 | public void setIndicatorSpacing(int indicatorSpacing) { 122 | mIndicatorSpacing = indicatorSpacing; 123 | } 124 | 125 | public int getSelectedIndicatorResId() { 126 | return mSelectedIndicatorResId; 127 | } 128 | 129 | public void setSelectedIndicatorResId(int selectedIndicatorResId) { 130 | mSelectedIndicatorResId = selectedIndicatorResId; 131 | } 132 | 133 | public int getUnSelectedIndicatorResId() { 134 | return mUnSelectedIndicatorResId; 135 | } 136 | 137 | public void setUnSelectedIndicatorResId(int unSelectedIndicatorResId) { 138 | mUnSelectedIndicatorResId = unSelectedIndicatorResId; 139 | } 140 | } 141 | -------------------------------------------------------------------------------- /face_text_layout/src/main/java/la/juju/android/ftil/widgets/FaceTextInputLayout.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil.widgets; 2 | 3 | import android.content.Context; 4 | import android.content.res.TypedArray; 5 | import android.graphics.Color; 6 | import android.support.v4.view.PagerAdapter; 7 | import android.support.v4.view.ViewPager; 8 | import android.support.v7.widget.LinearLayoutManager; 9 | import android.support.v7.widget.RecyclerView; 10 | import android.util.AttributeSet; 11 | import android.view.LayoutInflater; 12 | import android.view.View; 13 | import android.view.ViewGroup; 14 | import android.widget.LinearLayout; 15 | import android.widget.TextView; 16 | import java.util.ArrayList; 17 | import java.util.List; 18 | import la.juju.android.ftil.R; 19 | import la.juju.android.ftil.adapters.FaceTextInputLineAdapter; 20 | import la.juju.android.ftil.entities.FaceText; 21 | import la.juju.android.ftil.source.FaceTextProvider; 22 | import la.juju.android.ftil.utils.DensityUtil; 23 | import la.juju.android.ftil.utils.ScreenUtil; 24 | 25 | /** 26 | * Created by HelloVass on 16/2/24. 27 | */ 28 | public class FaceTextInputLayout extends LinearLayout { 29 | 30 | private static final String TAG = FaceTextInputLayout.class.getSimpleName(); 31 | 32 | public static final int PAGE_MAX_LINE_NUM = 3; // 每页的最大行号 33 | 34 | public static final int PAGE_MAX_COLUMN_COUNT = 4; // 每页的最大列数 35 | 36 | private ViewPager mViewPager; 37 | 38 | private DotViewLayout mDotViewLayout; // 底部指示器 39 | 40 | private TextView mTargetFaceTextView; // 用于测量颜文字长度的“TextView” 41 | 42 | private MyPagerAdapter mMyPagerAdapter; 43 | 44 | private int mFaceTextViewLeftMargin; // 颜文字左边距 45 | 46 | private int mFaceTextViewRightMargin; // 颜文字右边距 47 | 48 | private LinearLayout.LayoutParams mFaceTextContainerLayoutParams; // 颜文字容器的“LayoutParams”参数 49 | 50 | private FaceTextProvider mFaceTextProvider; // “颜文字source”接口 51 | 52 | private OnFaceTextClickListener mOnFaceTextClickListener; // "颜文字"点击事件回调接口 53 | 54 | public FaceTextInputLayout(Context context) { 55 | this(context, null); 56 | } 57 | 58 | public FaceTextInputLayout(Context context, AttributeSet attrs) { 59 | this(context, attrs, R.attr.FaceTextInputLayoutStyle); 60 | } 61 | 62 | public FaceTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) { 63 | super(context, attrs, defStyleAttr); 64 | init(attrs, defStyleAttr, 0); 65 | } 66 | 67 | /** 68 | * 设置“颜文字source” 69 | */ 70 | public void setFaceTextSource(FaceTextProvider provider) { 71 | mFaceTextProvider = provider; 72 | } 73 | 74 | /** 75 | * 设置“颜文字”点击事件回调 76 | */ 77 | public void setOnFaceTextClickListener(OnFaceTextClickListener onFaceTextClickListener) { 78 | mOnFaceTextClickListener = onFaceTextClickListener; 79 | } 80 | 81 | /** 82 | * 渲染“UI” 83 | */ 84 | public void render() { 85 | 86 | // TODO: 生成页面在主线程,需要放到非 UI线程 87 | List allPageList = generateAllPage(); 88 | mMyPagerAdapter.setFaceTextInputPageList(allPageList); 89 | mViewPager.setAdapter(mMyPagerAdapter); 90 | mDotViewLayout.setViewPager(mViewPager); 91 | } 92 | 93 | @Override protected void onAttachedToWindow() { 94 | super.onAttachedToWindow(); 95 | } 96 | 97 | @Override protected void onDetachedFromWindow() { 98 | super.onDetachedFromWindow(); 99 | 100 | mFaceTextProvider = null; 101 | mOnFaceTextClickListener = null; 102 | } 103 | 104 | private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) { 105 | 106 | setBackgroundColor(Color.parseColor("#efefef")); 107 | setOrientation(VERTICAL); 108 | View.inflate(getContext(), R.layout.layout_face_text_input, this); 109 | 110 | mViewPager = (ViewPager) findViewById(R.id.pager); 111 | mDotViewLayout = (DotViewLayout) findViewById(R.id.dotview_layout); 112 | mMyPagerAdapter = new MyPagerAdapter(); 113 | 114 | applyXMLAttributes(attrs, defStyleAttr, defStyleRes); 115 | } 116 | 117 | private void applyXMLAttributes(AttributeSet attrs, int defStyleAttr, int defStyleRes) { 118 | TypedArray typedArray = 119 | getContext().obtainStyledAttributes(attrs, R.styleable.FaceTextInputLayout, defStyleAttr, 120 | defStyleRes); 121 | 122 | // 默认"indicator"间距 10dp 123 | int indicatorSpacing = 124 | typedArray.getDimensionPixelSize(R.styleable.FaceTextInputLayout_indicatorSpacing, 125 | DensityUtil.dip2px(getContext(), 10)); 126 | mDotViewLayout.setIndicatorSpacing(indicatorSpacing); 127 | 128 | // 获取被选中的"indicator"图片资源 Id 129 | int selectedIndicatorResId = 130 | typedArray.getResourceId(R.styleable.FaceTextInputLayout_selectedSrc, -1); 131 | mDotViewLayout.setSelectedIndicatorResId(selectedIndicatorResId); 132 | 133 | // 获取未选中的"indicator" 图片资源 Id 134 | int unSelectedIndicatorResId = 135 | typedArray.getResourceId(R.styleable.FaceTextInputLayout_unselectedSrc, -1); 136 | mDotViewLayout.setUnSelectedIndicatorResId(unSelectedIndicatorResId); 137 | 138 | mFaceTextViewLeftMargin = 139 | typedArray.getResourceId(R.styleable.FaceTextInputLayout_faceTextViewLeftMargin, 140 | DensityUtil.dip2px(getContext(), 2)); 141 | 142 | mFaceTextViewRightMargin = 143 | typedArray.getResourceId(R.styleable.FaceTextInputLayout_faceTextViewLeftMargin, 144 | DensityUtil.dip2px(getContext(), 2)); 145 | 146 | typedArray.recycle(); 147 | } 148 | 149 | /** 150 | * 生成所有“颜文字”页面 151 | */ 152 | private List generateAllPage() { 153 | List>> allPageFaceTextList = getAllPageFaceTextList(); 154 | List pageList = new ArrayList<>(); 155 | for (int i = 0; i < allPageFaceTextList.size(); i++) { 156 | RecyclerView eachPage = generateEachPage(allPageFaceTextList.get(i)); 157 | pageList.add(eachPage); 158 | } 159 | return pageList; 160 | } 161 | 162 | /** 163 | * 颜文字排版算法 164 | */ 165 | private List>> getAllPageFaceTextList() { 166 | List faceTextList = mFaceTextProvider.provideFaceTextList(); 167 | List>> allPageFaceTextList = new ArrayList<>(); 168 | 169 | // 当前行号 170 | int currentLineNum = 0; 171 | // 列数 172 | int columnCount = 0; 173 | 174 | // 每页的 List 175 | List> pageFaceTextList = new ArrayList<>(); 176 | // 每行的 List 177 | List lineFaceTextList = new ArrayList<>(); 178 | // 保存每行所有“item宽度”的 List 179 | List lineItemWidthList = new ArrayList<>(PAGE_MAX_COLUMN_COUNT); 180 | // 将当前行添加到当前页 181 | pageFaceTextList.add(lineFaceTextList); 182 | // 将当前页添加到“页List”中 183 | allPageFaceTextList.add(pageFaceTextList); 184 | 185 | for (int i = 0; i < faceTextList.size(); i++) { 186 | FaceText faceText = faceTextList.get(i); 187 | 188 | int itemWidth = measureFaceTextWidth(inflateTargetFaceTextView(), faceText) 189 | + mFaceTextViewLeftMargin 190 | + mFaceTextViewRightMargin; 191 | 192 | columnCount++; 193 | lineItemWidthList.add(itemWidth); 194 | 195 | if (canPlaceItems(columnCount, lineItemWidthList)) { 196 | lineFaceTextList.add(faceText); 197 | } else { 198 | currentLineNum++; 199 | columnCount = 1; 200 | 201 | // 切换到下一个页面 202 | if (currentLineNum >= PAGE_MAX_LINE_NUM) { 203 | currentLineNum = 0; 204 | pageFaceTextList = new ArrayList<>(); 205 | allPageFaceTextList.add(pageFaceTextList); 206 | } 207 | 208 | lineItemWidthList = new ArrayList<>(); 209 | lineItemWidthList.add(itemWidth); 210 | 211 | lineFaceTextList = new ArrayList<>(); 212 | lineFaceTextList.add(faceText); 213 | pageFaceTextList.add(lineFaceTextList); 214 | } 215 | } 216 | 217 | return allPageFaceTextList; 218 | } 219 | 220 | /** 221 | * 生成每个“颜文字”页面 222 | */ 223 | private RecyclerView generateEachPage(List> faceTextList) { 224 | RecyclerView recyclerView = new RecyclerView(getContext()); 225 | recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); 226 | recyclerView.setHasFixedSize(true); 227 | recyclerView.setOverScrollMode(View.OVER_SCROLL_NEVER); 228 | 229 | FaceTextInputLineAdapter faceTextInputLineAdapter = new FaceTextInputLineAdapter(getContext()); 230 | faceTextInputLineAdapter.setPageFaceTextList(faceTextList); 231 | faceTextInputLineAdapter.setFaceTextContainerLayoutParams( 232 | generateFaceTextContainerLayoutParams()); 233 | faceTextInputLineAdapter.setOnFaceTextClickListener(mOnFaceTextClickListener); 234 | 235 | recyclerView.setAdapter(faceTextInputLineAdapter); 236 | return recyclerView; 237 | } 238 | 239 | /** 240 | * 生成每个“颜文字” item 对应的 layoutParams 241 | */ 242 | private LinearLayout.LayoutParams generateFaceTextContainerLayoutParams() { 243 | if (mFaceTextContainerLayoutParams == null) { 244 | mFaceTextContainerLayoutParams = 245 | new LinearLayout.LayoutParams(0, calculateFaceTextViewHeight(), 1.0F); 246 | mFaceTextContainerLayoutParams.leftMargin = mFaceTextViewLeftMargin; 247 | mFaceTextContainerLayoutParams.rightMargin = mFaceTextViewRightMargin; 248 | } 249 | 250 | return mFaceTextContainerLayoutParams; 251 | } 252 | 253 | /** 254 | * 计算“颜文字”的高度,父容器的高度减去竖直方向上的 padding(2 * 4dp)和底部的留白(8dp) 255 | * 256 | * @return “颜文字item”的高度 257 | */ 258 | private int calculateFaceTextViewHeight() { 259 | return getLayoutParams().height / PAGE_MAX_LINE_NUM 260 | - 2 * DensityUtil.dip2px(getContext(), 4.0F) 261 | - DensityUtil.dip2px(getContext(), 8.0F); 262 | } 263 | 264 | /** 265 | * 能否摆放颜文字“item” 266 | * 267 | * @param columnCount 当前列数 268 | */ 269 | private boolean canPlaceItems(int columnCount, List lineItemWidthList) { 270 | 271 | if (columnCount > PAGE_MAX_COLUMN_COUNT) { // 超出列数限制 272 | return false; 273 | } 274 | 275 | int screenWidth = ScreenUtil.getScreenWidth(getContext()); 276 | int averageWidth = screenWidth / columnCount; 277 | 278 | for (int itemWidth : lineItemWidthList) { 279 | 280 | if (itemWidth > screenWidth) { // 颜文字长度超出屏幕宽度了 281 | throw new IllegalArgumentException("item width is too long!!!"); 282 | } 283 | 284 | if (itemWidth > averageWidth) { // 颜文字长度超出均分宽度了 285 | return false; 286 | } 287 | } 288 | 289 | return true; 290 | } 291 | 292 | /** 293 | * 取一个用于测量“颜文字”长度的 TextView 294 | */ 295 | private TextView inflateTargetFaceTextView() { 296 | if (mTargetFaceTextView == null) { 297 | mTargetFaceTextView = (TextView) LayoutInflater.from(getContext()) 298 | .inflate(R.layout.wrapper_face_text, null) 299 | .findViewById(R.id.tv_face_text); 300 | } 301 | return mTargetFaceTextView; 302 | } 303 | 304 | /** 305 | * 测量“颜文字”的长度 306 | */ 307 | private int measureFaceTextWidth(TextView faceTextView, FaceText faceText) { 308 | if (faceTextView == null || faceText == null) { 309 | return 0; 310 | } 311 | faceTextView.setText(faceText.content); 312 | int widthMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST); 313 | int heightMeasureSpec = MeasureSpec.makeMeasureSpec((1 << 30) - 1, MeasureSpec.AT_MOST); 314 | faceTextView.measure(widthMeasureSpec, heightMeasureSpec); 315 | return faceTextView.getMeasuredWidth(); 316 | } 317 | 318 | /** 319 | * “颜文字”点击接口 320 | */ 321 | public interface OnFaceTextClickListener { 322 | void onFaceTextClick(FaceText faceText); 323 | } 324 | 325 | private static class MyPagerAdapter extends PagerAdapter { 326 | 327 | private List mFaceTextInputPageList; 328 | 329 | public MyPagerAdapter() { 330 | 331 | } 332 | 333 | public MyPagerAdapter(List recyclerViewList) { 334 | mFaceTextInputPageList = recyclerViewList; 335 | } 336 | 337 | @Override public int getCount() { 338 | return mFaceTextInputPageList.size(); 339 | } 340 | 341 | @Override public boolean isViewFromObject(View view, Object object) { 342 | return view == object; 343 | } 344 | 345 | @Override public Object instantiateItem(ViewGroup container, int position) { 346 | container.addView(mFaceTextInputPageList.get(position)); 347 | return mFaceTextInputPageList.get(position); 348 | } 349 | 350 | @Override public void destroyItem(ViewGroup container, int position, Object object) { 351 | container.removeView(mFaceTextInputPageList.get(position)); 352 | } 353 | 354 | public void setFaceTextInputPageList(List faceTextInputPageList) { 355 | mFaceTextInputPageList = faceTextInputPageList; 356 | } 357 | } 358 | } 359 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/drawable/item_face_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/layout/layout_face_text_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/layout/listitem_face_text_input.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/layout/view_face_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/layout/wrapper_face_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/raw/face_text.json: -------------------------------------------------------------------------------- 1 | [{"id":"1","content":"-0-"},{"id":"2","content":"- -"},{"id":"3","content":"(^人^)"},{"id":"4","content":"0.0"},{"id":"5","content":"-。-"},{"id":"6","content":"→_→"},{"id":"7","content":"(*´艸`)、"},{"id":"8","content":"+w+"},{"id":"9","content":"_(:3」∠)_"},{"id":"10","content":"٩(๑`^´๑)۶"},{"id":"11","content":" ˋ( ° ▽、° )"},{"id":"12","content":"ヾ(′▽`*)ゝ"},{"id":"13","content":" QAQ "},{"id":"14","content":">///////< "},{"id":"15","content":">w< "},{"id":"16","content":"(ˉ▽ˉ;) "},{"id":"17","content":"Σ( ° △ °|||)︴ "},{"id":"18","content":"+w+~~~~~~~ "},{"id":"19","content":"(╯‵□′)╯···*~●"},{"id":"20","content":"(╯‵□′)╯︵┻━┻"},{"id":"21","content":"╭(╯^╰)╮"},{"id":"22","content":"=.=||"},{"id":"23","content":"Σ(`д′*ノ)ノ"},{"id":"24","content":"(〃 ̄︶ ̄)人( ̄︶ ̄〃)"},{"id":"25","content":"(′д`σ)σ"},{"id":"26","content":"-_-!"},{"id":"27","content":"⊙﹏⊙‖∣"},{"id":"28","content":" o(>﹏<)o "},{"id":"29","content":"(#‵′)凸"},{"id":"30","content":"●▂●"},{"id":"31","content":"(#`-_ゝ-)"},{"id":"32","content":"@_@"},{"id":"33","content":"?_?"},{"id":"34","content":"-3-"},{"id":"35","content":"=3333="},{"id":"36","content":"“”\\( ̄ー ̄) ( ̄ー ̄)//“”"},{"id":"37","content":"~( TロT)σ"},{"id":"38","content":"<(_ _)>"},{"id":"39","content":"凸(゜皿゜メ)"},{"id":"40","content":"ヾ( ̄▽ ̄)"},{"id":"41","content":"Σ(っ °Д °;)っ"},{"id":"42","content":"(。•ˇ‸ˇ•。)"},{"id":"44","content":"눈_눈"},{"id":"45","content":"( •̥́ ˍ •̀ू )"},{"id":"46","content":"( *・ω・)✄╰ひ╯ 剪叽叽"},{"id":"47","content":"(;´༎ຶД༎ຶ` 【爽】"},{"id":"48","content":"(๑•̀=͟͟͞͞(๑•̀д•́=͟͟͞͞(๑•̀д•́๑)=͟͟͞͞(๑•̀д•́ ٩"},{"id":"49","content":"_(´ཀ`」 ∠)_"},{"id":"50","content":"(๑╹∀╹๑)萌"},{"id":"51","content":"(๑╹ڡ╹)╭ ~ ♡~~耐你~~"},{"id":"52","content":"(ง •̀_•́)ง"},{"id":"53","content":"( ͡° ͜ʖ ͡°)•ॢ"},{"id":"54","content":"( ꒪⌓꒪)"},{"id":"55","content":"ԅ(≖‿≖ԅ)"},{"id":"56","content":"_(¦3」∠)_"},{"id":"57","content":"✧*。٩(ˊᗜˋ*)و✧*。lalala"},{"id":"58","content":"( ̄ε(# ̄) Σ"},{"id":"59","content":" =͟͟͞͞ =͟͟͞͞ ヘ( ´Д`)ノ 放我去死一死啊"},{"id":"60","content":"(∩•̀ω•́)⊃-*⋆妖孽,看我不收了你!"},{"id":"61","content":"ლ(́◉◞౪◟◉‵ლ)哟吼"},{"id":"62","content":"(́ಢ.◞౪◟ಢ‵)哟哟哟"},{"id":"63","content":"(๑>ᴗ<๑)"},{"id":"64","content":"(’;°;ё;°;)"},{"id":"65","content":"\\//٩('ω')و////酱酱酱"},{"id":"66","content":"(✌゚∀゚)☞他是二逼"},{"id":"67","content":"(≖ω≖✿)面带桃花"},{"id":"68","content":"(o´ω`o)ノ"},{"id":"69","content":"(ヾ(´・ω・`)"},{"id":"67","content":"☆ミ(o*・ω・)ノ"},{"id":"70","content":"(੭ु˶˭̵̴⃙⃚⃘᷄ᗢ˭̴̵⃙⃚⃘᷅˶)੭ु⁾"},{"id":"71","content":"٩(˃̶͈̀௰˂̶͈́)و"},{"id":"72","content":"( ・᷄ὢ・᷅ )"},{"id":"73","content":"∠( ᐛ 」∠)_"},{"id":"74","content":"(⁄ ⁄•⁄ω⁄•⁄ ⁄)"},{"id":"75","content":"(⁍̥̥̥᷄д⁍̥̥̥᷅ ू )"},{"id":"76","content":"(˶‾᷄ །། ‾᷅˵)"},{"id":"77","content":"o(*≧▽≦)ツ┏━┓"},{"id":"78","content":"|・ω・`)"},{"id":"79","content":"๛ก(ー̀ωー́ก)"},{"id":"80","content":"٩۹(๑•̀ω•́ ๑)۶"},{"id":"81","content":"。*:★(´・ω・人・ω・`)。:゜★。"},{"id":"82","content":"(。・ω・。)ノ♡"},{"id":"83","content":"ψ(`∇´)ψ"},{"id":"84","content":"o(´^`)o"},{"id":"85","content":"(。-`ω´-)"},{"id":"86","content":"ヽ(・ω・ゞ)"}] -------------------------------------------------------------------------------- /face_text_layout/src/main/res/values/ftil_attrs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /face_text_layout/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | YanWenZiLayout 3 | 4 | -------------------------------------------------------------------------------- /face_text_layout/src/test/java/la/juju/android/yanwenzilayout/ExampleUnitTest.java: -------------------------------------------------------------------------------- 1 | package la.juju.android.ftil; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.*; 6 | 7 | /** 8 | * To work on unit tests, switch the Test Artifact in the Build Variants view. 9 | */ 10 | public class ExampleUnitTest { 11 | @Test public void addition_isCorrect() throws Exception { 12 | assertEquals(4, 2 + 2); 13 | } 14 | } -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/HelloVass/FaceTextLayout/0674cac1d83fbd328a869b550f18183237344c63/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Sun May 15 23:28:59 CST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' , ':face_text_layout' 2 | --------------------------------------------------------------------------------