Merge "Stop using DynamicLayout in case of non selectable PrecomputedText" into pi-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
a1ff74c68d
@@ -644,8 +644,12 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
*/
|
||||
private Layout mSavedMarqueeModeLayout;
|
||||
|
||||
// Do not update following mText/mSpannable/mPrecomputed except for setTextInternal()
|
||||
@ViewDebug.ExportedProperty(category = "text")
|
||||
private CharSequence mText;
|
||||
private @Nullable CharSequence mText;
|
||||
private @Nullable Spannable mSpannable;
|
||||
private @Nullable PrecomputedText mPrecomputed;
|
||||
|
||||
private CharSequence mTransformed;
|
||||
private BufferType mBufferType = BufferType.NORMAL;
|
||||
|
||||
@@ -874,7 +878,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
|
||||
}
|
||||
|
||||
mText = "";
|
||||
setTextInternal("");
|
||||
|
||||
final Resources res = getResources();
|
||||
final CompatibilityInfo compat = res.getCompatibilityInfo();
|
||||
@@ -1615,6 +1619,13 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
}
|
||||
|
||||
// Update mText and mPrecomputed
|
||||
private void setTextInternal(@Nullable CharSequence text) {
|
||||
mText = text;
|
||||
mSpannable = (text instanceof Spannable) ? (Spannable) text : null;
|
||||
mPrecomputed = (text instanceof PrecomputedText) ? (PrecomputedText) text : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Specify whether this widget should automatically scale the text to try to perfectly fit
|
||||
* within the layout bounds by using the default auto-size configuration.
|
||||
@@ -1973,9 +1984,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (mText instanceof Spannable) {
|
||||
} else if (mSpannable != null) {
|
||||
// Reset the selection.
|
||||
Selection.setSelection((Spannable) mText, getSelectionEnd());
|
||||
Selection.setSelection(mSpannable, getSelectionEnd());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2359,7 +2370,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
if (mMovement != movement) {
|
||||
mMovement = movement;
|
||||
|
||||
if (movement != null && !(mText instanceof Spannable)) {
|
||||
if (movement != null && mSpannable == null) {
|
||||
setText(mText);
|
||||
}
|
||||
|
||||
@@ -2409,8 +2420,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
return;
|
||||
}
|
||||
if (mTransformation != null) {
|
||||
if (mText instanceof Spannable) {
|
||||
((Spannable) mText).removeSpan(mTransformation);
|
||||
if (mSpannable != null) {
|
||||
mSpannable.removeSpan(mTransformation);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5254,7 +5265,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
((Editable) mText).append(text, start, end);
|
||||
|
||||
if (mAutoLinkMask != 0) {
|
||||
boolean linksWereAdded = Linkify.addLinks((Spannable) mText, mAutoLinkMask);
|
||||
boolean linksWereAdded = Linkify.addLinks(mSpannable, mAutoLinkMask);
|
||||
// Do not change the movement method for text that support text selection as it
|
||||
// would prevent an arbitrary cursor displacement.
|
||||
if (linksWereAdded && mLinksClickable && !textCanBeSelected()) {
|
||||
@@ -5413,7 +5424,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
|
||||
if (ss.selStart >= 0 && ss.selEnd >= 0) {
|
||||
if (mText instanceof Spannable) {
|
||||
if (mSpannable != null) {
|
||||
int len = mText.length();
|
||||
|
||||
if (ss.selStart > len || ss.selEnd > len) {
|
||||
@@ -5426,7 +5437,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + "/" + ss.selEnd
|
||||
+ " out of range for " + restored + "text " + mText);
|
||||
} else {
|
||||
Selection.setSelection((Spannable) mText, ss.selStart, ss.selEnd);
|
||||
Selection.setSelection(mSpannable, ss.selStart, ss.selEnd);
|
||||
|
||||
if (ss.frozenWithFocus) {
|
||||
createEditorIfNeeded();
|
||||
@@ -5688,7 +5699,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
* movement method, because setMovementMethod() may call
|
||||
* setText() again to try to upgrade the buffer type.
|
||||
*/
|
||||
mText = text;
|
||||
setTextInternal(text);
|
||||
|
||||
// Do not change the movement method for text that support text selection as it
|
||||
// would prevent an arbitrary cursor displacement.
|
||||
@@ -5699,7 +5710,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
|
||||
mBufferType = type;
|
||||
mText = text;
|
||||
setTextInternal(text);
|
||||
|
||||
if (mTransformation == null) {
|
||||
mTransformed = text;
|
||||
@@ -5825,8 +5836,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
setText(text, type);
|
||||
|
||||
if (start >= 0 || end >= 0) {
|
||||
if (mText instanceof Spannable) {
|
||||
Selection.setSelection((Spannable) mText,
|
||||
if (mSpannable != null) {
|
||||
Selection.setSelection(mSpannable,
|
||||
Math.max(0, Math.min(start, len)),
|
||||
Math.max(0, Math.min(end, len)));
|
||||
}
|
||||
@@ -6020,7 +6031,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
|
||||
if (!isSuggestionsEnabled()) {
|
||||
mText = removeSuggestionSpans(mText);
|
||||
setTextInternal(removeSuggestionSpans(mText));
|
||||
}
|
||||
|
||||
InputMethodManager imm = InputMethodManager.peekInstance();
|
||||
@@ -6948,8 +6959,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
public boolean hasOverlappingRendering() {
|
||||
// horizontal fading edge causes SaveLayerAlpha, which doesn't support alpha modulation
|
||||
return ((getBackground() != null && getBackground().getCurrent() != null)
|
||||
|| mText instanceof Spannable || hasSelection()
|
||||
|| isHorizontalFadingEdgeEnabled());
|
||||
|| mSpannable != null || hasSelection() || isHorizontalFadingEdgeEnabled());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -7399,11 +7409,11 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
@Override
|
||||
public PointerIcon onResolvePointerIcon(MotionEvent event, int pointerIndex) {
|
||||
if (mText instanceof Spannable && mLinksClickable) {
|
||||
if (mSpannable != null && mLinksClickable) {
|
||||
final float x = event.getX(pointerIndex);
|
||||
final float y = event.getY(pointerIndex);
|
||||
final int offset = getOffsetForPosition(x, y);
|
||||
final ClickableSpan[] clickables = ((Spannable) mText).getSpans(offset, offset,
|
||||
final ClickableSpan[] clickables = mSpannable.getSpans(offset, offset,
|
||||
ClickableSpan.class);
|
||||
if (clickables.length > 0) {
|
||||
return PointerIcon.getSystemIcon(mContext, PointerIcon.TYPE_HAND);
|
||||
@@ -7496,10 +7506,10 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
} else if (which == KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD) {
|
||||
// mMovement is not null from doKeyDown
|
||||
mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
|
||||
mMovement.onKeyUp(this, mSpannable, keyCode, up);
|
||||
while (--repeatCount > 0) {
|
||||
mMovement.onKeyDown(this, (Spannable) mText, keyCode, down);
|
||||
mMovement.onKeyUp(this, (Spannable) mText, keyCode, up);
|
||||
mMovement.onKeyDown(this, mSpannable, keyCode, down);
|
||||
mMovement.onKeyUp(this, mSpannable, keyCode, up);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7694,8 +7704,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
boolean doDown = true;
|
||||
if (otherEvent != null) {
|
||||
try {
|
||||
boolean handled = mMovement.onKeyOther(this, (Spannable) mText,
|
||||
otherEvent);
|
||||
boolean handled = mMovement.onKeyOther(this, mSpannable, otherEvent);
|
||||
doDown = false;
|
||||
if (handled) {
|
||||
return KEY_EVENT_HANDLED;
|
||||
@@ -7706,7 +7715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
}
|
||||
if (doDown) {
|
||||
if (mMovement.onKeyDown(this, (Spannable) mText, keyCode, event)) {
|
||||
if (mMovement.onKeyDown(this, mSpannable, keyCode, event)) {
|
||||
if (event.getRepeatCount() == 0 && !KeyEvent.isModifierKey(keyCode)) {
|
||||
mPreventDefaultMovement = true;
|
||||
}
|
||||
@@ -7848,7 +7857,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
|
||||
if (mMovement != null && mLayout != null) {
|
||||
if (mMovement.onKeyUp(this, (Spannable) mText, keyCode, event)) {
|
||||
if (mMovement.onKeyUp(this, mSpannable, keyCode, event)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -8313,6 +8322,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
if (mEditor != null) mEditor.prepareCursorControllers();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if DynamicLayout is required
|
||||
*
|
||||
* @hide
|
||||
*/
|
||||
@VisibleForTesting
|
||||
public boolean useDynamicLayout() {
|
||||
return isTextSelectable() || (mSpannable != null && mPrecomputed == null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @hide
|
||||
*/
|
||||
@@ -8320,7 +8339,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
Layout.Alignment alignment, boolean shouldEllipsize, TruncateAt effectiveEllipsize,
|
||||
boolean useSaved) {
|
||||
Layout result = null;
|
||||
if (mText instanceof Spannable) {
|
||||
if (useDynamicLayout()) {
|
||||
final DynamicLayout.Builder builder = DynamicLayout.Builder.obtain(mText, mTextPaint,
|
||||
wantWidth)
|
||||
.setDisplayText(mTransformed)
|
||||
@@ -9262,7 +9281,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
|
||||
if (newStart != start) {
|
||||
Selection.setSelection((Spannable) mText, newStart);
|
||||
Selection.setSelection(mSpannable, newStart);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -9999,9 +10018,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
if (mEditor != null) mEditor.onFocusChanged(focused, direction);
|
||||
|
||||
if (focused) {
|
||||
if (mText instanceof Spannable) {
|
||||
Spannable sp = (Spannable) mText;
|
||||
MetaKeyKeyListener.resetMetaState(sp);
|
||||
if (mSpannable != null) {
|
||||
MetaKeyKeyListener.resetMetaState(mSpannable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10039,7 +10057,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
*/
|
||||
public void clearComposingText() {
|
||||
if (mText instanceof Spannable) {
|
||||
BaseInputConnection.removeComposingSpans((Spannable) mText);
|
||||
BaseInputConnection.removeComposingSpans(mSpannable);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10095,7 +10113,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
boolean handled = false;
|
||||
|
||||
if (mMovement != null) {
|
||||
handled |= mMovement.onTouchEvent(this, (Spannable) mText, event);
|
||||
handled |= mMovement.onTouchEvent(this, mSpannable, event);
|
||||
}
|
||||
|
||||
final boolean textIsSelectable = isTextSelectable();
|
||||
@@ -10103,7 +10121,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
// The LinkMovementMethod which should handle taps on links has not been installed
|
||||
// on non editable text that support text selection.
|
||||
// We reproduce its behavior here to open links for these.
|
||||
ClickableSpan[] links = ((Spannable) mText).getSpans(getSelectionStart(),
|
||||
ClickableSpan[] links = mSpannable.getSpans(getSelectionStart(),
|
||||
getSelectionEnd(), ClickableSpan.class);
|
||||
|
||||
if (links.length > 0) {
|
||||
@@ -10138,7 +10156,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
public boolean onGenericMotionEvent(MotionEvent event) {
|
||||
if (mMovement != null && mText instanceof Spannable && mLayout != null) {
|
||||
try {
|
||||
if (mMovement.onGenericMotionEvent(this, (Spannable) mText, event)) {
|
||||
if (mMovement.onGenericMotionEvent(this, mSpannable, event)) {
|
||||
return true;
|
||||
}
|
||||
} catch (AbstractMethodError ex) {
|
||||
@@ -10199,8 +10217,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
|
||||
@Override
|
||||
public boolean onTrackballEvent(MotionEvent event) {
|
||||
if (mMovement != null && mText instanceof Spannable && mLayout != null) {
|
||||
if (mMovement.onTrackballEvent(this, (Spannable) mText, event)) {
|
||||
if (mMovement != null && mSpannable != null && mLayout != null) {
|
||||
if (mMovement.onTrackballEvent(this, mSpannable, event)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -11121,7 +11139,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
if (mText != null) {
|
||||
int updatedTextLength = mText.length();
|
||||
if (updatedTextLength > 0) {
|
||||
Selection.setSelection((Spannable) mText, updatedTextLength);
|
||||
Selection.setSelection(mSpannable, updatedTextLength);
|
||||
}
|
||||
}
|
||||
} return true;
|
||||
@@ -11697,7 +11715,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
hideFloatingToolbar(FLOATING_TOOLBAR_SELECT_ALL_REFRESH_DELAY);
|
||||
}
|
||||
final int length = mText.length();
|
||||
Selection.setSelection((Spannable) mText, 0, length);
|
||||
Selection.setSelection(mSpannable, 0, length);
|
||||
return length > 0;
|
||||
}
|
||||
|
||||
@@ -11725,7 +11743,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
}
|
||||
if (paste != null) {
|
||||
if (!didFirst) {
|
||||
Selection.setSelection((Spannable) mText, max);
|
||||
Selection.setSelection(mSpannable, max);
|
||||
((Editable) mText).replace(min, max, paste);
|
||||
didFirst = true;
|
||||
} else {
|
||||
@@ -11747,7 +11765,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
selectedText = TextUtils.trimToParcelableSize(selectedText);
|
||||
sharingIntent.putExtra(android.content.Intent.EXTRA_TEXT, selectedText);
|
||||
getContext().startActivity(Intent.createChooser(sharingIntent, null));
|
||||
Selection.setSelection((Spannable) mText, getSelectionEnd());
|
||||
Selection.setSelection(mSpannable, getSelectionEnd());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11822,7 +11840,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener
|
||||
case DragEvent.ACTION_DRAG_LOCATION:
|
||||
if (mText instanceof Spannable) {
|
||||
final int offset = getOffsetForPosition(event.getX(), event.getY());
|
||||
Selection.setSelection((Spannable) mText, offset);
|
||||
Selection.setSelection(mSpannable, offset);
|
||||
}
|
||||
return true;
|
||||
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package android.widget;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertNotNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@@ -32,9 +33,11 @@ import android.support.test.rule.ActivityTestRule;
|
||||
import android.support.test.runner.AndroidJUnit4;
|
||||
import android.text.GetChars;
|
||||
import android.text.Layout;
|
||||
import android.text.PrecomputedText;
|
||||
import android.text.Selection;
|
||||
import android.text.Spannable;
|
||||
import android.view.View;
|
||||
import android.widget.TextView.BufferType;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
@@ -241,6 +244,82 @@ public class TextViewTest {
|
||||
mTextView.onTextContextMenuItem(TextView.ID_CUT);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseDynamicLayout() {
|
||||
mTextView = new TextView(mActivity);
|
||||
mTextView.setTextIsSelectable(true);
|
||||
String text = "HelloWorld";
|
||||
PrecomputedText precomputed =
|
||||
PrecomputedText.create(text, mTextView.getTextMetricsParams());
|
||||
|
||||
mTextView.setTextIsSelectable(false);
|
||||
mTextView.setText(text);
|
||||
assertFalse(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(true);
|
||||
mTextView.setText(text);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(false);
|
||||
mTextView.setText(precomputed);
|
||||
assertFalse(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(true);
|
||||
mTextView.setText(precomputed);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseDynamicLayout_SPANNABLE() {
|
||||
mTextView = new TextView(mActivity);
|
||||
mTextView.setTextIsSelectable(true);
|
||||
String text = "HelloWorld";
|
||||
PrecomputedText precomputed =
|
||||
PrecomputedText.create(text, mTextView.getTextMetricsParams());
|
||||
|
||||
mTextView.setTextIsSelectable(false);
|
||||
mTextView.setText(text, BufferType.SPANNABLE);
|
||||
android.util.Log.e("TextViewTest", "Text:" + mTextView.getText().getClass().getName());
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(true);
|
||||
mTextView.setText(text, BufferType.SPANNABLE);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(false);
|
||||
mTextView.setText(precomputed, BufferType.SPANNABLE);
|
||||
assertFalse(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(true);
|
||||
mTextView.setText(precomputed, BufferType.SPANNABLE);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUseDynamicLayout_EDITABLE() {
|
||||
mTextView = new TextView(mActivity);
|
||||
mTextView.setTextIsSelectable(true);
|
||||
String text = "HelloWorld";
|
||||
PrecomputedText precomputed =
|
||||
PrecomputedText.create(text, mTextView.getTextMetricsParams());
|
||||
|
||||
mTextView.setTextIsSelectable(false);
|
||||
mTextView.setText(text, BufferType.EDITABLE);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(true);
|
||||
mTextView.setText(text, BufferType.EDITABLE);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(false);
|
||||
mTextView.setText(precomputed, BufferType.EDITABLE);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
|
||||
mTextView.setTextIsSelectable(true);
|
||||
mTextView.setText(precomputed, BufferType.EDITABLE);
|
||||
assertTrue(mTextView.useDynamicLayout());
|
||||
}
|
||||
|
||||
private String createLongText() {
|
||||
int size = 600 * 1000;
|
||||
final StringBuilder builder = new StringBuilder(size);
|
||||
|
||||
Reference in New Issue
Block a user