Loading core/java/android/widget/TextView.java +60 −42 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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. Loading Loading @@ -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()); } } } Loading Loading @@ -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); } Loading Loading @@ -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); } } Loading Loading @@ -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()) { Loading Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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. Loading @@ -5699,7 +5710,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mBufferType = type; mText = text; setTextInternal(text); if (mTransformation == null) { mTransformed = text; Loading Loading @@ -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))); } Loading Loading @@ -6020,7 +6031,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (!isSuggestionsEnabled()) { mText = removeSuggestionSpans(mText); setTextInternal(removeSuggestionSpans(mText)); } InputMethodManager imm = InputMethodManager.peekInstance(); Loading Loading @@ -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()); } /** Loading Loading @@ -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); Loading Loading @@ -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); } } Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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; } } Loading Loading @@ -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 */ Loading @@ -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) Loading Loading @@ -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; } Loading Loading @@ -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); } } Loading Loading @@ -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); } } Loading Loading @@ -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(); Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; } } Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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 { Loading @@ -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()); } } Loading Loading @@ -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; Loading core/tests/coretests/src/android/widget/TextViewTest.java +79 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading Loading
core/java/android/widget/TextView.java +60 −42 Original line number Diff line number Diff line Loading @@ -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; Loading Loading @@ -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(); Loading Loading @@ -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. Loading Loading @@ -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()); } } } Loading Loading @@ -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); } Loading Loading @@ -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); } } Loading Loading @@ -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()) { Loading Loading @@ -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) { Loading @@ -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(); Loading Loading @@ -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. Loading @@ -5699,7 +5710,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } mBufferType = type; mText = text; setTextInternal(text); if (mTransformation == null) { mTransformed = text; Loading Loading @@ -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))); } Loading Loading @@ -6020,7 +6031,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (!isSuggestionsEnabled()) { mText = removeSuggestionSpans(mText); setTextInternal(removeSuggestionSpans(mText)); } InputMethodManager imm = InputMethodManager.peekInstance(); Loading Loading @@ -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()); } /** Loading Loading @@ -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); Loading Loading @@ -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); } } Loading Loading @@ -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; Loading @@ -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; } Loading Loading @@ -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; } } Loading Loading @@ -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 */ Loading @@ -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) Loading Loading @@ -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; } Loading Loading @@ -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); } } Loading Loading @@ -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); } } Loading Loading @@ -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(); Loading @@ -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) { Loading Loading @@ -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) { Loading Loading @@ -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; } } Loading Loading @@ -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; Loading Loading @@ -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; } Loading Loading @@ -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 { Loading @@ -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()); } } Loading Loading @@ -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; Loading
core/tests/coretests/src/android/widget/TextViewTest.java +79 −0 Original line number Diff line number Diff line Loading @@ -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; Loading @@ -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; Loading Loading @@ -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); Loading