├── .gitignore
├── .idea
├── compiler.xml
├── copyright
│ └── profiles_settings.xml
├── encodings.xml
├── gradle.xml
├── misc.xml
├── modules.xml
└── runConfigurations.xml
├── README.md
├── app
├── .gitignore
├── build.gradle
├── lint.xml
└── src
│ └── main
│ ├── AndroidManifest.xml
│ ├── assets
│ └── 2.txt
│ ├── java
│ └── com
│ │ └── xs
│ │ └── justifytextview
│ │ ├── JustifyTextView.java
│ │ └── MainActivity.java
│ └── res
│ ├── drawable-hdpi
│ └── ic_launcher.png
│ ├── drawable-mdpi
│ └── ic_launcher.png
│ ├── drawable-xhdpi
│ └── ic_launcher.png
│ ├── drawable-xxhdpi
│ └── ic_launcher.png
│ ├── layout
│ └── activity_main.xml
│ ├── menu
│ └── main.xml
│ ├── values-v11
│ └── styles.xml
│ ├── values-v14
│ └── styles.xml
│ ├── values-w820dp
│ └── dimens.xml
│ └── values
│ ├── attrs.xml
│ ├── dimens.xml
│ ├── strings.xml
│ └── styles.xml
├── build.gradle
├── gradle
└── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
├── images
└── justifytextView.png
├── import-summary.txt
└── settings.gradle
/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/.idea/compiler.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/.idea/copyright/profiles_settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/.idea/encodings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/.idea/gradle.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
17 |
18 |
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 | 1.8
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/.idea/runConfigurations.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | JustifyTextView
2 | ===============
3 | 实现方式本身有缺陷,可能有bug,不要用于生产环境。有兴趣的,可以参考学习下...
4 | 两端分散对齐的TextView,支持中英文混编
5 |
6 | ######设置行距的属性;其他属性直接使用原TextView的属性,在Activity中使用也是直接使用TextView
7 | ```xml
8 | xmlns:custom="http://schemas.android.com/apk/res/com.xs.justifytextview"
9 | custom:lineSpacing="10sp"
10 | ```
11 | ```java
12 | TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
13 | R.styleable.JustifyTextView, defStyle, 0);
14 | int n = a.getIndexCount();
15 | for (int i = 0; i < n; i++)
16 | {
17 | int attr = a.getIndex(i);
18 | switch (attr)
19 | {
20 | case R.styleable.JustifyTextView_lineSpacing:
21 | lineSpacing = a.getDimensionPixelSize(attr, (int) TypedValue
22 | .applyDimension(TypedValue.COMPLEX_UNIT_SP, 3,
23 | getResources().getDisplayMetrics()));
24 | break;
25 | }
26 |
27 | }
28 | a.recycle();
29 | ```
30 | ###效果图:blush::blush::blush:
31 | 
32 |
--------------------------------------------------------------------------------
/app/.gitignore:
--------------------------------------------------------------------------------
1 | /build
2 |
3 |
--------------------------------------------------------------------------------
/app/build.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.android.application'
2 |
3 | android {
4 | compileSdkVersion 25
5 | buildToolsVersion "25.0.0"
6 |
7 | defaultConfig {
8 | applicationId "com.xs.justifytextview"
9 | minSdkVersion 14
10 | targetSdkVersion 19
11 | compileOptions {
12 | sourceCompatibility JavaVersion.VERSION_1_7
13 | targetCompatibility JavaVersion.VERSION_1_7
14 | }
15 | }
16 |
17 | buildTypes {
18 | release {
19 | minifyEnabled false
20 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/app/lint.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/app/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
10 |
11 |
16 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/assets/2.txt:
--------------------------------------------------------------------------------
1 | It’s not your fault if you can’t remember people’s names
2 | 记不住别人的名字 不是你的错
3 | Once, at a party, I was introduced to a friend of a friend. We shook hands, I told her my name, she told me hers. Then she did something that I was ever so grateful for.
4 | 曾经在一次聚会上,我的朋友把我介绍给他的朋友。我们握手,互通姓名。然后她做了一件让我很感激的事。
5 | “Hang on,” she said. “Can you say your name again? I wasn’t really listening.”
6 | “等一下,”她说,“您能重复一下您的名字吗?我刚才没注意听。”
7 | She saved me from having to later—possibly even at the same party—sheepishly admit that I, too, had already forgotten her name.
8 | 要不是她,我们下次再见时——甚至有可能就在这次聚会晚些时候,我会不好意思地承认,我像她一样,也没有记住她的名字。
9 | An informal poll of fellow Atlantic staffers confirmed my suspicion that this is something that happens to even the most kind and conscientious among us. No sooner does someone utter the most fundamental factoid about themselves than the information flees our brains forever.
10 | 是不是我们都会忘记别人的名字呢?最近的一项针对《大西洋月刊》职员的非正式调查证实了我的猜想,哪怕是我们当中待人最友善、最一丝不苟的人,也会忘记别人的名字。对方刚讲完自我介绍,他的名字可能就已经从我们的脑海中永远溜走了。
11 | There are a few reasons why this occurs:
12 | 这种情况主要由以下几种原因导致:
13 | The next-in-line effect: When you encounter a group of strangers with outstretched hands, your mind turns into a scared 9-year-old at the school talent show. You’re not watching the other contestants; you’re practicing your own routine. The process of both preparing to take in the others’ names and to say your own, as Esther Inglis-Arkell explained at i09, is so taxing that you don’t devote any brain power to actually learning the new names.
14 | 当一群初次见面的人向你伸出双手,你就会像一个在学校才艺展上受惊吓的9岁孩子。你不是在看其他的选手,而是在练习自己的表演动作。你要准备好记住他人的名字,还要说出你自己的名字,正如艾斯特·英格里斯·阿科尔(Esther Inglis-Arkell )所说,这太伤脑筋了,你的大脑又不是专门用来记新名字的。
15 | You’re not really that interested: Maybe you’re just making an appearance at this party and are planning to abscond shortly to a superior kick-back. Your level of interest can impact how well you remember something. “Some people, perhaps those who are more socially aware, are just more interested in people, more interested in relationships,” Richard Harris, professor of psychology at Kansas State University, told ScienceDaily. “They would be more motivated to remember somebody’s name.”
16 | 也许你想在聚会上露个脸,稍微待一会儿就走。对一件事感兴趣的程度会影响你记忆水平。堪萨斯州立大学(Kansas State University)心理学教授理查德·哈里斯(Richard Harris)告诉《每日科学》(ScienceDaily):“有些人可能更有社会意识,对人更感兴趣,对人际关系更感兴趣。他们更有动力去记住别人的名字。”
17 | A failure of working memory: There are two types of storage in the brain: Long-term and short-term. The short-term variety is called “working memory,” and it functions like a very leaky thermos. It doesn’t hold much and it spills stuff out all the time. “You can hold just a little bit of information there and if you don’t concentrate on it, it fades away rapidly,” Paul Reber, a psychology professor at Northwestern University, told me in an email. “Information like a name needs to be transferred to a different brain system that creates long-term memories that persist over time.”
18 | 大脑有两种记忆类型:长期记忆和短期记忆。短期记忆也叫“工作记忆”,工作原理就像一个漏水的热水瓶,装不了很多水,还在不断地漏水。美国西北大学(Northwestern University)心理学教授保罗·雷柏(Paul Reber)在一封邮件中说:“你只能保留一点信息,如果你不集中注意力,这点儿记忆也很快会消失。像名字这样的信息需要转移到大脑的长期记忆系统,才会变成长期记忆。”
19 | Names are kind of pointless: To answer the famous question, there’s not much in a name, frankly. It doesn’t actually tell you anything about the person you’re meeting, and thus it doesn’t give your brain anything to cling to. Steve may love parkour, but he’d love it just as much if he were Samuel or Sheldon. “Human memory is very good at things like faces and factual information that connects well to other information you already know,” Reber said. Wasn’t District 13, that French parkour movie, really awesome? And hey, remember that time you studied abroad in Paris? All those little connections help solidify the memory of who Steve is and what he does.
20 | 说实话,一个人的名字并不包含什么实质性内容,也不能告诉你任何有关这个人的信息。因此,你的大脑记忆也就没有依附的载体。史蒂文(Steve)可能喜欢跑酷,但是萨姆尔(Samuel)和谢尔顿(Sheldon)也喜欢跑酷。雷柏说:“人类善于记住脸孔,还有一些与已知信息联系在一起的事实性信息。”法国的跑酷电影《13区》(District 13)不是令人赞叹么?记得你留学巴黎的时光吗?所有这些小小的联系,都能帮助你深化对于史蒂文的记忆。
21 | The name, meanwhile, “is both completely arbitrary and somewhat familiar (for common names) and ends up neither connecting to what you already know nor standing out as unusual,” Reber said. “So you get this funny phenomenon where you can remember lots about a person you recently met—everything except their name (this happens to me all the time).”
22 | 同时,名字通常“也是完全随意的,所以听起来都有一点儿相似(尤其是常见的名字),和你已知的信息没有联系,本身也没有什么特色,”雷柏说,“所以,你会发现你能记住有关你最近见到的人的很多信息,但唯独记不住他的名字(这时常发生在我身上)。这很有意思。”
23 | So the next time you’d like to excuse yourself for forgetting someone’s name without offending the person, just say something like, “Oh sorry, I was just overly concerned with telling you my own name to remember yours. But to be fair, your name isn’t actually that interesting to me, and besides, it’s inconsequential in the grand scheme of things.”
24 | 所以,下一次见面,当你想为自己找个借口,同时又不冒犯他人时,可以说,“对不起,我刚才只顾着告诉您我的名字了,没有听清您的名字。但是说实话,您的名字我又不是那么感兴趣,何况名字也不是那么重要,是吧。”
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xs/justifytextview/JustifyTextView.java:
--------------------------------------------------------------------------------
1 | package com.xs.justifytextview;
2 |
3 | import java.lang.Character.UnicodeBlock;
4 |
5 | import android.content.Context;
6 | import android.content.res.TypedArray;
7 | import android.graphics.Canvas;
8 | import android.graphics.Paint;
9 | import android.graphics.Paint.FontMetricsInt;
10 | import android.util.AttributeSet;
11 | import android.util.Log;
12 | import android.util.TypedValue;
13 | import android.widget.TextView;
14 |
15 | /**
16 | * 两端分散对齐的TextView,支持中英文混编 你可以使用下面的自定义属性设置行距,其他属性和原生TextView一直并且有效
17 | * xmlns:custom="http://schemas.android.com/apk/res/com.xs.justifytextview"
18 | * custom:lineSpacing="10sp"
19 | *
20 | * @author xsing
21 | *
22 | */
23 | public class JustifyTextView extends TextView
24 | {
25 | private static final String TAG = "JustifyTextView";
26 | /** 文本 */
27 | private String mText;
28 |
29 | /** 画笔 */
30 | private Paint mPaint;
31 |
32 | /** 文本宽度 */
33 | private int textWidth;
34 |
35 | /** 行距 */
36 | private float lineSpacing;
37 |
38 | public JustifyTextView(Context context, AttributeSet attrs)
39 | {
40 | this(context, attrs, 0);
41 | }
42 |
43 | public JustifyTextView(Context context)
44 | {
45 | this(context, null);
46 | }
47 |
48 | public float mBaikeTextHeight = 0;// 文本高度
49 | public int mFontHeight = 0;
50 |
51 | public JustifyTextView(Context context, AttributeSet attrs, int defStyle)
52 | {
53 | super(context, attrs, defStyle);
54 | /**
55 | * 获得我们所定义的自定义样式属性,行距lineSpacing
56 | */
57 | TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
58 | R.styleable.JustifyTextView, defStyle, 0);
59 | int n = a.getIndexCount();
60 | for (int i = 0; i < n; i++)
61 | {
62 | int attr = a.getIndex(i);
63 | switch (attr)
64 | {
65 | case R.styleable.JustifyTextView_lineSpacing:
66 | lineSpacing = a.getDimensionPixelSize(attr, (int) TypedValue
67 | .applyDimension(TypedValue.COMPLEX_UNIT_SP, 3,
68 | getResources().getDisplayMetrics()));
69 | break;
70 | }
71 |
72 | }
73 | a.recycle();
74 |
75 | // TODO 获取原TextView的画笔,保持原属性不变
76 | mPaint = this.getPaint();
77 | // 获取文本颜色设置给画笔
78 | mPaint.setColor(this.getCurrentTextColor());
79 | }
80 |
81 | /** 单词单元数组,主要针对英文 */
82 | private String[] words;
83 |
84 | private void arrayTowords()
85 | {
86 | char[] array = mText.toCharArray();
87 | int j = 0;
88 | words = new String[array.length];
89 | for (int i = 0; i < array.length; i++)
90 | {
91 | words[i] = "";
92 | if(array[i] >= 0 && array[i] < 0x7f)
93 | {
94 | if(String.valueOf(array[i]).equals("\n"))
95 | {
96 | j++;
97 | words[j] = "\n";
98 | j++;
99 | continue;
100 | }
101 | words[j] = words[j] + (array[i] + "").trim();
102 | if(array.length - 1 > i + 1
103 | && (array[i + 1] == ' ' || array[i + 1] == ' '))
104 | {
105 | j++;
106 | }
107 |
108 | } else
109 | {
110 | if(String.valueOf(array[i]).equals("\n"))
111 | {
112 | j++;
113 | words[j] = "\n";
114 | j++;
115 | continue;
116 | }
117 | words[j] = words[j] + (array[i] + "").trim();
118 | UnicodeBlock ub = Character.UnicodeBlock.of((array[i + 1]));
119 | if(ub == Character.UnicodeBlock.GENERAL_PUNCTUATION
120 | || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
121 | || ub == Character.UnicodeBlock.CJK_SYMBOLS_AND_PUNCTUATION
122 | || ub == Character.UnicodeBlock.HALFWIDTH_AND_FULLWIDTH_FORMS
123 | || ub == Character.UnicodeBlock.CJK_COMPATIBILITY_FORMS)
124 | {
125 | continue;
126 | }
127 | j++;
128 | }
129 | }
130 |
131 | }
132 |
133 | /**
134 | * @return lines-int 重新排版后文档的行数
135 | */
136 | private int getLines()
137 | {
138 | float linewidth = 0;
139 | int line = 0;
140 | float blankwidth = mPaint.measureText(" ");
141 | for (int i = 0; i < words.length; i++)
142 | {
143 | float measureText = mPaint.measureText(words[i]);
144 |
145 | if(linewidth + measureText >= textWidth)
146 | {
147 | if(words[i].isEmpty() || words[i] == "")
148 | break;
149 | line++;
150 | linewidth = 0;
151 | i--;
152 | } else
153 | {
154 | if(String.valueOf(words[i]).equals("\n"))
155 | {
156 | linewidth = textWidth;
157 | }
158 | if(mPaint.measureText(words[i]) != mPaint.measureText("中"))
159 | {
160 | linewidth += (measureText + blankwidth);
161 | } else
162 | {
163 | linewidth += measureText;
164 | }
165 | }
166 | }
167 | return line + 1;
168 | }
169 |
170 | @Override
171 | protected void onDraw(Canvas canvas)
172 | {
173 | // super.onDraw(canvas);
174 | Log.d(TAG, "==============onDraw");
175 | float linewidth = 0;
176 | int point = 0;
177 | int line = 0;
178 | float blankwidth = mPaint.measureText(" ");
179 | for (int i = 0; i < words.length; i++)
180 | {
181 | float measureText = mPaint.measureText(words[i]);
182 |
183 | if(linewidth + measureText >= textWidth)
184 | {
185 | float widthPoint = 0;
186 | for (int k = point; k < i; k++)
187 | {
188 |
189 | if(String.valueOf(words[k]).equals("\n"))
190 | {
191 |
192 | } else
193 | {
194 | // TODO 核心,逐行逐个绘制单词word
195 | canvas.drawText(words[k],
196 | widthPoint + getPaddingLeft(),
197 | (float) (mPaint.getTextSize() + lineSpacing)
198 | * (line + 1) + getPaddingTop(), mPaint);
199 | }
200 | widthPoint = widthPoint + mPaint.measureText(words[k])
201 | + ((textWidth - linewidth) / (i - point - 1));
202 | // 如果不是中文,增加一个空格
203 | if(mPaint.measureText(words[k]) != mPaint.measureText("过"))
204 | {
205 | widthPoint += blankwidth;
206 | }
207 | }
208 | // if(words[i]);
209 | line++;
210 | point = i;
211 | linewidth = 0;
212 | widthPoint = 0;
213 | i--;
214 | } else
215 | { // 逐个单词累计,长度够一行绘制一次or换行
216 | if(String.valueOf(words[i]).equals("\n"))
217 | {
218 | linewidth = textWidth;
219 | }
220 | // 英文每个单词后面有一个空格
221 | if(mPaint.measureText(words[i]) != mPaint.measureText("中"))
222 | {
223 | linewidth += (measureText + blankwidth);
224 | } else
225 | {
226 | linewidth += measureText;
227 | }
228 | }
229 | }
230 | Log.d(TAG, "lines=====ondraw" + line);
231 | }
232 |
233 | @Override
234 | protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
235 | {
236 | Log.d(TAG, "==============onMeasure");
237 | super.onMeasure(widthMeasureSpec, heightMeasureSpec);
238 | // 此处得到的是TextView的宽度;高度需重新计算
239 | int widthSize = MeasureSpec.getSize(widthMeasureSpec);
240 |
241 | int width = widthSize;
242 | // 减去左右文本边距的文本区域宽度
243 | textWidth = widthSize - getPaddingLeft() - getPaddingRight();
244 | int height = 1000;// TextView高度
245 |
246 | // 获取text,分析构造单词数组,并计算出行数
247 | mText = (String) this.getText();
248 | arrayTowords();
249 | int lines = getLines();
250 | Log.d(TAG, "lines" + lines);
251 |
252 | float fontSpacing = mPaint.getFontSpacing();// 推荐行间距
253 | FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
254 | int fontheight = fontMetricsInt.bottom - fontMetricsInt.top;
255 |
256 | height = (int) (lines * (mPaint.getTextSize() + lineSpacing));// 0.8偏大
257 | Log.d(TAG,
258 | "width" + width + " height:" + height + " fontheight:"
259 | + fontheight + " textSize:" + mPaint.getTextSize()
260 | + " fontSpacing:" + fontSpacing + "mPaint属性:"
261 | + mPaint.getColor());
262 | setMeasuredDimension(widthSize, height + getPaddingBottom());
263 | }
264 |
265 | }
266 |
--------------------------------------------------------------------------------
/app/src/main/java/com/xs/justifytextview/MainActivity.java:
--------------------------------------------------------------------------------
1 | package com.xs.justifytextview;
2 |
3 | import java.io.BufferedReader;
4 | import java.io.IOException;
5 | import java.io.InputStream;
6 | import java.io.InputStreamReader;
7 |
8 | import android.app.Activity;
9 | import android.content.Context;
10 | import android.content.res.AssetManager;
11 | import android.os.Bundle;
12 | import android.widget.TextView;
13 |
14 | public class MainActivity extends Activity
15 | {
16 |
17 | @Override
18 | protected void onCreate(Bundle savedInstanceState)
19 | {
20 | super.onCreate(savedInstanceState);
21 | setContentView(R.layout.activity_main);
22 | // 此处为子类JustifyTextView对象,使用方法与原生TextView相同
23 | TextView tv = (TextView) findViewById(R.id.main_tv);
24 | tv.setText(getAssetsString(this, "2.txt"));
25 | }
26 |
27 | public String getAssetsString(Context context, String fileName)
28 | {
29 | StringBuffer sb = new StringBuffer();
30 | // 根据语言选择加载
31 | try
32 | {
33 | AssetManager am = context.getAssets();
34 | InputStream in = am.open(fileName);
35 | BufferedReader reader = new BufferedReader(
36 | new InputStreamReader(in));
37 | String line;
38 | while ((line = reader.readLine()) != null)
39 | {
40 | line += ("\n");
41 | sb.append(line);
42 | }
43 | reader.close();
44 | in.close();
45 | } catch (IOException e)
46 | {
47 | e.printStackTrace();
48 | }
49 | return sb.toString();
50 | }
51 |
52 | }
53 |
54 |
--------------------------------------------------------------------------------
/app/src/main/res/drawable-hdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsingv/JustifyTextView/f03a19a594224be1c9814e67929d8a8a42311593/app/src/main/res/drawable-hdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-mdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsingv/JustifyTextView/f03a19a594224be1c9814e67929d8a8a42311593/app/src/main/res/drawable-mdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsingv/JustifyTextView/f03a19a594224be1c9814e67929d8a8a42311593/app/src/main/res/drawable-xhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/drawable-xxhdpi/ic_launcher.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsingv/JustifyTextView/f03a19a594224be1c9814e67929d8a8a42311593/app/src/main/res/drawable-xxhdpi/ic_launcher.png
--------------------------------------------------------------------------------
/app/src/main/res/layout/activity_main.xml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
10 |
11 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/app/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v11/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/app/src/main/res/values-v14/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/app/src/main/res/values-w820dp/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
8 | 64dp
9 |
10 |
11 |
--------------------------------------------------------------------------------
/app/src/main/res/values/attrs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/dimens.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 16dp
5 | 16dp
6 |
7 |
8 |
--------------------------------------------------------------------------------
/app/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | JustifyTextView
5 | It’s not your fault if you can’t remember people’s names\n
6 | 记不住别人的名字 不是你的错\n
7 | Once, at a party, I was introduced to a friend of a friend. We shook hands, I told her my name, she told me hers. Then she did something that I was ever so grateful for.\n
8 | 曾经在一次聚会上,我的朋友把我介绍给他的朋友。我们握手,互通姓名。然后她做了一件让我很感激的事。\n
9 | “Hang on,” she said. “Can you say your name again? I wasn’t really listening.”\n
10 | “等一下,”她说,“您能重复一下您的名字吗?我刚才没注意听。”\n
11 | It’s not your fault if you can’t remember people’s names\n
12 | 记不住别人的名字 不是你的错\n
13 | Once, at a party, I was introduced to a friend of a friend. We shook hands, I told her my name, she told me hers. Then she did something that I was ever so grateful for.\n
14 | 曾经在一次聚会上,我的朋友把我介绍给他的朋友。我们握手,互通姓名。然后她做了一件让我很感激的事。\n
15 | “Hang on,” she said. “Can you say your name again? I wasn’t really listening.”\n
16 | “等一下,”她说,“您能重复一下您的名字吗?我刚才没注意听。”\n
17 | Settings
18 |
19 |
--------------------------------------------------------------------------------
/app/src/main/res/values/styles.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
7 |
14 |
15 |
16 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/build.gradle:
--------------------------------------------------------------------------------
1 | // Top-level build file where you can add configuration options common to all sub-projects/modules.
2 | buildscript {
3 | repositories {
4 | jcenter()
5 | }
6 | dependencies {
7 | classpath 'com.android.tools.build:gradle:2.2.2'
8 | }
9 | }
10 |
11 | allprojects {
12 | repositories {
13 | jcenter()
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsingv/JustifyTextView/f03a19a594224be1c9814e67929d8a8a42311593/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Mon Dec 28 10:00:20 PST 2015
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.14.1-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 |
--------------------------------------------------------------------------------
/images/justifytextView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xsingv/JustifyTextView/f03a19a594224be1c9814e67929d8a8a42311593/images/justifytextView.png
--------------------------------------------------------------------------------
/import-summary.txt:
--------------------------------------------------------------------------------
1 | ECLIPSE ANDROID PROJECT IMPORT SUMMARY
2 | ======================================
3 |
4 | Ignored Files:
5 | --------------
6 | The following files were *not* copied into the new Gradle project; you
7 | should evaluate whether these are still needed in your project and if
8 | so manually move them:
9 |
10 | * .gitignore
11 | * .idea\
12 | * .idea\compiler.xml
13 | * .idea\copyright\
14 | * .idea\copyright\profiles_settings.xml
15 | * .idea\encodings.xml
16 | * .idea\gradle.xml
17 | * .idea\misc.xml
18 | * .idea\workspace.xml
19 | * README.md
20 | * images\
21 | * images\justifytextView.png
22 | * proguard-project.txt
23 |
24 | Moved Files:
25 | ------------
26 | Android Gradle projects use a different directory structure than ADT
27 | Eclipse projects. Here's how the projects were restructured:
28 |
29 | * .apt_generated\ => app\src\main\java\
30 | * AndroidManifest.xml => app\src\main\AndroidManifest.xml
31 | * assets\ => app\src\main\assets\
32 | * lint.xml => app\lint.xml
33 | * res\ => app\src\main\res\
34 | * src\ => app\src\main\java\
35 |
36 | Next Steps:
37 | -----------
38 | You can now build the project. The Gradle project needs network
39 | connectivity to download dependencies.
40 |
41 | Bugs:
42 | -----
43 | If for some reason your project does not build, and you determine that
44 | it is due to a bug or limitation of the Eclipse to Gradle importer,
45 | please file a bug at http://b.android.com with category
46 | Component-Tools.
47 |
48 | (This import summary is for your information only, and can be deleted
49 | after import once you are satisfied with the results.)
50 |
--------------------------------------------------------------------------------
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':app'
2 |
--------------------------------------------------------------------------------