Add View textAlignment
- fix bug #6163772
- use bits field and pack them as much as possible
- take care of "supportsRtl" flag from Manifest
- add visual unit tests
CTS unit tests in another CL
Change-Id: Ib77c4eb423854209af130688c5ef9977401a9c1c
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
index c40a7d5..afd07df 100644
--- a/core/java/android/view/View.java
+++ b/core/java/android/view/View.java
@@ -624,6 +624,7 @@
* @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
* @attr ref android.R.styleable#View_soundEffectsEnabled
* @attr ref android.R.styleable#View_tag
+ * @attr ref android.R.styleable#View_textAlignment
* @attr ref android.R.styleable#View_transformPivotX
* @attr ref android.R.styleable#View_transformPivotY
* @attr ref android.R.styleable#View_translationX
@@ -1827,17 +1828,17 @@
public static final int TEXT_DIRECTION_LOCALE = 5;
/**
+ * Default text direction is inherited
+ */
+ protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
+
+ /**
* Bit shift to get the horizontal layout direction. (bits after LAYOUT_DIRECTION_RESOLVED)
* @hide
*/
static final int TEXT_DIRECTION_MASK_SHIFT = 6;
/**
- * Default text direction is inherited
- */
- protected static int TEXT_DIRECTION_DEFAULT = TEXT_DIRECTION_INHERIT;
-
- /**
* Mask for use with private flags indicating bits used for text direction.
* @hide
*/
@@ -1882,6 +1883,113 @@
static final int TEXT_DIRECTION_RESOLVED_DEFAULT =
TEXT_DIRECTION_FIRST_STRONG << TEXT_DIRECTION_RESOLVED_MASK_SHIFT;
+ /*
+ * Default text alignment. The text alignment of this View is inherited from its parent.
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_INHERIT = 0;
+
+ /**
+ * Default for the root view. The gravity determines the text alignment, ALIGN_NORMAL,
+ * ALIGN_CENTER, or ALIGN_OPPOSITE, which are relative to each paragraph’s text direction.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_GRAVITY = 1;
+
+ /**
+ * Align to the start of the paragraph, e.g. ALIGN_NORMAL.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_TEXT_START = 2;
+
+ /**
+ * Align to the end of the paragraph, e.g. ALIGN_OPPOSITE.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_TEXT_END = 3;
+
+ /**
+ * Center the paragraph, e.g. ALIGN_CENTER.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_CENTER = 4;
+
+ /**
+ * Align to the start of the view, which is ALIGN_LEFT if the view’s resolved
+ * layoutDirection is LTR, and ALIGN_RIGHT otherwise.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_VIEW_START = 5;
+
+ /**
+ * Align to the end of the view, which is ALIGN_RIGHT if the view’s resolved
+ * layoutDirection is LTR, and ALIGN_LEFT otherwise.
+ *
+ * Use with {@link #setTextAlignment(int)}
+ */
+ public static final int TEXT_ALIGNMENT_VIEW_END = 6;
+
+ /**
+ * Default text alignment is inherited
+ */
+ protected static int TEXT_ALIGNMENT_DEFAULT = TEXT_ALIGNMENT_GRAVITY;
+
+ /**
+ * Bit shift to get the horizontal layout direction. (bits after DRAG_HOVERED)
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_MASK_SHIFT = 13;
+
+ /**
+ * Mask for use with private flags indicating bits used for text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_MASK = 0x00000007 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+ /**
+ * Array of text direction flags for mapping attribute "textAlignment" to correct
+ * flag value.
+ * @hide
+ */
+ private static final int[] TEXT_ALIGNMENT_FLAGS = {
+ TEXT_ALIGNMENT_INHERIT << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_TEXT_START << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_TEXT_END << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_CENTER << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_VIEW_START << TEXT_ALIGNMENT_MASK_SHIFT,
+ TEXT_ALIGNMENT_VIEW_END << TEXT_ALIGNMENT_MASK_SHIFT
+ };
+
+ /**
+ * Indicates whether the view text alignment has been resolved.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED = 0x00000008 << TEXT_ALIGNMENT_MASK_SHIFT;
+
+ /**
+ * Bit shift to get the resolved text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT = 17;
+
+ /**
+ * Mask for use with private flags indicating bits used for text alignment.
+ * @hide
+ */
+ static final int TEXT_ALIGNMENT_RESOLVED_MASK = 0x00000007 << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
+ /**
+ * Indicates whether if the view text alignment has been resolved to gravity
+ */
+ public static final int TEXT_ALIGNMENT_RESOLVED_DEFAULT =
+ TEXT_ALIGNMENT_GRAVITY << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+
/* End of masks for mPrivateFlags2 */
@@ -2841,7 +2949,8 @@
mViewFlags = SOUND_EFFECTS_ENABLED | HAPTIC_FEEDBACK_ENABLED;
// Set layout and text direction defaults
mPrivateFlags2 = (LAYOUT_DIRECTION_DEFAULT << LAYOUT_DIRECTION_MASK_SHIFT) |
- (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT);
+ (TEXT_DIRECTION_DEFAULT << TEXT_DIRECTION_MASK_SHIFT) |
+ (TEXT_ALIGNMENT_DEFAULT << TEXT_ALIGNMENT_MASK_SHIFT);
mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
setOverScrollMode(OVER_SCROLL_IF_CONTENT_SCROLLS);
mUserPaddingStart = -1;
@@ -3222,6 +3331,13 @@
mPrivateFlags2 |= TEXT_DIRECTION_FLAGS[textDirection];
}
break;
+ case R.styleable.View_textAlignment:
+ // Clear any text alignment flag already set
+ mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+ // Set the text alignment flag depending on the value of the attribute
+ final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
+ mPrivateFlags2 |= TEXT_ALIGNMENT_FLAGS[textAlignment];
+ break;
}
}
@@ -9989,6 +10105,7 @@
resolveLayoutDirection();
resolvePadding();
resolveTextDirection();
+ resolveTextAlignment();
if (isFocused()) {
InputMethodManager imm = InputMethodManager.peekInstance();
imm.focusIn(this);
@@ -10218,6 +10335,7 @@
mCurrentAnimation = null;
resetResolvedLayoutDirection();
+ resetResolvedTextAlignment();
}
/**
@@ -14766,7 +14884,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
@ViewDebug.ExportedProperty(category = "text", mapping = {
@ViewDebug.IntToString(from = TEXT_DIRECTION_INHERIT, to = "INHERIT"),
@@ -14790,7 +14908,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
public void setTextDirection(int textDirection) {
if (getTextDirection() != textDirection) {
@@ -14799,6 +14917,7 @@
resetResolvedTextDirection();
// Set the new text direction
mPrivateFlags2 |= ((textDirection << TEXT_DIRECTION_MASK_SHIFT) & TEXT_DIRECTION_MASK);
+ // Refresh
requestLayout();
invalidate(true);
}
@@ -14818,7 +14937,7 @@
* {@link #TEXT_DIRECTION_ANY_RTL},
* {@link #TEXT_DIRECTION_LTR},
* {@link #TEXT_DIRECTION_RTL},
- * {@link #TEXT_DIRECTION_LOCALE},
+ * {@link #TEXT_DIRECTION_LOCALE}
*/
public int getResolvedTextDirection() {
// The text direction will be resolved only if needed
@@ -14927,6 +15046,199 @@
public void onResolvedTextDirectionReset() {
}
+ /**
+ * Return the value specifying the text alignment or policy that was set with
+ * {@link #setTextAlignment(int)}.
+ *
+ * @return the defined text alignment. It can be one of:
+ *
+ * {@link #TEXT_ALIGNMENT_INHERIT},
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ */
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+ })
+ public int getTextAlignment() {
+ return (mPrivateFlags2 & TEXT_ALIGNMENT_MASK) >> TEXT_ALIGNMENT_MASK_SHIFT;
+ }
+
+ /**
+ * Set the text alignment.
+ *
+ * @param textAlignment The text alignment to set. Should be one of
+ *
+ * {@link #TEXT_ALIGNMENT_INHERIT},
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ *
+ * @attr ref android.R.styleable#View_textAlignment
+ */
+ public void setTextAlignment(int textAlignment) {
+ if (textAlignment != getTextAlignment()) {
+ // Reset the current and resolved text alignment
+ mPrivateFlags2 &= ~TEXT_ALIGNMENT_MASK;
+ resetResolvedTextAlignment();
+ // Set the new text alignment
+ mPrivateFlags2 |= ((textAlignment << TEXT_ALIGNMENT_MASK_SHIFT) & TEXT_ALIGNMENT_MASK);
+ // Refresh
+ requestLayout();
+ invalidate(true);
+ }
+ }
+
+ /**
+ * Return the resolved text alignment.
+ *
+ * The resolved text alignment. This needs resolution if the value is
+ * TEXT_ALIGNMENT_INHERIT. The resolution matches {@link #setTextAlignment(int)} if it is
+ * not TEXT_ALIGNMENT_INHERIT, otherwise resolution proceeds up the parent chain of the view.
+ *
+ * @return the resolved text alignment. Returns one of:
+ *
+ * {@link #TEXT_ALIGNMENT_GRAVITY},
+ * {@link #TEXT_ALIGNMENT_CENTER},
+ * {@link #TEXT_ALIGNMENT_TEXT_START},
+ * {@link #TEXT_ALIGNMENT_TEXT_END},
+ * {@link #TEXT_ALIGNMENT_VIEW_START},
+ * {@link #TEXT_ALIGNMENT_VIEW_END}
+ */
+ @ViewDebug.ExportedProperty(category = "text", mapping = {
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_INHERIT, to = "INHERIT"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_GRAVITY, to = "GRAVITY"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_START, to = "TEXT_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_TEXT_END, to = "TEXT_END"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_CENTER, to = "CENTER"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_START, to = "VIEW_START"),
+ @ViewDebug.IntToString(from = TEXT_ALIGNMENT_VIEW_END, to = "VIEW_END")
+ })
+ public int getResolvedTextAlignment() {
+ // If text alignment is not resolved, then resolve it
+ if ((mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED) != TEXT_ALIGNMENT_RESOLVED) {
+ resolveTextAlignment();
+ }
+ return (mPrivateFlags2 & TEXT_ALIGNMENT_RESOLVED_MASK) >> TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT;
+ }
+
+ /**
+ * Resolve the text alignment. Will call {@link View#onResolvedTextAlignmentChanged} when
+ * resolution is done.
+ */
+ public void resolveTextAlignment() {
+ // Reset any previous text alignment resolution
+ mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+
+ if (hasRtlSupport()) {
+ // Set resolved text alignment flag depending on text alignment flag
+ final int textAlignment = getTextAlignment();
+ switch (textAlignment) {
+ case TEXT_ALIGNMENT_INHERIT:
+ // Check if we can resolve the text alignment
+ if (canResolveLayoutDirection() && mParent instanceof View) {
+ View view = (View) mParent;
+
+ final int parentResolvedTextAlignment = view.getResolvedTextAlignment();
+ switch (parentResolvedTextAlignment) {
+ case TEXT_ALIGNMENT_GRAVITY:
+ case TEXT_ALIGNMENT_TEXT_START:
+ case TEXT_ALIGNMENT_TEXT_END:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_VIEW_START:
+ case TEXT_ALIGNMENT_VIEW_END:
+ // Resolved text alignment is the same as the parent resolved
+ // text alignment
+ mPrivateFlags2 |=
+ (parentResolvedTextAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ }
+ else {
+ // We cannot do the resolution if there is no parent so use the default
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ break;
+ case TEXT_ALIGNMENT_GRAVITY:
+ case TEXT_ALIGNMENT_TEXT_START:
+ case TEXT_ALIGNMENT_TEXT_END:
+ case TEXT_ALIGNMENT_CENTER:
+ case TEXT_ALIGNMENT_VIEW_START:
+ case TEXT_ALIGNMENT_VIEW_END:
+ // Resolved text alignment is the same as text alignment
+ mPrivateFlags2 |= (textAlignment << TEXT_ALIGNMENT_RESOLVED_MASK_SHIFT);
+ break;
+ default:
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+ } else {
+ // Use default resolved text alignment
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED_DEFAULT;
+ }
+
+ // Set the resolved
+ mPrivateFlags2 |= TEXT_ALIGNMENT_RESOLVED;
+ onResolvedTextAlignmentChanged();
+ }
+
+ /**
+ * Check if text alignment resolution can be done.
+ *
+ * @return true if text alignment resolution can be done otherwise return false.
+ */
+ public boolean canResolveTextAlignment() {
+ switch (getTextAlignment()) {
+ case TEXT_DIRECTION_INHERIT:
+ return (mParent != null);
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Called when text alignment has been resolved. Subclasses that care about text alignment
+ * resolution should override this method.
+ *
+ * The default implementation does nothing.
+ */
+ public void onResolvedTextAlignmentChanged() {
+ }
+
+ /**
+ * Reset resolved text alignment. Text alignment can be resolved with a call to
+ * getResolvedTextAlignment(). Will call {@link View#onResolvedTextAlignmentReset} when
+ * reset is done.
+ */
+ public void resetResolvedTextAlignment() {
+ // Reset any previous text alignment resolution
+ mPrivateFlags2 &= ~(TEXT_ALIGNMENT_RESOLVED | TEXT_ALIGNMENT_RESOLVED_MASK);
+ onResolvedTextAlignmentReset();
+ }
+
+ /**
+ * Called when text alignment is reset. Subclasses that care about text alignment reset should
+ * override this method and do a reset of the text alignment of their children. The default
+ * implementation does nothing.
+ */
+ public void onResolvedTextAlignmentReset() {
+ }
+
//
// Properties
//