diff options
| author | Gilles Debunne <debunne@google.com> | 2012-02-27 15:30:57 -0800 |
|---|---|---|
| committer | Android (Google) Code Review <android-gerrit@google.com> | 2012-02-27 15:30:57 -0800 |
| commit | 528e6dda428f388b7fca4c76696046c0f202f929 (patch) | |
| tree | 0927cfced2806112c4c97c0ca256ab400d4c519d /core/java/android/widget/TextView.java | |
| parent | 0ca95700b170997abd03645e751f9679139fec24 (diff) | |
| parent | 83051b8c75a145a12eca1a021ff1a4fbcc77ba4b (diff) | |
| download | frameworks_base-528e6dda428f388b7fca4c76696046c0f202f929.zip frameworks_base-528e6dda428f388b7fca4c76696046c0f202f929.tar.gz frameworks_base-528e6dda428f388b7fca4c76696046c0f202f929.tar.bz2 | |
Merge "Selection highlight made possible with no editor"
Diffstat (limited to 'core/java/android/widget/TextView.java')
| -rw-r--r-- | core/java/android/widget/TextView.java | 300 |
1 files changed, 143 insertions, 157 deletions
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java index b126625..f384dc1 100644 --- a/core/java/android/widget/TextView.java +++ b/core/java/android/widget/TextView.java @@ -406,9 +406,15 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener private InputFilter[] mFilters = NO_FILTERS; + // It is possible to have a selection even when mEditor is null (programmatically set, like when + // a link is pressed). These highlight-related fields do not go in mEditor. + private int mHighlightColor = 0x6633B5E5; + private Path mHighlightPath; + private final Paint mHighlightPaint; + private boolean mHighlightPathBogus = true; + // Although these fields are specific to editable text, they are not added to Editor because // they are defined by the TextView's style and are theme-dependent. - private int mHighlightColor = 0x6633B5E5; private int mCursorDrawableRes; // These four fields, could be moved to Editor, since we know their default values and we // could condition the creation of the Editor to a non standard value. This is however @@ -477,6 +483,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mTextPaint.density = res.getDisplayMetrics().density; mTextPaint.setCompatibilityScaling(compat.applicationScale); + mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); + mHighlightPaint.setCompatibilityScaling(compat.applicationScale); + mMovement = getDefaultMovementMethod(); mTransformation = null; @@ -4064,7 +4073,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private void invalidateCursorPath() { - if (getEditor().mHighlightPathBogus) { + if (mHighlightPathBogus) { invalidateCursor(); } else { final int horizontalPadding = getCompoundPaddingLeft(); @@ -4088,7 +4097,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener thick /= 2.0f; - getEditor().mHighlightPath.computeBounds(TEMP_RECTF, false); + // mHighlightPath is guaranteed to be non null at that point. + mHighlightPath.computeBounds(TEMP_RECTF, false); invalidate((int) FloatMath.floor(horizontalPadding + TEMP_RECTF.left - thick), (int) FloatMath.floor(verticalPadding + TEMP_RECTF.top - thick), @@ -4150,7 +4160,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener int bottom = mLayout.getLineBottom(lineEnd); - if (invalidateCursor) { + // mEditor can be null in case selection is set programmatically. + if (invalidateCursor && mEditor != null) { for (int i = 0; i < getEditor().mCursorCount; i++) { Rect bounds = getEditor().mCursorDrawable[i].getBounds(); top = Math.min(top, bounds.top); @@ -4515,6 +4526,55 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener return drawableState; } + private Path getUpdatedHighlightPath() { + Path highlight = null; + Paint highlightPaint = mHighlightPaint; + + final int selStart = getSelectionStart(); + final int selEnd = getSelectionEnd(); + if (mMovement != null && (isFocused() || isPressed()) && selStart >= 0) { + if (selStart == selEnd) { + if (mEditor != null && isCursorVisible() && + (SystemClock.uptimeMillis() - getEditor().mShowCursor) % (2 * BLINK) < BLINK) { + if (mHighlightPathBogus) { + if (mHighlightPath == null) mHighlightPath = new Path(); + mHighlightPath.reset(); + mLayout.getCursorPath(selStart, mHighlightPath, mText); + getEditor().updateCursorsPositions(); + mHighlightPathBogus = false; + } + + // XXX should pass to skin instead of drawing directly + highlightPaint.setColor(mCurTextColor); + if (mCurrentAlpha != 255) { + highlightPaint.setAlpha( + (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255); + } + highlightPaint.setStyle(Paint.Style.STROKE); + highlight = mHighlightPath; + } + } else { + if (mHighlightPathBogus) { + if (mHighlightPath == null) mHighlightPath = new Path(); + mHighlightPath.reset(); + mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); + mHighlightPathBogus = false; + } + + // XXX should pass to skin instead of drawing directly + highlightPaint.setColor(mHighlightColor); + if (mCurrentAlpha != 255) { + highlightPaint.setAlpha( + (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255); + } + highlightPaint.setStyle(Paint.Style.FILL); + + highlight = mHighlightPath; + } + } + return highlight; + } + @Override protected void onDraw(Canvas canvas) { if (mCurrentAlpha <= ViewConfiguration.ALPHA_THRESHOLD_INT) return; @@ -4666,68 +4726,21 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener final int cursorOffsetVertical = voffsetCursor - voffsetText; + Path highlight = getUpdatedHighlightPath(); if (mEditor != null) { - getEditor().onDraw(canvas, layout, cursorOffsetVertical); + getEditor().onDraw(canvas, layout, highlight, cursorOffsetVertical); } else { - layout.draw(canvas, null, null, cursorOffsetVertical); + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); if (mMarquee != null && mMarquee.shouldDrawGhost()) { canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); - layout.draw(canvas, null, null, cursorOffsetVertical); + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); } } canvas.restore(); } - private void updateCursorsPositions() { - if (mCursorDrawableRes == 0) { - getEditor().mCursorCount = 0; - return; - } - - final int offset = getSelectionStart(); - final int line = mLayout.getLineForOffset(offset); - final int top = mLayout.getLineTop(line); - final int bottom = mLayout.getLineTop(line + 1); - - getEditor().mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1; - - int middle = bottom; - if (getEditor().mCursorCount == 2) { - // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)} - middle = (top + bottom) >> 1; - } - - updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset)); - - if (getEditor().mCursorCount == 2) { - updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset)); - } - } - - private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) { - if (getEditor().mCursorDrawable[cursorIndex] == null) - getEditor().mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes); - - if (mTempRect == null) mTempRect = new Rect(); - getEditor().mCursorDrawable[cursorIndex].getPadding(mTempRect); - final int width = getEditor().mCursorDrawable[cursorIndex].getIntrinsicWidth(); - horizontal = Math.max(0.5f, horizontal - 0.5f); - final int left = (int) (horizontal) - mTempRect.left; - getEditor().mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width, - bottom + mTempRect.bottom); - } - - private void drawCursor(Canvas canvas, int cursorOffsetVertical) { - final boolean translate = cursorOffsetVertical != 0; - if (translate) canvas.translate(0, cursorOffsetVertical); - for (int i = 0; i < getEditor().mCursorCount; i++) { - getEditor().mCursorDrawable[i].draw(canvas); - } - if (translate) canvas.translate(0, -cursorOffsetVertical); - } - @Override public void getFocusedRect(Rect r) { if (mLayout == null) { @@ -4759,21 +4772,16 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } else { // Selection extends across multiple lines -- make the focused // rect cover the entire width. - if (mEditor != null) { - if (getEditor().mHighlightPath == null) getEditor().mHighlightPath = new Path(); - if (getEditor().mHighlightPathBogus) { - getEditor().mHighlightPath.reset(); - mLayout.getSelectionPath(selStart, selEnd, getEditor().mHighlightPath); - getEditor().mHighlightPathBogus = false; - } - synchronized (TEMP_RECTF) { - getEditor().mHighlightPath.computeBounds(TEMP_RECTF, true); - r.left = (int)TEMP_RECTF.left-1; - r.right = (int)TEMP_RECTF.right+1; - } - } else { - r.left = 0; - r.right = getMeasuredWidth(); + if (mHighlightPathBogus) { + if (mHighlightPath == null) mHighlightPath = new Path(); + mHighlightPath.reset(); + mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); + mHighlightPathBogus = false; + } + synchronized (TEMP_RECTF) { + mHighlightPath.computeBounds(TEMP_RECTF, true); + r.left = (int)TEMP_RECTF.left-1; + r.right = (int)TEMP_RECTF.right+1; } } } @@ -5584,7 +5592,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (curs >= 0) { - getEditor().mHighlightPathBogus = true; + mHighlightPathBogus = true; makeBlink(); bringPointIntoView(curs); } @@ -5741,7 +5749,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mOldMaximum = mMaximum; mOldMaxMode = mMaxMode; - if (mEditor != null) getEditor().mHighlightPathBogus = true; + mHighlightPathBogus = true; if (wantWidth < 0) { wantWidth = 0; @@ -6982,10 +6990,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener */ protected void onSelectionChanged(int selStart, int selEnd) { sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_TEXT_SELECTION_CHANGED); - // mEditor may be null if selection is created programatically. - createEditorIfNeeded("onSelectionChanged"); - // Invalidate even when selection range is empty, to remove previous highlight - getEditor().mTextDisplayListIsValid = false; + if (mEditor != null) getEditor().mTextDisplayListIsValid = false; } /** @@ -7141,10 +7146,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (selChanged) { - if (mEditor != null) { - getEditor().mHighlightPathBogus = true; - if (!isFocused()) getEditor().mSelectionMoved = true; - } + mHighlightPathBogus = true; + if (mEditor != null && !isFocused()) getEditor().mSelectionMoved = true; if ((buf.getSpanFlags(what)&Spanned.SPAN_INTERMEDIATE) == 0) { if (newSelStart < 0) { @@ -7161,7 +7164,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener what instanceof CharacterStyle) { if (ims == null || ims.mBatchEditNesting == 0) { invalidate(); - if (mEditor != null) getEditor().mHighlightPathBogus = true; + mHighlightPathBogus = true; checkForResize(); } else { ims.mContentChanged = true; @@ -7170,7 +7173,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } if (MetaKeyKeyListener.isMetaTracker(buf, what)) { - if (mEditor != null) getEditor().mHighlightPathBogus = true; + mHighlightPathBogus = true; if (ims != null && MetaKeyKeyListener.isSelectingMetaTracker(buf, what)) { ims.mSelectionModeChanged = true; } @@ -11330,12 +11333,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } private class Editor { - Editor() { - mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); - final CompatibilityInfo compat = TextView.this.getResources().getCompatibilityInfo(); - mHighlightPaint.setCompatibilityScaling(compat.applicationScale); - } - // Cursor Controllers. InsertionPointCursorController mInsertionPointCursorController; SelectionModifierCursorController mSelectionModifierCursorController; @@ -11349,10 +11346,6 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener InputContentType mInputContentType; InputMethodState mInputMethodState; - Path mHighlightPath; - boolean mHighlightPathBogus = true; - final Paint mHighlightPaint; - DisplayList mTextDisplayList; boolean mTextDisplayListIsValid; @@ -11391,7 +11384,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener Runnable mShowSuggestionRunnable; final Drawable[] mCursorDrawable = new Drawable[2]; - int mCursorCount; // Actual current number of used mCursorDrawable: 0, 1 or 2 (split) + int mCursorCount; // Current number of used mCursorDrawable: 0 (resource=0), 1 or 2 (split) Drawable mSelectHandleLeft; Drawable mSelectHandleRight; @@ -11649,65 +11642,9 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener } } - void onDraw(Canvas canvas, Layout layout, int cursorOffsetVertical) { - Path highlight = null; - Paint highlightPaint = null; - - int selStart = -1, selEnd = -1; - boolean drawCursor = false; - - highlightPaint = mHighlightPaint; - // If there is no movement method, then there can be no selection. - // Check that first and attempt to skip everything having to do with - // the cursor. - // XXX This is not strictly true -- a program could set the - // selection manually if it really wanted to. - if (mMovement != null && (isFocused() || isPressed())) { - selStart = getSelectionStart(); - selEnd = getSelectionEnd(); - - if (selStart >= 0) { - if (mHighlightPath == null) mHighlightPath = new Path(); - - if (selStart == selEnd) { - if (isCursorVisible() && - (SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) { - if (mHighlightPathBogus) { - mHighlightPath.reset(); - mLayout.getCursorPath(selStart, mHighlightPath, mText); - updateCursorsPositions(); - mHighlightPathBogus = false; - } - - // XXX should pass to skin instead of drawing directly - highlightPaint.setColor(mCurTextColor); - if (mCurrentAlpha != 255) { - highlightPaint.setAlpha( - (mCurrentAlpha * Color.alpha(mCurTextColor)) / 255); - } - highlightPaint.setStyle(Paint.Style.STROKE); - highlight = mHighlightPath; - drawCursor = mCursorCount > 0; - } - } else if (textCanBeSelected()) { - if (mHighlightPathBogus) { - mHighlightPath.reset(); - mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); - mHighlightPathBogus = false; - } - - // XXX should pass to skin instead of drawing directly - highlightPaint.setColor(mHighlightColor); - if (mCurrentAlpha != 255) { - highlightPaint.setAlpha( - (mCurrentAlpha * Color.alpha(mHighlightColor)) / 255); - } - highlightPaint.setStyle(Paint.Style.FILL); - - highlight = mHighlightPath; - } - } - } + void onDraw(Canvas canvas, Layout layout, Path highlight, int cursorOffsetVertical) { + final int selectionStart = getSelectionStart(); + final int selectionEnd = getSelectionEnd(); final InputMethodState ims = mInputMethodState; if (ims != null && ims.mBatchEditNesting == 0) { @@ -11729,7 +11666,8 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener candStart = EditableInputConnection.getComposingSpanStart(sp); candEnd = EditableInputConnection.getComposingSpanEnd(sp); } - imm.updateSelection(TextView.this, selStart, selEnd, candStart, candEnd); + imm.updateSelection(TextView.this, + selectionStart, selectionEnd, candStart, candEnd); } } @@ -11758,7 +11696,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener mCorrectionHighlighter.draw(canvas, cursorOffsetVertical); } - if (drawCursor) { + if (highlight != null && selectionStart == selectionEnd && mCursorCount > 0) { drawCursor(canvas, cursorOffsetVertical); // Rely on the drawable entirely, do not draw the cursor line. // Has to be done after the IMM related code above which relies on the highlight. @@ -11781,7 +11719,7 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener // The dirty rect should always be null for a display list hardwareCanvas.onPreDraw(null); hardwareCanvas.translate(-mScrollX, -mScrollY); - layout.draw(hardwareCanvas, highlight, highlightPaint, cursorOffsetVertical); + layout.draw(hardwareCanvas, highlight, mHighlightPaint, cursorOffsetVertical); hardwareCanvas.translate(mScrollX, mScrollY); } finally { hardwareCanvas.onPostDraw(); @@ -11794,13 +11732,61 @@ public class TextView extends View implements ViewTreeObserver.OnPreDrawListener DisplayList.FLAG_CLIP_CHILDREN); canvas.translate(-mScrollX, -mScrollY); } else { - layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical); + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); } if (mMarquee != null && mMarquee.shouldDrawGhost()) { canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); - layout.draw(canvas, highlight, highlightPaint, cursorOffsetVertical); + layout.draw(canvas, highlight, mHighlightPaint, cursorOffsetVertical); + } + } + + private void drawCursor(Canvas canvas, int cursorOffsetVertical) { + final boolean translate = cursorOffsetVertical != 0; + if (translate) canvas.translate(0, cursorOffsetVertical); + for (int i = 0; i < getEditor().mCursorCount; i++) { + mCursorDrawable[i].draw(canvas); } + if (translate) canvas.translate(0, -cursorOffsetVertical); + } + + private void updateCursorsPositions() { + if (mCursorDrawableRes == 0) { + mCursorCount = 0; + return; + } + + final int offset = getSelectionStart(); + final int line = mLayout.getLineForOffset(offset); + final int top = mLayout.getLineTop(line); + final int bottom = mLayout.getLineTop(line + 1); + + mCursorCount = mLayout.isLevelBoundary(offset) ? 2 : 1; + + int middle = bottom; + if (mCursorCount == 2) { + // Similar to what is done in {@link Layout.#getCursorPath(int, Path, CharSequence)} + middle = (top + bottom) >> 1; + } + + updateCursorPosition(0, top, middle, mLayout.getPrimaryHorizontal(offset)); + + if (mCursorCount == 2) { + updateCursorPosition(1, middle, bottom, mLayout.getSecondaryHorizontal(offset)); + } + } + + private void updateCursorPosition(int cursorIndex, int top, int bottom, float horizontal) { + if (mCursorDrawable[cursorIndex] == null) + mCursorDrawable[cursorIndex] = mContext.getResources().getDrawable(mCursorDrawableRes); + + if (mTempRect == null) mTempRect = new Rect(); + mCursorDrawable[cursorIndex].getPadding(mTempRect); + final int width = mCursorDrawable[cursorIndex].getIntrinsicWidth(); + horizontal = Math.max(0.5f, horizontal - 0.5f); + final int left = (int) (horizontal) - mTempRect.left; + mCursorDrawable[cursorIndex].setBounds(left, top - mTempRect.top, left + width, + bottom + mTempRect.bottom); } } } |
