Add hyphenationFrequency attribute to TextView and StaticLayout.

This patch adds plumbing to TextView and StaticLayout to control the
frequency of automatic hyphenation used in laying out paragraphs.

Bug: 21038249
Change-Id: Ib45de190eb0a1ed738e69fd61f2b39561b11aec7
This commit is contained in:
Roozbeh Pournader
2015-05-12 12:01:06 -07:00
parent d2c05188b6
commit 95c7a13f2a
10 changed files with 128 additions and 12 deletions

View File

@@ -653,6 +653,7 @@ package android {
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
field public static final int horizontalSpacing = 16843028; // 0x1010114
field public static final int host = 16842792; // 0x1010028
field public static final int hyphenationFrequency = 16844024; // 0x10104f8
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
field public static final int iconTint = 16843999; // 0x10104df
@@ -32166,6 +32167,9 @@ package android.text {
field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0
field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1
field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff
field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2
field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0
field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1
}
public static final class Layout.Alignment extends java.lang.Enum {
@@ -32364,6 +32368,7 @@ package android.text {
method public android.text.StaticLayout.Builder setBreakStrategy(int);
method public android.text.StaticLayout.Builder setEllipsize(android.text.TextUtils.TruncateAt);
method public android.text.StaticLayout.Builder setEllipsizedWidth(int);
method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
method public android.text.StaticLayout.Builder setIncludePad(boolean);
method public android.text.StaticLayout.Builder setIndents(int[], int[]);
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
@@ -41446,6 +41451,7 @@ package android.widget {
method public int getHighlightColor();
method public java.lang.CharSequence getHint();
method public final android.content.res.ColorStateList getHintTextColors();
method public int getHyphenationFrequency();
method public int getImeActionId();
method public java.lang.CharSequence getImeActionLabel();
method public int getImeOptions();
@@ -41552,6 +41558,7 @@ package android.widget {
method public final void setHintTextColor(int);
method public final void setHintTextColor(android.content.res.ColorStateList);
method public void setHorizontallyScrolling(boolean);
method public void setHyphenationFrequency(int);
method public void setImeActionLabel(java.lang.CharSequence, int);
method public void setImeOptions(int);
method public void setIncludeFontPadding(boolean);

View File

@@ -725,6 +725,7 @@ package android {
field public static final int horizontalScrollViewStyle = 16843603; // 0x1010353
field public static final int horizontalSpacing = 16843028; // 0x1010114
field public static final int host = 16842792; // 0x1010028
field public static final int hyphenationFrequency = 16844024; // 0x10104f8
field public static final int icon = 16842754; // 0x1010002
field public static final int iconPreview = 16843337; // 0x1010249
field public static final int iconTint = 16843999; // 0x10104df
@@ -34388,6 +34389,9 @@ package android.text {
field public static final int BREAK_STRATEGY_SIMPLE = 0; // 0x0
field public static final int DIR_LEFT_TO_RIGHT = 1; // 0x1
field public static final int DIR_RIGHT_TO_LEFT = -1; // 0xffffffff
field public static final int HYPHENATION_FREQUENCY_FULL = 2; // 0x2
field public static final int HYPHENATION_FREQUENCY_NONE = 0; // 0x0
field public static final int HYPHENATION_FREQUENCY_NORMAL = 1; // 0x1
}
public static final class Layout.Alignment extends java.lang.Enum {
@@ -34586,6 +34590,7 @@ package android.text {
method public android.text.StaticLayout.Builder setBreakStrategy(int);
method public android.text.StaticLayout.Builder setEllipsize(android.text.TextUtils.TruncateAt);
method public android.text.StaticLayout.Builder setEllipsizedWidth(int);
method public android.text.StaticLayout.Builder setHyphenationFrequency(int);
method public android.text.StaticLayout.Builder setIncludePad(boolean);
method public android.text.StaticLayout.Builder setIndents(int[], int[]);
method public android.text.StaticLayout.Builder setLineSpacing(float, float);
@@ -43979,6 +43984,7 @@ package android.widget {
method public int getHighlightColor();
method public java.lang.CharSequence getHint();
method public final android.content.res.ColorStateList getHintTextColors();
method public int getHyphenationFrequency();
method public int getImeActionId();
method public java.lang.CharSequence getImeActionLabel();
method public int getImeOptions();
@@ -44085,6 +44091,7 @@ package android.widget {
method public final void setHintTextColor(int);
method public final void setHintTextColor(android.content.res.ColorStateList);
method public void setHorizontallyScrolling(boolean);
method public void setHyphenationFrequency(int);
method public void setImeActionLabel(java.lang.CharSequence, int);
method public void setImeOptions(int);
method public void setIncludeFontPadding(boolean);

View File

@@ -79,7 +79,8 @@ public class DynamicLayout extends Layout
boolean includepad,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
this(base, display, paint, width, align, TextDirectionHeuristics.FIRSTSTRONG_LTR,
spacingmult, spacingadd, includepad, StaticLayout.BREAK_STRATEGY_SIMPLE,
spacingmult, spacingadd, includepad,
StaticLayout.BREAK_STRATEGY_SIMPLE, StaticLayout.HYPHENATION_FREQUENCY_NONE,
ellipsize, ellipsizedWidth);
}
@@ -96,7 +97,7 @@ public class DynamicLayout extends Layout
TextPaint paint,
int width, Alignment align, TextDirectionHeuristic textDir,
float spacingmult, float spacingadd,
boolean includepad, int breakStrategy,
boolean includepad, int breakStrategy, int hyphenationFrequency,
TextUtils.TruncateAt ellipsize, int ellipsizedWidth) {
super((ellipsize == null)
? display
@@ -122,6 +123,7 @@ public class DynamicLayout extends Layout
mIncludePad = includepad;
mBreakStrategy = breakStrategy;
mHyphenationFrequency = hyphenationFrequency;
/*
* This is annoying, but we can't refer to the layout until
@@ -293,7 +295,8 @@ public class DynamicLayout extends Layout
.setLineSpacing(getSpacingAdd(), getSpacingMultiplier())
.setEllipsizedWidth(mEllipsizedWidth)
.setEllipsize(mEllipsizeAt)
.setBreakStrategy(mBreakStrategy);
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency);
reflowed.generate(b, false, true);
int n = reflowed.getLineCount();
@@ -719,6 +722,7 @@ public class DynamicLayout extends Layout
private int mEllipsizedWidth;
private TextUtils.TruncateAt mEllipsizeAt;
private int mBreakStrategy;
private int mHyphenationFrequency;
private PackedIntVector mInts;
private PackedObjectVector<Directions> mObjects;

View File

@@ -71,6 +71,35 @@ public abstract class Layout {
*/
public static final int BREAK_STRATEGY_BALANCED = 2;
/** @hide */
@IntDef({HYPHENATION_FREQUENCY_NORMAL, HYPHENATION_FREQUENCY_FULL,
HYPHENATION_FREQUENCY_NONE})
@Retention(RetentionPolicy.SOURCE)
public @interface HyphenationFrequency {}
/**
* Value for hyphenation frequency indicating no automatic hyphenation. Useful
* for backward compatibility, and for cases where the automatic hyphenation algorithm results
* in incorrect hyphenation. Mid-word breaks may still happen when a word is wider than the
* layout and there is otherwise no valid break. Soft hyphens are ignored and will not be used
* as suggestions for potential line breaks.
*/
public static final int HYPHENATION_FREQUENCY_NONE = 0;
/**
* Value for hyphenation frequency indicating a light amount of automatic hyphenation, which
* is a conservative default. Useful for informal cases, such as short sentences or chat
* messages.
*/
public static final int HYPHENATION_FREQUENCY_NORMAL = 1;
/**
* Value for hyphenation frequency indicating the full amount of automatic hyphenation, typical
* in typography. Useful for running text and where it's important to put the maximum amount of
* text in a screen with limited space.
*/
public static final int HYPHENATION_FREQUENCY_FULL = 2;
private static final ParagraphStyle[] NO_PARA_SPANS =
ArrayUtils.emptyArray(ParagraphStyle.class);

View File

@@ -92,6 +92,7 @@ public class StaticLayout extends Layout {
b.mEllipsize = null;
b.mMaxLines = Integer.MAX_VALUE;
b.mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
b.mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
b.mMeasuredText = MeasuredText.obtain();
return b;
@@ -275,6 +276,19 @@ public class StaticLayout extends Layout {
return this;
}
/**
* Set hyphenation frequency, to control the amount of automatic hyphenation used. The
* default is {@link Layout#HYPHENATION_FREQUENCY_NONE}.
*
* @param hyphenationFrequency hyphenation frequency for the paragraph
* @return this builder, useful for chaining
* @see android.widget.TextView#setHyphenationFrequency
*/
public Builder setHyphenationFrequency(@HyphenationFrequency int hyphenationFrequency) {
mHyphenationFrequency = hyphenationFrequency;
return this;
}
/**
* Set indents. Arguments are arrays holding an indent amount, one per line, measured in
* pixels. For lines past the last element in the array, the last element repeats.
@@ -302,7 +316,8 @@ public class StaticLayout extends Layout {
* the native code is as follows.
*
* For each paragraph, do a nSetupParagraph, which sets paragraph text, line width, tab
* stops, break strategy (and possibly other parameters in the future).
* stops, break strategy, and hyphenation frequency (and possibly other parameters in the
* future).
*
* Then, for each run within the paragraph:
* - setLocale (this must be done at least for the first run, optional afterwards)
@@ -377,6 +392,7 @@ public class StaticLayout extends Layout {
TextUtils.TruncateAt mEllipsize;
int mMaxLines;
int mBreakStrategy;
int mHyphenationFrequency;
Paint.FontMetricsInt mFontMetricsInt = new Paint.FontMetricsInt();
@@ -644,7 +660,7 @@ public class StaticLayout extends Layout {
nSetupParagraph(b.mNativePtr, chs, paraEnd - paraStart,
firstWidth, firstWidthLineCount, restWidth,
variableTabStops, TAB_INCREMENT, b.mBreakStrategy);
variableTabStops, TAB_INCREMENT, b.mBreakStrategy, b.mHyphenationFrequency);
// measurement has to be done before performing line breaking
// but we don't want to recompute fontmetrics or span ranges the
@@ -1153,7 +1169,7 @@ public class StaticLayout extends Layout {
// Set up paragraph text and settings; done as one big method to minimize jni crossings
private static native void nSetupParagraph(long nativePtr, char[] text, int length,
float firstWidth, int firstWidthLineCount, float restWidth,
int[] variableTabStops, int defaultTabStop, int breakStrategy);
int[] variableTabStops, int defaultTabStop, int breakStrategy, int hyphenationFrequency);
private static native float nAddStyleRun(long nativePtr, long nativePaint,
long nativeTypeface, int start, int end, boolean isRtl);

View File

@@ -238,6 +238,7 @@ import static android.os.Build.VERSION_CODES.JELLY_BEAN_MR1;
* @attr ref android.R.styleable#TextView_letterSpacing
* @attr ref android.R.styleable#TextView_fontFeatureSettings
* @attr ref android.R.styleable#TextView_breakStrategy
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @attr ref android.R.styleable#TextView_leftIndents
* @attr ref android.R.styleable#TextView_rightIndents
*/
@@ -555,6 +556,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
private float mSpacingAdd = 0.0f;
private int mBreakStrategy;
private int mHyphenationFrequency;
private int[] mLeftIndents;
private int[] mRightIndents;
@@ -696,6 +698,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
float letterSpacing = 0;
String fontFeatureSettings = null;
mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
final Resources.Theme theme = context.getTheme();
@@ -1154,6 +1157,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE);
break;
case com.android.internal.R.styleable.TextView_hyphenationFrequency:
mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE);
break;
case com.android.internal.R.styleable.TextView_leftIndents:
TypedArray margins = res.obtainTypedArray(a.getResourceId(attr, View.NO_ID));
mLeftIndents = parseDimensionArray(margins);
@@ -3049,6 +3056,33 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
return mBreakStrategy;
}
/**
* Sets the hyphenation frequency. The default value for both TextView and EditText, which is set
* from the theme, is {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
*
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @see #getHyphenationFrequency()
*/
public void setHyphenationFrequency(@Layout.HyphenationFrequency int hyphenationFrequency) {
mHyphenationFrequency = hyphenationFrequency;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* @return the currently set hyphenation frequency.
*
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @see #setHyphenationFrequency(int)
*/
@Layout.HyphenationFrequency
public int getHyphenationFrequency() {
return mHyphenationFrequency;
}
/**
* Set indents. Arguments are arrays holding an indent amount, one per line, measured in
* pixels. For lines past the last element in the array, the last element repeats.
@@ -6637,7 +6671,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDir(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy);
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency);
if (mLeftIndents != null || mRightIndents != null) {
builder.setIndents(mLeftIndents, mRightIndents);
}
@@ -6678,7 +6713,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
Layout result = null;
if (mText instanceof Spannable) {
result = new DynamicLayout(mText, mTransformed, mTextPaint, wantWidth,
alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad, mBreakStrategy,
alignment, mTextDir, mSpacingMult, mSpacingAdd, mIncludePad,
mBreakStrategy, mHyphenationFrequency,
getKeyListener() == null ? effectiveEllipsize : null, ellipsisWidth);
} else {
if (boring == UNKNOWN_BORING) {
@@ -6726,7 +6762,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
.setTextDir(mTextDir)
.setLineSpacing(mSpacingAdd, mSpacingMult)
.setIncludePad(mIncludePad)
.setBreakStrategy(mBreakStrategy);
.setBreakStrategy(mBreakStrategy)
.setHyphenationFrequency(mHyphenationFrequency);
if (mLeftIndents != null || mRightIndents != null) {
builder.setIndents(mLeftIndents, mRightIndents);
}

View File

@@ -48,10 +48,11 @@ struct JLineBreaksID {
static jclass gLineBreaks_class;
static JLineBreaksID gLineBreaks_fieldID;
// set text and set a number of parameters for creating a layout (width, tabstops, strategy)
// set text and set a number of parameters for creating a layout (width, tabstops, strategy,
// hyphenFrequency)
static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray text, jint length,
jfloat firstWidth, jint firstWidthLineLimit, jfloat restWidth,
jintArray variableTabStops, jint defaultTabStop, jint strategy) {
jintArray variableTabStops, jint defaultTabStop, jint strategy, jint hyphenFrequency) {
LineBreaker* b = reinterpret_cast<LineBreaker*>(nativePtr);
b->resize(length);
env->GetCharArrayRegion(text, 0, length, b->buffer());
@@ -64,6 +65,7 @@ static void nSetupParagraph(JNIEnv* env, jclass, jlong nativePtr, jcharArray tex
b->setTabStops(stops.get(), stops.size(), defaultTabStop);
}
b->setStrategy(static_cast<BreakStrategy>(strategy));
b->setHyphenationFrequency(static_cast<HyphenationFrequency>(hyphenFrequency));
}
static void recycleCopy(JNIEnv* env, jobject recycle, jintArray recycleBreaks,
@@ -177,7 +179,7 @@ static JNINativeMethod gMethods[] = {
{"nFinishBuilder", "(J)V", (void*) nFinishBuilder},
{"nLoadHyphenator", "(Ljava/lang/String;)J", (void*) nLoadHyphenator},
{"nSetLocale", "(JLjava/lang/String;J)V", (void*) nSetLocale},
{"nSetupParagraph", "(J[CIFIF[III)V", (void*) nSetupParagraph},
{"nSetupParagraph", "(J[CIFIF[IIII)V", (void*) nSetupParagraph},
{"nSetIndents", "(J[I)V", (void*) nSetIndents},
{"nAddStyleRun", "(JJJIIZ)F", (void*) nAddStyleRun},
{"nAddMeasuredRun", "(JII[F)V", (void*) nAddMeasuredRun},

View File

@@ -4369,6 +4369,17 @@
<!-- Line breaking stratgegy balances line lengths. -->
<enum name="balanced" value="2" />
</attr>
<!-- Frequency of automatic hyphenation. -->
<attr name="hyphenationFrequency">
<!-- No hyphenation. -->
<enum name="none" value="0" />
<!-- Less frequent hyphenation, useful for informal use cases, such
as chat messages. -->
<enum name="normal" value="1" />
<!-- Standard amount of hyphenation, useful for running text and for
screens with limited space for text. -->
<enum name="full" value="2" />
</attr>
<!-- Array of indents, one dimension value per line, left side. -->
<attr name="leftIndents" format="reference" />
<!-- Array of indents, one dimension value per line, right side. -->

View File

@@ -2688,4 +2688,5 @@
<public type="attr" name="stylusButtonPressable" />
<public type="attr" name="supportsLaunchVoiceAssistFromKeyguard" />
<public type="attr" name="scrollIndicators" />
<public type="attr" name="hyphenationFrequency" />
</resources>

View File

@@ -498,6 +498,7 @@ please see styles_device_defaults.xml.
<item name="textEditSuggestionItemLayout">?attr/textEditSuggestionItemLayout</item>
<item name="textCursorDrawable">?attr/textCursorDrawable</item>
<item name="breakStrategy">high_quality</item>
<item name="hyphenationFrequency">normal</item>
</style>
<style name="Widget.CheckedTextView">
@@ -529,6 +530,7 @@ please see styles_device_defaults.xml.
<item name="textColor">?attr/editTextColor</item>
<item name="gravity">center_vertical</item>
<item name="breakStrategy">simple</item>
<item name="hyphenationFrequency">normal</item>
</style>
<style name="Widget.ExpandableListView" parent="Widget.ListView">