1 /* 2 * Copyright (C) 2006 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package android.widget; 18 19 import com.android.internal.util.FastMath; 20 import com.android.internal.widget.EditableInputConnection; 21 22 import org.xmlpull.v1.XmlPullParserException; 23 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.pm.PackageManager; 27 import android.content.res.ColorStateList; 28 import android.content.res.Resources; 29 import android.content.res.TypedArray; 30 import android.content.res.XmlResourceParser; 31 import android.graphics.Canvas; 32 import android.graphics.Paint; 33 import android.graphics.Path; 34 import android.graphics.Rect; 35 import android.graphics.RectF; 36 import android.graphics.Typeface; 37 import android.graphics.drawable.Drawable; 38 import android.inputmethodservice.ExtractEditText; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.Message; 42 import android.os.Parcel; 43 import android.os.Parcelable; 44 import android.os.ResultReceiver; 45 import android.os.SystemClock; 46 import android.text.BoringLayout; 47 import android.text.ClipboardManager; 48 import android.text.DynamicLayout; 49 import android.text.Editable; 50 import android.text.GetChars; 51 import android.text.GraphicsOperations; 52 import android.text.InputFilter; 53 import android.text.InputType; 54 import android.text.Layout; 55 import android.text.ParcelableSpan; 56 import android.text.Selection; 57 import android.text.SpanWatcher; 58 import android.text.Spannable; 59 import android.text.SpannableString; 60 import android.text.Spanned; 61 import android.text.SpannedString; 62 import android.text.StaticLayout; 63 import android.text.TextPaint; 64 import android.text.TextUtils; 65 import android.text.TextWatcher; 66 import android.text.method.DateKeyListener; 67 import android.text.method.DateTimeKeyListener; 68 import android.text.method.DialerKeyListener; 69 import android.text.method.DigitsKeyListener; 70 import android.text.method.KeyListener; 71 import android.text.method.LinkMovementMethod; 72 import android.text.method.MetaKeyKeyListener; 73 import android.text.method.MovementMethod; 74 import android.text.method.PasswordTransformationMethod; 75 import android.text.method.SingleLineTransformationMethod; 76 import android.text.method.TextKeyListener; 77 import android.text.method.TimeKeyListener; 78 import android.text.method.TransformationMethod; 79 import android.text.style.ParagraphStyle; 80 import android.text.style.URLSpan; 81 import android.text.style.UpdateAppearance; 82 import android.text.util.Linkify; 83 import android.util.AttributeSet; 84 import android.util.FloatMath; 85 import android.util.Log; 86 import android.util.TypedValue; 87 import android.view.ContextMenu; 88 import android.view.Gravity; 89 import android.view.KeyEvent; 90 import android.view.LayoutInflater; 91 import android.view.MenuItem; 92 import android.view.MotionEvent; 93 import android.view.View; 94 import android.view.ViewConfiguration; 95 import android.view.ViewDebug; 96 import android.view.ViewGroup; 97 import android.view.ViewGroup.LayoutParams; 98 import android.view.ViewParent; 99 import android.view.ViewRoot; 100 import android.view.ViewTreeObserver; 101 import android.view.WindowManager; 102 import android.view.accessibility.AccessibilityEvent; 103 import android.view.accessibility.AccessibilityManager; 104 import android.view.animation.AnimationUtils; 105 import android.view.inputmethod.BaseInputConnection; 106 import android.view.inputmethod.CompletionInfo; 107 import android.view.inputmethod.EditorInfo; 108 import android.view.inputmethod.ExtractedText; 109 import android.view.inputmethod.ExtractedTextRequest; 110 import android.view.inputmethod.InputConnection; 111 import android.view.inputmethod.InputMethodManager; 112 import android.widget.RemoteViews.RemoteView; 113 114 import java.io.IOException; 115 import java.lang.ref.WeakReference; 116 import java.util.ArrayList; 117 118 /** 119 * Displays text to the user and optionally allows them to edit it. A TextView 120 * is a complete text editor, however the basic class is configured to not 121 * allow editing; see {@link EditText} for a subclass that configures the text 122 * view for editing. 123 * 124 * <p> 125 * <b>XML attributes</b> 126 * <p> 127 * See {@link android.R.styleable#TextView TextView Attributes}, 128 * {@link android.R.styleable#View View Attributes} 129 * 130 * @attr ref android.R.styleable#TextView_text 131 * @attr ref android.R.styleable#TextView_bufferType 132 * @attr ref android.R.styleable#TextView_hint 133 * @attr ref android.R.styleable#TextView_textColor 134 * @attr ref android.R.styleable#TextView_textColorHighlight 135 * @attr ref android.R.styleable#TextView_textColorHint 136 * @attr ref android.R.styleable#TextView_textAppearance 137 * @attr ref android.R.styleable#TextView_textColorLink 138 * @attr ref android.R.styleable#TextView_textSize 139 * @attr ref android.R.styleable#TextView_textScaleX 140 * @attr ref android.R.styleable#TextView_typeface 141 * @attr ref android.R.styleable#TextView_textStyle 142 * @attr ref android.R.styleable#TextView_cursorVisible 143 * @attr ref android.R.styleable#TextView_maxLines 144 * @attr ref android.R.styleable#TextView_maxHeight 145 * @attr ref android.R.styleable#TextView_lines 146 * @attr ref android.R.styleable#TextView_height 147 * @attr ref android.R.styleable#TextView_minLines 148 * @attr ref android.R.styleable#TextView_minHeight 149 * @attr ref android.R.styleable#TextView_maxEms 150 * @attr ref android.R.styleable#TextView_maxWidth 151 * @attr ref android.R.styleable#TextView_ems 152 * @attr ref android.R.styleable#TextView_width 153 * @attr ref android.R.styleable#TextView_minEms 154 * @attr ref android.R.styleable#TextView_minWidth 155 * @attr ref android.R.styleable#TextView_gravity 156 * @attr ref android.R.styleable#TextView_scrollHorizontally 157 * @attr ref android.R.styleable#TextView_password 158 * @attr ref android.R.styleable#TextView_singleLine 159 * @attr ref android.R.styleable#TextView_selectAllOnFocus 160 * @attr ref android.R.styleable#TextView_includeFontPadding 161 * @attr ref android.R.styleable#TextView_maxLength 162 * @attr ref android.R.styleable#TextView_shadowColor 163 * @attr ref android.R.styleable#TextView_shadowDx 164 * @attr ref android.R.styleable#TextView_shadowDy 165 * @attr ref android.R.styleable#TextView_shadowRadius 166 * @attr ref android.R.styleable#TextView_autoLink 167 * @attr ref android.R.styleable#TextView_linksClickable 168 * @attr ref android.R.styleable#TextView_numeric 169 * @attr ref android.R.styleable#TextView_digits 170 * @attr ref android.R.styleable#TextView_phoneNumber 171 * @attr ref android.R.styleable#TextView_inputMethod 172 * @attr ref android.R.styleable#TextView_capitalize 173 * @attr ref android.R.styleable#TextView_autoText 174 * @attr ref android.R.styleable#TextView_editable 175 * @attr ref android.R.styleable#TextView_freezesText 176 * @attr ref android.R.styleable#TextView_ellipsize 177 * @attr ref android.R.styleable#TextView_drawableTop 178 * @attr ref android.R.styleable#TextView_drawableBottom 179 * @attr ref android.R.styleable#TextView_drawableRight 180 * @attr ref android.R.styleable#TextView_drawableLeft 181 * @attr ref android.R.styleable#TextView_drawablePadding 182 * @attr ref android.R.styleable#TextView_lineSpacingExtra 183 * @attr ref android.R.styleable#TextView_lineSpacingMultiplier 184 * @attr ref android.R.styleable#TextView_marqueeRepeatLimit 185 * @attr ref android.R.styleable#TextView_inputType 186 * @attr ref android.R.styleable#TextView_imeOptions 187 * @attr ref android.R.styleable#TextView_privateImeOptions 188 * @attr ref android.R.styleable#TextView_imeActionLabel 189 * @attr ref android.R.styleable#TextView_imeActionId 190 * @attr ref android.R.styleable#TextView_editorExtras 191 */ 192 @RemoteView 193 public class TextView extends View implements ViewTreeObserver.OnPreDrawListener { 194 static final String LOG_TAG = "TextView"; 195 static final boolean DEBUG_EXTRACT = false; 196 197 private static int PRIORITY = 100; 198 199 final int[] mTempCoords = new int[2]; 200 Rect mTempRect; 201 202 private ColorStateList mTextColor; 203 private int mCurTextColor; 204 private ColorStateList mHintTextColor; 205 private ColorStateList mLinkTextColor; 206 private int mCurHintTextColor; 207 private boolean mFreezesText; 208 private boolean mFrozenWithFocus; 209 private boolean mTemporaryDetach; 210 private boolean mDispatchTemporaryDetach; 211 212 private boolean mEatTouchRelease = false; 213 private boolean mScrolled = false; 214 215 private Editable.Factory mEditableFactory = Editable.Factory.getInstance(); 216 private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance(); 217 218 private float mShadowRadius, mShadowDx, mShadowDy; 219 220 private static final int PREDRAW_NOT_REGISTERED = 0; 221 private static final int PREDRAW_PENDING = 1; 222 private static final int PREDRAW_DONE = 2; 223 private int mPreDrawState = PREDRAW_NOT_REGISTERED; 224 225 private TextUtils.TruncateAt mEllipsize = null; 226 227 // Enum for the "typeface" XML parameter. 228 // TODO: How can we get this from the XML instead of hardcoding it here? 229 private static final int SANS = 1; 230 private static final int SERIF = 2; 231 private static final int MONOSPACE = 3; 232 233 // Bitfield for the "numeric" XML parameter. 234 // TODO: How can we get this from the XML instead of hardcoding it here? 235 private static final int SIGNED = 2; 236 private static final int DECIMAL = 4; 237 238 class Drawables { 239 final Rect mCompoundRect = new Rect(); 240 Drawable mDrawableTop, mDrawableBottom, mDrawableLeft, mDrawableRight; 241 int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight; 242 int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight; 243 int mDrawablePadding; 244 } 245 private Drawables mDrawables; 246 247 private CharSequence mError; 248 private boolean mErrorWasChanged; 249 private ErrorPopup mPopup; 250 /** 251 * This flag is set if the TextView tries to display an error before it 252 * is attached to the window (so its position is still unknown). 253 * It causes the error to be shown later, when onAttachedToWindow() 254 * is called. 255 */ 256 private boolean mShowErrorAfterAttach; 257 258 private CharWrapper mCharWrapper = null; 259 260 private boolean mSelectionMoved = false; 261 private boolean mTouchFocusSelected = false; 262 263 private Marquee mMarquee; 264 private boolean mRestartMarquee; 265 266 private int mMarqueeRepeatLimit = 3; 267 268 class InputContentType { 269 int imeOptions = EditorInfo.IME_NULL; 270 String privateImeOptions; 271 CharSequence imeActionLabel; 272 int imeActionId; 273 Bundle extras; 274 OnEditorActionListener onEditorActionListener; 275 boolean enterDown; 276 } 277 InputContentType mInputContentType; 278 279 class InputMethodState { 280 Rect mCursorRectInWindow = new Rect(); 281 RectF mTmpRectF = new RectF(); 282 float[] mTmpOffset = new float[2]; 283 ExtractedTextRequest mExtracting; 284 final ExtractedText mTmpExtracted = new ExtractedText(); 285 int mBatchEditNesting; 286 boolean mCursorChanged; 287 boolean mSelectionModeChanged; 288 boolean mContentChanged; 289 int mChangedStart, mChangedEnd, mChangedDelta; 290 } 291 InputMethodState mInputMethodState; 292 293 int mTextSelectHandleLeftRes; 294 int mTextSelectHandleRightRes; 295 int mTextSelectHandleRes; 296 297 Drawable mSelectHandleLeft; 298 Drawable mSelectHandleRight; 299 Drawable mSelectHandleCenter; 300 301 // Set when this TextView gained focus with some text selected. Will start selection mode. 302 private boolean mCreatedWithASelection = false; 303 304 private boolean mNoContextMenuOnUp = false; 305 306 /* 307 * Kick-start the font cache for the zygote process (to pay the cost of 308 * initializing freetype for our default font only once). 309 */ 310 static { 311 Paint p = new Paint(); 312 p.setAntiAlias(true); 313 // We don't care about the result, just the side-effect of measuring. 314 p.measureText("H"); 315 } 316 317 /** 318 * Interface definition for a callback to be invoked when an action is 319 * performed on the editor. 320 */ 321 public interface OnEditorActionListener { 322 /** 323 * Called when an action is being performed. 324 * 325 * @param v The view that was clicked. 326 * @param actionId Identifier of the action. This will be either the 327 * identifier you supplied, or {@link EditorInfo#IME_NULL 328 * EditorInfo.IME_NULL} if being called due to the enter key 329 * being pressed. 330 * @param event If triggered by an enter key, this is the event; 331 * otherwise, this is null. 332 * @return Return true if you have consumed the action, else false. 333 */ 334 boolean onEditorAction(TextView v, int actionId, KeyEvent event); 335 } 336 337 public TextView(Context context) { 338 this(context, null); 339 } 340 341 public TextView(Context context, 342 AttributeSet attrs) { 343 this(context, attrs, com.android.internal.R.attr.textViewStyle); 344 } 345 346 @SuppressWarnings("deprecation") 347 public TextView(Context context, 348 AttributeSet attrs, 349 int defStyle) { 350 super(context, attrs, defStyle); 351 mText = ""; 352 353 mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); 354 mTextPaint.density = getResources().getDisplayMetrics().density; 355 mTextPaint.setCompatibilityScaling( 356 getResources().getCompatibilityInfo().applicationScale); 357 358 // If we get the paint from the skin, we should set it to left, since 359 // the layout always wants it to be left. 360 // mTextPaint.setTextAlign(Paint.Align.LEFT); 361 362 mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG); 363 mHighlightPaint.setCompatibilityScaling( 364 getResources().getCompatibilityInfo().applicationScale); 365 366 mMovement = getDefaultMovementMethod(); 367 mTransformation = null; 368 369 TypedArray a = 370 context.obtainStyledAttributes( 371 attrs, com.android.internal.R.styleable.TextView, defStyle, 0); 372 373 int textColorHighlight = 0; 374 ColorStateList textColor = null; 375 ColorStateList textColorHint = null; 376 ColorStateList textColorLink = null; 377 int textSize = 15; 378 int typefaceIndex = -1; 379 int styleIndex = -1; 380 381 /* 382 * Look the appearance up without checking first if it exists because 383 * almost every TextView has one and it greatly simplifies the logic 384 * to be able to parse the appearance first and then let specific tags 385 * for this View override it. 386 */ 387 TypedArray appearance = null; 388 int ap = a.getResourceId(com.android.internal.R.styleable.TextView_textAppearance, -1); 389 if (ap != -1) { 390 appearance = context.obtainStyledAttributes(ap, 391 com.android.internal.R.styleable. 392 TextAppearance); 393 } 394 if (appearance != null) { 395 int n = appearance.getIndexCount(); 396 for (int i = 0; i < n; i++) { 397 int attr = appearance.getIndex(i); 398 399 switch (attr) { 400 case com.android.internal.R.styleable.TextAppearance_textColorHighlight: 401 textColorHighlight = appearance.getColor(attr, textColorHighlight); 402 break; 403 404 case com.android.internal.R.styleable.TextAppearance_textColor: 405 textColor = appearance.getColorStateList(attr); 406 break; 407 408 case com.android.internal.R.styleable.TextAppearance_textColorHint: 409 textColorHint = appearance.getColorStateList(attr); 410 break; 411 412 case com.android.internal.R.styleable.TextAppearance_textColorLink: 413 textColorLink = appearance.getColorStateList(attr); 414 break; 415 416 case com.android.internal.R.styleable.TextAppearance_textSize: 417 textSize = appearance.getDimensionPixelSize(attr, textSize); 418 break; 419 420 case com.android.internal.R.styleable.TextAppearance_typeface: 421 typefaceIndex = appearance.getInt(attr, -1); 422 break; 423 424 case com.android.internal.R.styleable.TextAppearance_textStyle: 425 styleIndex = appearance.getInt(attr, -1); 426 break; 427 } 428 } 429 430 appearance.recycle(); 431 } 432 433 boolean editable = getDefaultEditable(); 434 CharSequence inputMethod = null; 435 int numeric = 0; 436 CharSequence digits = null; 437 boolean phone = false; 438 boolean autotext = false; 439 int autocap = -1; 440 int buffertype = 0; 441 boolean selectallonfocus = false; 442 Drawable drawableLeft = null, drawableTop = null, drawableRight = null, 443 drawableBottom = null; 444 int drawablePadding = 0; 445 int ellipsize = -1; 446 boolean singleLine = false; 447 int maxlength = -1; 448 CharSequence text = ""; 449 CharSequence hint = null; 450 int shadowcolor = 0; 451 float dx = 0, dy = 0, r = 0; 452 boolean password = false; 453 int inputType = EditorInfo.TYPE_NULL; 454 455 int n = a.getIndexCount(); 456 for (int i = 0; i < n; i++) { 457 int attr = a.getIndex(i); 458 459 switch (attr) { 460 case com.android.internal.R.styleable.TextView_editable: 461 editable = a.getBoolean(attr, editable); 462 break; 463 464 case com.android.internal.R.styleable.TextView_inputMethod: 465 inputMethod = a.getText(attr); 466 break; 467 468 case com.android.internal.R.styleable.TextView_numeric: 469 numeric = a.getInt(attr, numeric); 470 break; 471 472 case com.android.internal.R.styleable.TextView_digits: 473 digits = a.getText(attr); 474 break; 475 476 case com.android.internal.R.styleable.TextView_phoneNumber: 477 phone = a.getBoolean(attr, phone); 478 break; 479 480 case com.android.internal.R.styleable.TextView_autoText: 481 autotext = a.getBoolean(attr, autotext); 482 break; 483 484 case com.android.internal.R.styleable.TextView_capitalize: 485 autocap = a.getInt(attr, autocap); 486 break; 487 488 case com.android.internal.R.styleable.TextView_bufferType: 489 buffertype = a.getInt(attr, buffertype); 490 break; 491 492 case com.android.internal.R.styleable.TextView_selectAllOnFocus: 493 selectallonfocus = a.getBoolean(attr, selectallonfocus); 494 break; 495 496 case com.android.internal.R.styleable.TextView_autoLink: 497 mAutoLinkMask = a.getInt(attr, 0); 498 break; 499 500 case com.android.internal.R.styleable.TextView_linksClickable: 501 mLinksClickable = a.getBoolean(attr, true); 502 break; 503 504 case com.android.internal.R.styleable.TextView_drawableLeft: 505 drawableLeft = a.getDrawable(attr); 506 break; 507 508 case com.android.internal.R.styleable.TextView_drawableTop: 509 drawableTop = a.getDrawable(attr); 510 break; 511 512 case com.android.internal.R.styleable.TextView_drawableRight: 513 drawableRight = a.getDrawable(attr); 514 break; 515 516 case com.android.internal.R.styleable.TextView_drawableBottom: 517 drawableBottom = a.getDrawable(attr); 518 break; 519 520 case com.android.internal.R.styleable.TextView_drawablePadding: 521 drawablePadding = a.getDimensionPixelSize(attr, drawablePadding); 522 break; 523 524 case com.android.internal.R.styleable.TextView_maxLines: 525 setMaxLines(a.getInt(attr, -1)); 526 break; 527 528 case com.android.internal.R.styleable.TextView_maxHeight: 529 setMaxHeight(a.getDimensionPixelSize(attr, -1)); 530 break; 531 532 case com.android.internal.R.styleable.TextView_lines: 533 setLines(a.getInt(attr, -1)); 534 break; 535 536 case com.android.internal.R.styleable.TextView_height: 537 setHeight(a.getDimensionPixelSize(attr, -1)); 538 break; 539 540 case com.android.internal.R.styleable.TextView_minLines: 541 setMinLines(a.getInt(attr, -1)); 542 break; 543 544 case com.android.internal.R.styleable.TextView_minHeight: 545 setMinHeight(a.getDimensionPixelSize(attr, -1)); 546 break; 547 548 case com.android.internal.R.styleable.TextView_maxEms: 549 setMaxEms(a.getInt(attr, -1)); 550 break; 551 552 case com.android.internal.R.styleable.TextView_maxWidth: 553 setMaxWidth(a.getDimensionPixelSize(attr, -1)); 554 break; 555 556 case com.android.internal.R.styleable.TextView_ems: 557 setEms(a.getInt(attr, -1)); 558 break; 559 560 case com.android.internal.R.styleable.TextView_width: 561 setWidth(a.getDimensionPixelSize(attr, -1)); 562 break; 563 564 case com.android.internal.R.styleable.TextView_minEms: 565 setMinEms(a.getInt(attr, -1)); 566 break; 567 568 case com.android.internal.R.styleable.TextView_minWidth: 569 setMinWidth(a.getDimensionPixelSize(attr, -1)); 570 break; 571 572 case com.android.internal.R.styleable.TextView_gravity: 573 setGravity(a.getInt(attr, -1)); 574 break; 575 576 case com.android.internal.R.styleable.TextView_hint: 577 hint = a.getText(attr); 578 break; 579 580 case com.android.internal.R.styleable.TextView_text: 581 text = a.getText(attr); 582 break; 583 584 case com.android.internal.R.styleable.TextView_scrollHorizontally: 585 if (a.getBoolean(attr, false)) { 586 setHorizontallyScrolling(true); 587 } 588 break; 589 590 case com.android.internal.R.styleable.TextView_singleLine: 591 singleLine = a.getBoolean(attr, singleLine); 592 break; 593 594 case com.android.internal.R.styleable.TextView_ellipsize: 595 ellipsize = a.getInt(attr, ellipsize); 596 break; 597 598 case com.android.internal.R.styleable.TextView_marqueeRepeatLimit: 599 setMarqueeRepeatLimit(a.getInt(attr, mMarqueeRepeatLimit)); 600 break; 601 602 case com.android.internal.R.styleable.TextView_includeFontPadding: 603 if (!a.getBoolean(attr, true)) { 604 setIncludeFontPadding(false); 605 } 606 break; 607 608 case com.android.internal.R.styleable.TextView_cursorVisible: 609 if (!a.getBoolean(attr, true)) { 610 setCursorVisible(false); 611 } 612 break; 613 614 case com.android.internal.R.styleable.TextView_maxLength: 615 maxlength = a.getInt(attr, -1); 616 break; 617 618 case com.android.internal.R.styleable.TextView_textScaleX: 619 setTextScaleX(a.getFloat(attr, 1.0f)); 620 break; 621 622 case com.android.internal.R.styleable.TextView_freezesText: 623 mFreezesText = a.getBoolean(attr, false); 624 break; 625 626 case com.android.internal.R.styleable.TextView_shadowColor: 627 shadowcolor = a.getInt(attr, 0); 628 break; 629 630 case com.android.internal.R.styleable.TextView_shadowDx: 631 dx = a.getFloat(attr, 0); 632 break; 633 634 case com.android.internal.R.styleable.TextView_shadowDy: 635 dy = a.getFloat(attr, 0); 636 break; 637 638 case com.android.internal.R.styleable.TextView_shadowRadius: 639 r = a.getFloat(attr, 0); 640 break; 641 642 case com.android.internal.R.styleable.TextView_enabled: 643 setEnabled(a.getBoolean(attr, isEnabled())); 644 break; 645 646 case com.android.internal.R.styleable.TextView_textColorHighlight: 647 textColorHighlight = a.getColor(attr, textColorHighlight); 648 break; 649 650 case com.android.internal.R.styleable.TextView_textColor: 651 textColor = a.getColorStateList(attr); 652 break; 653 654 case com.android.internal.R.styleable.TextView_textColorHint: 655 textColorHint = a.getColorStateList(attr); 656 break; 657 658 case com.android.internal.R.styleable.TextView_textColorLink: 659 textColorLink = a.getColorStateList(attr); 660 break; 661 662 case com.android.internal.R.styleable.TextView_textSize: 663 textSize = a.getDimensionPixelSize(attr, textSize); 664 break; 665 666 case com.android.internal.R.styleable.TextView_typeface: 667 typefaceIndex = a.getInt(attr, typefaceIndex); 668 break; 669 670 case com.android.internal.R.styleable.TextView_textStyle: 671 styleIndex = a.getInt(attr, styleIndex); 672 break; 673 674 case com.android.internal.R.styleable.TextView_password: 675 password = a.getBoolean(attr, password); 676 break; 677 678 case com.android.internal.R.styleable.TextView_lineSpacingExtra: 679 mSpacingAdd = a.getDimensionPixelSize(attr, (int) mSpacingAdd); 680 break; 681 682 case com.android.internal.R.styleable.TextView_lineSpacingMultiplier: 683 mSpacingMult = a.getFloat(attr, mSpacingMult); 684 break; 685 686 case com.android.internal.R.styleable.TextView_inputType: 687 inputType = a.getInt(attr, mInputType); 688 break; 689 690 case com.android.internal.R.styleable.TextView_imeOptions: 691 if (mInputContentType == null) { 692 mInputContentType = new InputContentType(); 693 } 694 mInputContentType.imeOptions = a.getInt(attr, 695 mInputContentType.imeOptions); 696 break; 697 698 case com.android.internal.R.styleable.TextView_imeActionLabel: 699 if (mInputContentType == null) { 700 mInputContentType = new InputContentType(); 701 } 702 mInputContentType.imeActionLabel = a.getText(attr); 703 break; 704 705 case com.android.internal.R.styleable.TextView_imeActionId: 706 if (mInputContentType == null) { 707 mInputContentType = new InputContentType(); 708 } 709 mInputContentType.imeActionId = a.getInt(attr, 710 mInputContentType.imeActionId); 711 break; 712 713 case com.android.internal.R.styleable.TextView_privateImeOptions: 714 setPrivateImeOptions(a.getString(attr)); 715 break; 716 717 case com.android.internal.R.styleable.TextView_editorExtras: 718 try { 719 setInputExtras(a.getResourceId(attr, 0)); 720 } catch (XmlPullParserException e) { 721 Log.w(LOG_TAG, "Failure reading input extras", e); 722 } catch (IOException e) { 723 Log.w(LOG_TAG, "Failure reading input extras", e); 724 } 725 break; 726 727 case com.android.internal.R.styleable.TextView_textSelectHandleLeft: 728 mTextSelectHandleLeftRes = a.getResourceId(attr, 0); 729 break; 730 731 case com.android.internal.R.styleable.TextView_textSelectHandleRight: 732 mTextSelectHandleRightRes = a.getResourceId(attr, 0); 733 break; 734 735 case com.android.internal.R.styleable.TextView_textSelectHandle: 736 mTextSelectHandleRes = a.getResourceId(attr, 0); 737 break; 738 } 739 } 740 a.recycle(); 741 742 BufferType bufferType = BufferType.EDITABLE; 743 744 if ((inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) 745 == (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) { 746 password = true; 747 } 748 749 if (inputMethod != null) { 750 Class<?> c; 751 752 try { 753 c = Class.forName(inputMethod.toString()); 754 } catch (ClassNotFoundException ex) { 755 throw new RuntimeException(ex); 756 } 757 758 try { 759 mInput = (KeyListener) c.newInstance(); 760 } catch (InstantiationException ex) { 761 throw new RuntimeException(ex); 762 } catch (IllegalAccessException ex) { 763 throw new RuntimeException(ex); 764 } 765 try { 766 mInputType = inputType != EditorInfo.TYPE_NULL 767 ? inputType 768 : mInput.getInputType(); 769 } catch (IncompatibleClassChangeError e) { 770 mInputType = EditorInfo.TYPE_CLASS_TEXT; 771 } 772 } else if (digits != null) { 773 mInput = DigitsKeyListener.getInstance(digits.toString()); 774 // If no input type was specified, we will default to generic 775 // text, since we can't tell the IME about the set of digits 776 // that was selected. 777 mInputType = inputType != EditorInfo.TYPE_NULL 778 ? inputType : EditorInfo.TYPE_CLASS_TEXT; 779 } else if (inputType != EditorInfo.TYPE_NULL) { 780 setInputType(inputType, true); 781 singleLine = (inputType&(EditorInfo.TYPE_MASK_CLASS 782 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE)) != 783 (EditorInfo.TYPE_CLASS_TEXT 784 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); 785 } else if (phone) { 786 mInput = DialerKeyListener.getInstance(); 787 mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE; 788 } else if (numeric != 0) { 789 mInput = DigitsKeyListener.getInstance((numeric & SIGNED) != 0, 790 (numeric & DECIMAL) != 0); 791 inputType = EditorInfo.TYPE_CLASS_NUMBER; 792 if ((numeric & SIGNED) != 0) { 793 inputType |= EditorInfo.TYPE_NUMBER_FLAG_SIGNED; 794 } 795 if ((numeric & DECIMAL) != 0) { 796 inputType |= EditorInfo.TYPE_NUMBER_FLAG_DECIMAL; 797 } 798 mInputType = inputType; 799 } else if (autotext || autocap != -1) { 800 TextKeyListener.Capitalize cap; 801 802 inputType = EditorInfo.TYPE_CLASS_TEXT; 803 if (!singleLine) { 804 inputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 805 } 806 807 switch (autocap) { 808 case 1: 809 cap = TextKeyListener.Capitalize.SENTENCES; 810 inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES; 811 break; 812 813 case 2: 814 cap = TextKeyListener.Capitalize.WORDS; 815 inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS; 816 break; 817 818 case 3: 819 cap = TextKeyListener.Capitalize.CHARACTERS; 820 inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS; 821 break; 822 823 default: 824 cap = TextKeyListener.Capitalize.NONE; 825 break; 826 } 827 828 mInput = TextKeyListener.getInstance(autotext, cap); 829 mInputType = inputType; 830 } else if (editable) { 831 mInput = TextKeyListener.getInstance(); 832 mInputType = EditorInfo.TYPE_CLASS_TEXT; 833 if (!singleLine) { 834 mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 835 } 836 } else { 837 mInput = null; 838 839 switch (buffertype) { 840 case 0: 841 bufferType = BufferType.NORMAL; 842 break; 843 case 1: 844 bufferType = BufferType.SPANNABLE; 845 break; 846 case 2: 847 bufferType = BufferType.EDITABLE; 848 break; 849 } 850 } 851 852 if (password && (mInputType&EditorInfo.TYPE_MASK_CLASS) 853 == EditorInfo.TYPE_CLASS_TEXT) { 854 mInputType = (mInputType & ~(EditorInfo.TYPE_MASK_VARIATION)) 855 | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD; 856 } 857 858 if (selectallonfocus) { 859 mSelectAllOnFocus = true; 860 861 if (bufferType == BufferType.NORMAL) 862 bufferType = BufferType.SPANNABLE; 863 } 864 865 setCompoundDrawablesWithIntrinsicBounds( 866 drawableLeft, drawableTop, drawableRight, drawableBottom); 867 setCompoundDrawablePadding(drawablePadding); 868 869 if (singleLine) { 870 setSingleLine(); 871 872 if (mInput == null && ellipsize < 0) { 873 ellipsize = 3; // END 874 } 875 } 876 877 switch (ellipsize) { 878 case 1: 879 setEllipsize(TextUtils.TruncateAt.START); 880 break; 881 case 2: 882 setEllipsize(TextUtils.TruncateAt.MIDDLE); 883 break; 884 case 3: 885 setEllipsize(TextUtils.TruncateAt.END); 886 break; 887 case 4: 888 setHorizontalFadingEdgeEnabled(true); 889 setEllipsize(TextUtils.TruncateAt.MARQUEE); 890 break; 891 } 892 893 setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000)); 894 setHintTextColor(textColorHint); 895 setLinkTextColor(textColorLink); 896 if (textColorHighlight != 0) { 897 setHighlightColor(textColorHighlight); 898 } 899 setRawTextSize(textSize); 900 901 if (password) { 902 setTransformationMethod(PasswordTransformationMethod.getInstance()); 903 typefaceIndex = MONOSPACE; 904 } else if ((mInputType&(EditorInfo.TYPE_MASK_CLASS 905 |EditorInfo.TYPE_MASK_VARIATION)) 906 == (EditorInfo.TYPE_CLASS_TEXT 907 |EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) { 908 typefaceIndex = MONOSPACE; 909 } 910 911 setTypefaceByIndex(typefaceIndex, styleIndex); 912 913 if (shadowcolor != 0) { 914 setShadowLayer(r, dx, dy, shadowcolor); 915 } 916 917 if (maxlength >= 0) { 918 setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) }); 919 } else { 920 setFilters(NO_FILTERS); 921 } 922 923 setText(text, bufferType); 924 if (hint != null) setHint(hint); 925 926 /* 927 * Views are not normally focusable unless specified to be. 928 * However, TextViews that have input or movement methods *are* 929 * focusable by default. 930 */ 931 a = context.obtainStyledAttributes(attrs, 932 com.android.internal.R.styleable.View, 933 defStyle, 0); 934 935 boolean focusable = mMovement != null || mInput != null; 936 boolean clickable = focusable; 937 boolean longClickable = focusable; 938 939 n = a.getIndexCount(); 940 for (int i = 0; i < n; i++) { 941 int attr = a.getIndex(i); 942 943 switch (attr) { 944 case com.android.internal.R.styleable.View_focusable: 945 focusable = a.getBoolean(attr, focusable); 946 break; 947 948 case com.android.internal.R.styleable.View_clickable: 949 clickable = a.getBoolean(attr, clickable); 950 break; 951 952 case com.android.internal.R.styleable.View_longClickable: 953 longClickable = a.getBoolean(attr, longClickable); 954 break; 955 } 956 } 957 a.recycle(); 958 959 setFocusable(focusable); 960 setClickable(clickable); 961 setLongClickable(longClickable); 962 963 prepareCursorControllers(); 964 } 965 966 private void setTypefaceByIndex(int typefaceIndex, int styleIndex) { 967 Typeface tf = null; 968 switch (typefaceIndex) { 969 case SANS: 970 tf = Typeface.SANS_SERIF; 971 break; 972 973 case SERIF: 974 tf = Typeface.SERIF; 975 break; 976 977 case MONOSPACE: 978 tf = Typeface.MONOSPACE; 979 break; 980 } 981 982 setTypeface(tf, styleIndex); 983 } 984 985 /** 986 * Sets the typeface and style in which the text should be displayed, 987 * and turns on the fake bold and italic bits in the Paint if the 988 * Typeface that you provided does not have all the bits in the 989 * style that you specified. 990 * 991 * @attr ref android.R.styleable#TextView_typeface 992 * @attr ref android.R.styleable#TextView_textStyle 993 */ 994 public void setTypeface(Typeface tf, int style) { 995 if (style > 0) { 996 if (tf == null) { 997 tf = Typeface.defaultFromStyle(style); 998 } else { 999 tf = Typeface.create(tf, style); 1000 } 1001 1002 setTypeface(tf); 1003 // now compute what (if any) algorithmic styling is needed 1004 int typefaceStyle = tf != null ? tf.getStyle() : 0; 1005 int need = style & ~typefaceStyle; 1006 mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0); 1007 mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0); 1008 } else { 1009 mTextPaint.setFakeBoldText(false); 1010 mTextPaint.setTextSkewX(0); 1011 setTypeface(tf); 1012 } 1013 } 1014 1015 /** 1016 * Subclasses override this to specify that they have a KeyListener 1017 * by default even if not specifically called for in the XML options. 1018 */ 1019 protected boolean getDefaultEditable() { 1020 return false; 1021 } 1022 1023 /** 1024 * Subclasses override this to specify a default movement method. 1025 */ 1026 protected MovementMethod getDefaultMovementMethod() { 1027 return null; 1028 } 1029 1030 /** 1031 * Return the text the TextView is displaying. If setText() was called with 1032 * an argument of BufferType.SPANNABLE or BufferType.EDITABLE, you can cast 1033 * the return value from this method to Spannable or Editable, respectively. 1034 * 1035 * Note: The content of the return value should not be modified. If you want 1036 * a modifiable one, you should make your own copy first. 1037 */ 1038 @ViewDebug.CapturedViewProperty 1039 public CharSequence getText() { 1040 return mText; 1041 } 1042 1043 /** 1044 * Returns the length, in characters, of the text managed by this TextView 1045 */ 1046 public int length() { 1047 return mText.length(); 1048 } 1049 1050 /** 1051 * Return the text the TextView is displaying as an Editable object. If 1052 * the text is not editable, null is returned. 1053 * 1054 * @see #getText 1055 */ 1056 public Editable getEditableText() { 1057 return (mText instanceof Editable) ? (Editable)mText : null; 1058 } 1059 1060 /** 1061 * @return the height of one standard line in pixels. Note that markup 1062 * within the text can cause individual lines to be taller or shorter 1063 * than this height, and the layout may contain additional first- 1064 * or last-line padding. 1065 */ 1066 public int getLineHeight() { 1067 return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult 1068 + mSpacingAdd); 1069 } 1070 1071 /** 1072 * @return the Layout that is currently being used to display the text. 1073 * This can be null if the text or width has recently changes. 1074 */ 1075 public final Layout getLayout() { 1076 return mLayout; 1077 } 1078 1079 /** 1080 * @return the current key listener for this TextView. 1081 * This will frequently be null for non-EditText TextViews. 1082 */ 1083 public final KeyListener getKeyListener() { 1084 return mInput; 1085 } 1086 1087 /** 1088 * Sets the key listener to be used with this TextView. This can be null 1089 * to disallow user input. Note that this method has significant and 1090 * subtle interactions with soft keyboards and other input method: 1091 * see {@link KeyListener#getInputType() KeyListener.getContentType()} 1092 * for important details. Calling this method will replace the current 1093 * content type of the text view with the content type returned by the 1094 * key listener. 1095 * <p> 1096 * Be warned that if you want a TextView with a key listener or movement 1097 * method not to be focusable, or if you want a TextView without a 1098 * key listener or movement method to be focusable, you must call 1099 * {@link #setFocusable} again after calling this to get the focusability 1100 * back the way you want it. 1101 * 1102 * @attr ref android.R.styleable#TextView_numeric 1103 * @attr ref android.R.styleable#TextView_digits 1104 * @attr ref android.R.styleable#TextView_phoneNumber 1105 * @attr ref android.R.styleable#TextView_inputMethod 1106 * @attr ref android.R.styleable#TextView_capitalize 1107 * @attr ref android.R.styleable#TextView_autoText 1108 */ 1109 public void setKeyListener(KeyListener input) { 1110 setKeyListenerOnly(input); 1111 fixFocusableAndClickableSettings(); 1112 1113 if (input != null) { 1114 try { 1115 mInputType = mInput.getInputType(); 1116 } catch (IncompatibleClassChangeError e) { 1117 mInputType = EditorInfo.TYPE_CLASS_TEXT; 1118 } 1119 if ((mInputType&EditorInfo.TYPE_MASK_CLASS) 1120 == EditorInfo.TYPE_CLASS_TEXT) { 1121 if (mSingleLine) { 1122 mInputType &= ~EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 1123 } else { 1124 mInputType |= EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE; 1125 } 1126 } 1127 } else { 1128 mInputType = EditorInfo.TYPE_NULL; 1129 } 1130 1131 InputMethodManager imm = InputMethodManager.peekInstance(); 1132 if (imm != null) imm.restartInput(this); 1133 } 1134 1135 private void setKeyListenerOnly(KeyListener input) { 1136 mInput = input; 1137 if (mInput != null && !(mText instanceof Editable)) 1138 setText(mText); 1139 1140 setFilters((Editable) mText, mFilters); 1141 } 1142 1143 /** 1144 * @return the movement method being used for this TextView. 1145 * This will frequently be null for non-EditText TextViews. 1146 */ 1147 public final MovementMethod getMovementMethod() { 1148 return mMovement; 1149 } 1150 1151 /** 1152 * Sets the movement method (arrow key handler) to be used for 1153 * this TextView. This can be null to disallow using the arrow keys 1154 * to move the cursor or scroll the view. 1155 * <p> 1156 * Be warned that if you want a TextView with a key listener or movement 1157 * method not to be focusable, or if you want a TextView without a 1158 * key listener or movement method to be focusable, you must call 1159 * {@link #setFocusable} again after calling this to get the focusability 1160 * back the way you want it. 1161 */ 1162 public final void setMovementMethod(MovementMethod movement) { 1163 mMovement = movement; 1164 1165 if (mMovement != null && !(mText instanceof Spannable)) 1166 setText(mText); 1167 1168 fixFocusableAndClickableSettings(); 1169 1170 // SelectionModifierCursorController depends on textCanBeSelected, which depends on mMovement 1171 prepareCursorControllers(); 1172 } 1173 1174 private void fixFocusableAndClickableSettings() { 1175 if ((mMovement != null) || mInput != null) { 1176 setFocusable(true); 1177 setClickable(true); 1178 setLongClickable(true); 1179 } else { 1180 setFocusable(false); 1181 setClickable(false); 1182 setLongClickable(false); 1183 } 1184 } 1185 1186 /** 1187 * @return the current transformation method for this TextView. 1188 * This will frequently be null except for single-line and password 1189 * fields. 1190 */ 1191 public final TransformationMethod getTransformationMethod() { 1192 return mTransformation; 1193 } 1194 1195 /** 1196 * Sets the transformation that is applied to the text that this 1197 * TextView is displaying. 1198 * 1199 * @attr ref android.R.styleable#TextView_password 1200 * @attr ref android.R.styleable#TextView_singleLine 1201 */ 1202 public final void setTransformationMethod(TransformationMethod method) { 1203 if (method == mTransformation) { 1204 // Avoid the setText() below if the transformation is 1205 // the same. 1206 return; 1207 } 1208 if (mTransformation != null) { 1209 if (mText instanceof Spannable) { 1210 ((Spannable) mText).removeSpan(mTransformation); 1211 } 1212 } 1213 1214 mTransformation = method; 1215 1216 setText(mText); 1217 } 1218 1219 /** 1220 * Returns the top padding of the view, plus space for the top 1221 * Drawable if any. 1222 */ 1223 public int getCompoundPaddingTop() { 1224 final Drawables dr = mDrawables; 1225 if (dr == null || dr.mDrawableTop == null) { 1226 return mPaddingTop; 1227 } else { 1228 return mPaddingTop + dr.mDrawablePadding + dr.mDrawableSizeTop; 1229 } 1230 } 1231 1232 /** 1233 * Returns the bottom padding of the view, plus space for the bottom 1234 * Drawable if any. 1235 */ 1236 public int getCompoundPaddingBottom() { 1237 final Drawables dr = mDrawables; 1238 if (dr == null || dr.mDrawableBottom == null) { 1239 return mPaddingBottom; 1240 } else { 1241 return mPaddingBottom + dr.mDrawablePadding + dr.mDrawableSizeBottom; 1242 } 1243 } 1244 1245 /** 1246 * Returns the left padding of the view, plus space for the left 1247 * Drawable if any. 1248 */ 1249 public int getCompoundPaddingLeft() { 1250 final Drawables dr = mDrawables; 1251 if (dr == null || dr.mDrawableLeft == null) { 1252 return mPaddingLeft; 1253 } else { 1254 return mPaddingLeft + dr.mDrawablePadding + dr.mDrawableSizeLeft; 1255 } 1256 } 1257 1258 /** 1259 * Returns the right padding of the view, plus space for the right 1260 * Drawable if any. 1261 */ 1262 public int getCompoundPaddingRight() { 1263 final Drawables dr = mDrawables; 1264 if (dr == null || dr.mDrawableRight == null) { 1265 return mPaddingRight; 1266 } else { 1267 return mPaddingRight + dr.mDrawablePadding + dr.mDrawableSizeRight; 1268 } 1269 } 1270 1271 /** 1272 * Returns the extended top padding of the view, including both the 1273 * top Drawable if any and any extra space to keep more than maxLines 1274 * of text from showing. It is only valid to call this after measuring. 1275 */ 1276 public int getExtendedPaddingTop() { 1277 if (mMaxMode != LINES) { 1278 return getCompoundPaddingTop(); 1279 } 1280 1281 if (mLayout.getLineCount() <= mMaximum) { 1282 return getCompoundPaddingTop(); 1283 } 1284 1285 int top = getCompoundPaddingTop(); 1286 int bottom = getCompoundPaddingBottom(); 1287 int viewht = getHeight() - top - bottom; 1288 int layoutht = mLayout.getLineTop(mMaximum); 1289 1290 if (layoutht >= viewht) { 1291 return top; 1292 } 1293 1294 final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1295 if (gravity == Gravity.TOP) { 1296 return top; 1297 } else if (gravity == Gravity.BOTTOM) { 1298 return top + viewht - layoutht; 1299 } else { // (gravity == Gravity.CENTER_VERTICAL) 1300 return top + (viewht - layoutht) / 2; 1301 } 1302 } 1303 1304 /** 1305 * Returns the extended bottom padding of the view, including both the 1306 * bottom Drawable if any and any extra space to keep more than maxLines 1307 * of text from showing. It is only valid to call this after measuring. 1308 */ 1309 public int getExtendedPaddingBottom() { 1310 if (mMaxMode != LINES) { 1311 return getCompoundPaddingBottom(); 1312 } 1313 1314 if (mLayout.getLineCount() <= mMaximum) { 1315 return getCompoundPaddingBottom(); 1316 } 1317 1318 int top = getCompoundPaddingTop(); 1319 int bottom = getCompoundPaddingBottom(); 1320 int viewht = getHeight() - top - bottom; 1321 int layoutht = mLayout.getLineTop(mMaximum); 1322 1323 if (layoutht >= viewht) { 1324 return bottom; 1325 } 1326 1327 final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 1328 if (gravity == Gravity.TOP) { 1329 return bottom + viewht - layoutht; 1330 } else if (gravity == Gravity.BOTTOM) { 1331 return bottom; 1332 } else { // (gravity == Gravity.CENTER_VERTICAL) 1333 return bottom + (viewht - layoutht) / 2; 1334 } 1335 } 1336 1337 /** 1338 * Returns the total left padding of the view, including the left 1339 * Drawable if any. 1340 */ 1341 public int getTotalPaddingLeft() { 1342 return getCompoundPaddingLeft(); 1343 } 1344 1345 /** 1346 * Returns the total right padding of the view, including the right 1347 * Drawable if any. 1348 */ 1349 public int getTotalPaddingRight() { 1350 return getCompoundPaddingRight(); 1351 } 1352 1353 /** 1354 * Returns the total top padding of the view, including the top 1355 * Drawable if any, the extra space to keep more than maxLines 1356 * from showing, and the vertical offset for gravity, if any. 1357 */ 1358 public int getTotalPaddingTop() { 1359 return getExtendedPaddingTop() + getVerticalOffset(true); 1360 } 1361 1362 /** 1363 * Returns the total bottom padding of the view, including the bottom 1364 * Drawable if any, the extra space to keep more than maxLines 1365 * from showing, and the vertical offset for gravity, if any. 1366 */ 1367 public int getTotalPaddingBottom() { 1368 return getExtendedPaddingBottom() + getBottomVerticalOffset(true); 1369 } 1370 1371 /** 1372 * Sets the Drawables (if any) to appear to the left of, above, 1373 * to the right of, and below the text. Use null if you do not 1374 * want a Drawable there. The Drawables must already have had 1375 * {@link Drawable#setBounds} called. 1376 * 1377 * @attr ref android.R.styleable#TextView_drawableLeft 1378 * @attr ref android.R.styleable#TextView_drawableTop 1379 * @attr ref android.R.styleable#TextView_drawableRight 1380 * @attr ref android.R.styleable#TextView_drawableBottom 1381 */ 1382 public void setCompoundDrawables(Drawable left, Drawable top, 1383 Drawable right, Drawable bottom) { 1384 Drawables dr = mDrawables; 1385 1386 final boolean drawables = left != null || top != null 1387 || right != null || bottom != null; 1388 1389 if (!drawables) { 1390 // Clearing drawables... can we free the data structure? 1391 if (dr != null) { 1392 if (dr.mDrawablePadding == 0) { 1393 mDrawables = null; 1394 } else { 1395 // We need to retain the last set padding, so just clear 1396 // out all of the fields in the existing structure. 1397 if (dr.mDrawableLeft != null) dr.mDrawableLeft.setCallback(null); 1398 dr.mDrawableLeft = null; 1399 if (dr.mDrawableTop != null) dr.mDrawableTop.setCallback(null); 1400 dr.mDrawableTop = null; 1401 if (dr.mDrawableRight != null) dr.mDrawableRight.setCallback(null); 1402 dr.mDrawableRight = null; 1403 if (dr.mDrawableBottom != null) dr.mDrawableBottom.setCallback(null); 1404 dr.mDrawableBottom = null; 1405 dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0; 1406 dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0; 1407 dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0; 1408 dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0; 1409 } 1410 } 1411 } else { 1412 if (dr == null) { 1413 mDrawables = dr = new Drawables(); 1414 } 1415 1416 if (dr.mDrawableLeft != left && dr.mDrawableLeft != null) { 1417 dr.mDrawableLeft.setCallback(null); 1418 } 1419 dr.mDrawableLeft = left; 1420 1421 if (dr.mDrawableTop != top && dr.mDrawableTop != null) { 1422 dr.mDrawableTop.setCallback(null); 1423 } 1424 dr.mDrawableTop = top; 1425 1426 if (dr.mDrawableRight != right && dr.mDrawableRight != null) { 1427 dr.mDrawableRight.setCallback(null); 1428 } 1429 dr.mDrawableRight = right; 1430 1431 if (dr.mDrawableBottom != bottom && dr.mDrawableBottom != null) { 1432 dr.mDrawableBottom.setCallback(null); 1433 } 1434 dr.mDrawableBottom = bottom; 1435 1436 final Rect compoundRect = dr.mCompoundRect; 1437 int[] state; 1438 1439 state = getDrawableState(); 1440 1441 if (left != null) { 1442 left.setState(state); 1443 left.copyBounds(compoundRect); 1444 left.setCallback(this); 1445 dr.mDrawableSizeLeft = compoundRect.width(); 1446 dr.mDrawableHeightLeft = compoundRect.height(); 1447 } else { 1448 dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0; 1449 } 1450 1451 if (right != null) { 1452 right.setState(state); 1453 right.copyBounds(compoundRect); 1454 right.setCallback(this); 1455 dr.mDrawableSizeRight = compoundRect.width(); 1456 dr.mDrawableHeightRight = compoundRect.height(); 1457 } else { 1458 dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0; 1459 } 1460 1461 if (top != null) { 1462 top.setState(state); 1463 top.copyBounds(compoundRect); 1464 top.setCallback(this); 1465 dr.mDrawableSizeTop = compoundRect.height(); 1466 dr.mDrawableWidthTop = compoundRect.width(); 1467 } else { 1468 dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0; 1469 } 1470 1471 if (bottom != null) { 1472 bottom.setState(state); 1473 bottom.copyBounds(compoundRect); 1474 bottom.setCallback(this); 1475 dr.mDrawableSizeBottom = compoundRect.height(); 1476 dr.mDrawableWidthBottom = compoundRect.width(); 1477 } else { 1478 dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0; 1479 } 1480 } 1481 1482 invalidate(); 1483 requestLayout(); 1484 } 1485 1486 /** 1487 * Sets the Drawables (if any) to appear to the left of, above, 1488 * to the right of, and below the text. Use 0 if you do not 1489 * want a Drawable there. The Drawables' bounds will be set to 1490 * their intrinsic bounds. 1491 * 1492 * @param left Resource identifier of the left Drawable. 1493 * @param top Resource identifier of the top Drawable. 1494 * @param right Resource identifier of the right Drawable. 1495 * @param bottom Resource identifier of the bottom Drawable. 1496 * 1497 * @attr ref android.R.styleable#TextView_drawableLeft 1498 * @attr ref android.R.styleable#TextView_drawableTop 1499 * @attr ref android.R.styleable#TextView_drawableRight 1500 * @attr ref android.R.styleable#TextView_drawableBottom 1501 */ 1502 public void setCompoundDrawablesWithIntrinsicBounds(int left, int top, int right, int bottom) { 1503 final Resources resources = getContext().getResources(); 1504 setCompoundDrawablesWithIntrinsicBounds(left != 0 ? resources.getDrawable(left) : null, 1505 top != 0 ? resources.getDrawable(top) : null, 1506 right != 0 ? resources.getDrawable(right) : null, 1507 bottom != 0 ? resources.getDrawable(bottom) : null); 1508 } 1509 1510 /** 1511 * Sets the Drawables (if any) to appear to the left of, above, 1512 * to the right of, and below the text. Use null if you do not 1513 * want a Drawable there. The Drawables' bounds will be set to 1514 * their intrinsic bounds. 1515 * 1516 * @attr ref android.R.styleable#TextView_drawableLeft 1517 * @attr ref android.R.styleable#TextView_drawableTop 1518 * @attr ref android.R.styleable#TextView_drawableRight 1519 * @attr ref android.R.styleable#TextView_drawableBottom 1520 */ 1521 public void setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, 1522 Drawable right, Drawable bottom) { 1523 1524 if (left != null) { 1525 left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight()); 1526 } 1527 if (right != null) { 1528 right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight()); 1529 } 1530 if (top != null) { 1531 top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight()); 1532 } 1533 if (bottom != null) { 1534 bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight()); 1535 } 1536 setCompoundDrawables(left, top, right, bottom); 1537 } 1538 1539 /** 1540 * Returns drawables for the left, top, right, and bottom borders. 1541 */ 1542 public Drawable[] getCompoundDrawables() { 1543 final Drawables dr = mDrawables; 1544 if (dr != null) { 1545 return new Drawable[] { 1546 dr.mDrawableLeft, dr.mDrawableTop, dr.mDrawableRight, dr.mDrawableBottom 1547 }; 1548 } else { 1549 return new Drawable[] { null, null, null, null }; 1550 } 1551 } 1552 1553 /** 1554 * Sets the size of the padding between the compound drawables and 1555 * the text. 1556 * 1557 * @attr ref android.R.styleable#TextView_drawablePadding 1558 */ 1559 public void setCompoundDrawablePadding(int pad) { 1560 Drawables dr = mDrawables; 1561 if (pad == 0) { 1562 if (dr != null) { 1563 dr.mDrawablePadding = pad; 1564 } 1565 } else { 1566 if (dr == null) { 1567 mDrawables = dr = new Drawables(); 1568 } 1569 dr.mDrawablePadding = pad; 1570 } 1571 1572 invalidate(); 1573 requestLayout(); 1574 } 1575 1576 /** 1577 * Returns the padding between the compound drawables and the text. 1578 */ 1579 public int getCompoundDrawablePadding() { 1580 final Drawables dr = mDrawables; 1581 return dr != null ? dr.mDrawablePadding : 0; 1582 } 1583 1584 @Override 1585 public void setPadding(int left, int top, int right, int bottom) { 1586 if (left != mPaddingLeft || 1587 right != mPaddingRight || 1588 top != mPaddingTop || 1589 bottom != mPaddingBottom) { 1590 nullLayouts(); 1591 } 1592 1593 // the super call will requestLayout() 1594 super.setPadding(left, top, right, bottom); 1595 invalidate(); 1596 } 1597 1598 /** 1599 * Gets the autolink mask of the text. See {@link 1600 * android.text.util.Linkify#ALL Linkify.ALL} and peers for 1601 * possible values. 1602 * 1603 * @attr ref android.R.styleable#TextView_autoLink 1604 */ 1605 public final int getAutoLinkMask() { 1606 return mAutoLinkMask; 1607 } 1608 1609 /** 1610 * Sets the text color, size, style, hint color, and highlight color 1611 * from the specified TextAppearance resource. 1612 */ 1613 public void setTextAppearance(Context context, int resid) { 1614 TypedArray appearance = 1615 context.obtainStyledAttributes(resid, 1616 com.android.internal.R.styleable.TextAppearance); 1617 1618 int color; 1619 ColorStateList colors; 1620 int ts; 1621 1622 color = appearance.getColor(com.android.internal.R.styleable.TextAppearance_textColorHighlight, 0); 1623 if (color != 0) { 1624 setHighlightColor(color); 1625 } 1626 1627 colors = appearance.getColorStateList(com.android.internal.R.styleable. 1628 TextAppearance_textColor); 1629 if (colors != null) { 1630 setTextColor(colors); 1631 } 1632 1633 ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable. 1634 TextAppearance_textSize, 0); 1635 if (ts != 0) { 1636 setRawTextSize(ts); 1637 } 1638 1639 colors = appearance.getColorStateList(com.android.internal.R.styleable. 1640 TextAppearance_textColorHint); 1641 if (colors != null) { 1642 setHintTextColor(colors); 1643 } 1644 1645 colors = appearance.getColorStateList(com.android.internal.R.styleable. 1646 TextAppearance_textColorLink); 1647 if (colors != null) { 1648 setLinkTextColor(colors); 1649 } 1650 1651 int typefaceIndex, styleIndex; 1652 1653 typefaceIndex = appearance.getInt(com.android.internal.R.styleable. 1654 TextAppearance_typeface, -1); 1655 styleIndex = appearance.getInt(com.android.internal.R.styleable. 1656 TextAppearance_textStyle, -1); 1657 1658 setTypefaceByIndex(typefaceIndex, styleIndex); 1659 appearance.recycle(); 1660 } 1661 1662 /** 1663 * @return the size (in pixels) of the default text size in this TextView. 1664 */ 1665 public float getTextSize() { 1666 return mTextPaint.getTextSize(); 1667 } 1668 1669 /** 1670 * Set the default text size to the given value, interpreted as "scaled 1671 * pixel" units. This size is adjusted based on the current density and 1672 * user font size preference. 1673 * 1674 * @param size The scaled pixel size. 1675 * 1676 * @attr ref android.R.styleable#TextView_textSize 1677 */ 1678 @android.view.RemotableViewMethod 1679 public void setTextSize(float size) { 1680 setTextSize(TypedValue.COMPLEX_UNIT_SP, size); 1681 } 1682 1683 /** 1684 * Set the default text size to a given unit and value. See {@link 1685 * TypedValue} for the possible dimension units. 1686 * 1687 * @param unit The desired dimension unit. 1688 * @param size The desired size in the given units. 1689 * 1690 * @attr ref android.R.styleable#TextView_textSize 1691 */ 1692 public void setTextSize(int unit, float size) { 1693 Context c = getContext(); 1694 Resources r; 1695 1696 if (c == null) 1697 r = Resources.getSystem(); 1698 else 1699 r = c.getResources(); 1700 1701 setRawTextSize(TypedValue.applyDimension( 1702 unit, size, r.getDisplayMetrics())); 1703 } 1704 1705 private void setRawTextSize(float size) { 1706 if (size != mTextPaint.getTextSize()) { 1707 mTextPaint.setTextSize(size); 1708 1709 if (mLayout != null) { 1710 nullLayouts(); 1711 requestLayout(); 1712 invalidate(); 1713 } 1714 } 1715 } 1716 1717 /** 1718 * @return the extent by which text is currently being stretched 1719 * horizontally. This will usually be 1. 1720 */ 1721 public float getTextScaleX() { 1722 return mTextPaint.getTextScaleX(); 1723 } 1724 1725 /** 1726 * Sets the extent by which text should be stretched horizontally. 1727 * 1728 * @attr ref android.R.styleable#TextView_textScaleX 1729 */ 1730 @android.view.RemotableViewMethod 1731 public void setTextScaleX(float size) { 1732 if (size != mTextPaint.getTextScaleX()) { 1733 mUserSetTextScaleX = true; 1734 mTextPaint.setTextScaleX(size); 1735 1736 if (mLayout != null) { 1737 nullLayouts(); 1738 requestLayout(); 1739 invalidate(); 1740 } 1741 } 1742 } 1743 1744 /** 1745 * Sets the typeface and style in which the text should be displayed. 1746 * Note that not all Typeface families actually have bold and italic 1747 * variants, so you may need to use 1748 * {@link #setTypeface(Typeface, int)} to get the appearance 1749 * that you actually want. 1750 * 1751 * @attr ref android.R.styleable#TextView_typeface 1752 * @attr ref android.R.styleable#TextView_textStyle 1753 */ 1754 public void setTypeface(Typeface tf) { 1755 if (mTextPaint.getTypeface() != tf) { 1756 mTextPaint.setTypeface(tf); 1757 1758 if (mLayout != null) { 1759 nullLayouts(); 1760 requestLayout(); 1761 invalidate(); 1762 } 1763 } 1764 } 1765 1766 /** 1767 * @return the current typeface and style in which the text is being 1768 * displayed. 1769 */ 1770 public Typeface getTypeface() { 1771 return mTextPaint.getTypeface(); 1772 } 1773 1774 /** 1775 * Sets the text color for all the states (normal, selected, 1776 * focused) to be this color. 1777 * 1778 * @attr ref android.R.styleable#TextView_textColor 1779 */ 1780 @android.view.RemotableViewMethod 1781 public void setTextColor(int color) { 1782 mTextColor = ColorStateList.valueOf(color); 1783 updateTextColors(); 1784 } 1785 1786 /** 1787 * Sets the text color. 1788 * 1789 * @attr ref android.R.styleable#TextView_textColor 1790 */ 1791 public void setTextColor(ColorStateList colors) { 1792 if (colors == null) { 1793 throw new NullPointerException(); 1794 } 1795 1796 mTextColor = colors; 1797 updateTextColors(); 1798 } 1799 1800 /** 1801 * Return the set of text colors. 1802 * 1803 * @return Returns the set of text colors. 1804 */ 1805 public final ColorStateList getTextColors() { 1806 return mTextColor; 1807 } 1808 1809 /** 1810 * <p>Return the current color selected for normal text.</p> 1811 * 1812 * @return Returns the current text color. 1813 */ 1814 public final int getCurrentTextColor() { 1815 return mCurTextColor; 1816 } 1817 1818 /** 1819 * Sets the color used to display the selection highlight. 1820 * 1821 * @attr ref android.R.styleable#TextView_textColorHighlight 1822 */ 1823 @android.view.RemotableViewMethod 1824 public void setHighlightColor(int color) { 1825 if (mHighlightColor != color) { 1826 mHighlightColor = color; 1827 invalidate(); 1828 } 1829 } 1830 1831 /** 1832 * Gives the text a shadow of the specified radius and color, the specified 1833 * distance from its normal position. 1834 * 1835 * @attr ref android.R.styleable#TextView_shadowColor 1836 * @attr ref android.R.styleable#TextView_shadowDx 1837 * @attr ref android.R.styleable#TextView_shadowDy 1838 * @attr ref android.R.styleable#TextView_shadowRadius 1839 */ 1840 public void setShadowLayer(float radius, float dx, float dy, int color) { 1841 mTextPaint.setShadowLayer(radius, dx, dy, color); 1842 1843 mShadowRadius = radius; 1844 mShadowDx = dx; 1845 mShadowDy = dy; 1846 1847 invalidate(); 1848 } 1849 1850 /** 1851 * @return the base paint used for the text. Please use this only to 1852 * consult the Paint's properties and not to change them. 1853 */ 1854 public TextPaint getPaint() { 1855 return mTextPaint; 1856 } 1857 1858 /** 1859 * Sets the autolink mask of the text. See {@link 1860 * android.text.util.Linkify#ALL Linkify.ALL} and peers for 1861 * possible values. 1862 * 1863 * @attr ref android.R.styleable#TextView_autoLink 1864 */ 1865 @android.view.RemotableViewMethod 1866 public final void setAutoLinkMask(int mask) { 1867 mAutoLinkMask = mask; 1868 } 1869 1870 /** 1871 * Sets whether the movement method will automatically be set to 1872 * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been 1873 * set to nonzero and links are detected in {@link #setText}. 1874 * The default is true. 1875 * 1876 * @attr ref android.R.styleable#TextView_linksClickable 1877 */ 1878 @android.view.RemotableViewMethod 1879 public final void setLinksClickable(boolean whether) { 1880 mLinksClickable = whether; 1881 } 1882 1883 /** 1884 * Returns whether the movement method will automatically be set to 1885 * {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been 1886 * set to nonzero and links are detected in {@link #setText}. 1887 * The default is true. 1888 * 1889 * @attr ref android.R.styleable#TextView_linksClickable 1890 */ 1891 public final boolean getLinksClickable() { 1892 return mLinksClickable; 1893 } 1894 1895 /** 1896 * Returns the list of URLSpans attached to the text 1897 * (by {@link Linkify} or otherwise) if any. You can call 1898 * {@link URLSpan#getURL} on them to find where they link to 1899 * or use {@link Spanned#getSpanStart} and {@link Spanned#getSpanEnd} 1900 * to find the region of the text they are attached to. 1901 */ 1902 public URLSpan[] getUrls() { 1903 if (mText instanceof Spanned) { 1904 return ((Spanned) mText).getSpans(0, mText.length(), URLSpan.class); 1905 } else { 1906 return new URLSpan[0]; 1907 } 1908 } 1909 1910 /** 1911 * Sets the color of the hint text. 1912 * 1913 * @attr ref android.R.styleable#TextView_textColorHint 1914 */ 1915 @android.view.RemotableViewMethod 1916 public final void setHintTextColor(int color) { 1917 mHintTextColor = ColorStateList.valueOf(color); 1918 updateTextColors(); 1919 } 1920 1921 /** 1922 * Sets the color of the hint text. 1923 * 1924 * @attr ref android.R.styleable#TextView_textColorHint 1925 */ 1926 public final void setHintTextColor(ColorStateList colors) { 1927 mHintTextColor = colors; 1928 updateTextColors(); 1929 } 1930 1931 /** 1932 * <p>Return the color used to paint the hint text.</p> 1933 * 1934 * @return Returns the list of hint text colors. 1935 */ 1936 public final ColorStateList getHintTextColors() { 1937 return mHintTextColor; 1938 } 1939 1940 /** 1941 * <p>Return the current color selected to paint the hint text.</p> 1942 * 1943 * @return Returns the current hint text color. 1944 */ 1945 public final int getCurrentHintTextColor() { 1946 return mHintTextColor != null ? mCurHintTextColor : mCurTextColor; 1947 } 1948 1949 /** 1950 * Sets the color of links in the text. 1951 * 1952 * @attr ref android.R.styleable#TextView_textColorLink 1953 */ 1954 @android.view.RemotableViewMethod 1955 public final void setLinkTextColor(int color) { 1956 mLinkTextColor = ColorStateList.valueOf(color); 1957 updateTextColors(); 1958 } 1959 1960 /** 1961 * Sets the color of links in the text. 1962 * 1963 * @attr ref android.R.styleable#TextView_textColorLink 1964 */ 1965 public final void setLinkTextColor(ColorStateList colors) { 1966 mLinkTextColor = colors; 1967 updateTextColors(); 1968 } 1969 1970 /** 1971 * <p>Returns the color used to paint links in the text.</p> 1972 * 1973 * @return Returns the list of link text colors. 1974 */ 1975 public final ColorStateList getLinkTextColors() { 1976 return mLinkTextColor; 1977 } 1978 1979 /** 1980 * Sets the horizontal alignment of the text and the 1981 * vertical gravity that will be used when there is extra space 1982 * in the TextView beyond what is required for the text itself. 1983 * 1984 * @see android.view.Gravity 1985 * @attr ref android.R.styleable#TextView_gravity 1986 */ 1987 public void setGravity(int gravity) { 1988 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) == 0) { 1989 gravity |= Gravity.LEFT; 1990 } 1991 if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) { 1992 gravity |= Gravity.TOP; 1993 } 1994 1995 boolean newLayout = false; 1996 1997 if ((gravity & Gravity.HORIZONTAL_GRAVITY_MASK) != 1998 (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK)) { 1999 newLayout = true; 2000 } 2001 2002 if (gravity != mGravity) { 2003 invalidate(); 2004 } 2005 2006 mGravity = gravity; 2007 2008 if (mLayout != null && newLayout) { 2009 // XXX this is heavy-handed because no actual content changes. 2010 int want = mLayout.getWidth(); 2011 int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth(); 2012 2013 makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING, 2014 mRight - mLeft - getCompoundPaddingLeft() - 2015 getCompoundPaddingRight(), true); 2016 } 2017 } 2018 2019 /** 2020 * Returns the horizontal and vertical alignment of this TextView. 2021 * 2022 * @see android.view.Gravity 2023 * @attr ref android.R.styleable#TextView_gravity 2024 */ 2025 public int getGravity() { 2026 return mGravity; 2027 } 2028 2029 /** 2030 * @return the flags on the Paint being used to display the text. 2031 * @see Paint#getFlags 2032 */ 2033 public int getPaintFlags() { 2034 return mTextPaint.getFlags(); 2035 } 2036 2037 /** 2038 * Sets flags on the Paint being used to display the text and 2039 * reflows the text if they are different from the old flags. 2040 * @see Paint#setFlags 2041 */ 2042 @android.view.RemotableViewMethod 2043 public void setPaintFlags(int flags) { 2044 if (mTextPaint.getFlags() != flags) { 2045 mTextPaint.setFlags(flags); 2046 2047 if (mLayout != null) { 2048 nullLayouts(); 2049 requestLayout(); 2050 invalidate(); 2051 } 2052 } 2053 } 2054 2055 /** 2056 * Sets whether the text should be allowed to be wider than the 2057 * View is. If false, it will be wrapped to the width of the View. 2058 * 2059 * @attr ref android.R.styleable#TextView_scrollHorizontally 2060 */ 2061 public void setHorizontallyScrolling(boolean whether) { 2062 mHorizontallyScrolling = whether; 2063 2064 if (mLayout != null) { 2065 nullLayouts(); 2066 requestLayout(); 2067 invalidate(); 2068 } 2069 } 2070 2071 /** 2072 * Makes the TextView at least this many lines tall 2073 * 2074 * @attr ref android.R.styleable#TextView_minLines 2075 */ 2076 @android.view.RemotableViewMethod 2077 public void setMinLines(int minlines) { 2078 mMinimum = minlines; 2079 mMinMode = LINES; 2080 2081 requestLayout(); 2082 invalidate(); 2083 } 2084 2085 /** 2086 * Makes the TextView at least this many pixels tall 2087 * 2088 * @attr ref android.R.styleable#TextView_minHeight 2089 */ 2090 @android.view.RemotableViewMethod 2091 public void setMinHeight(int minHeight) { 2092 mMinimum = minHeight; 2093 mMinMode = PIXELS; 2094 2095 requestLayout(); 2096 invalidate(); 2097 } 2098 2099 /** 2100 * Makes the TextView at most this many lines tall 2101 * 2102 * @attr ref android.R.styleable#TextView_maxLines 2103 */ 2104 @android.view.RemotableViewMethod 2105 public void setMaxLines(int maxlines) { 2106 mMaximum = maxlines; 2107 mMaxMode = LINES; 2108 2109 requestLayout(); 2110 invalidate(); 2111 } 2112 2113 /** 2114 * Makes the TextView at most this many pixels tall 2115 * 2116 * @attr ref android.R.styleable#TextView_maxHeight 2117 */ 2118 @android.view.RemotableViewMethod 2119 public void setMaxHeight(int maxHeight) { 2120 mMaximum = maxHeight; 2121 mMaxMode = PIXELS; 2122 2123 requestLayout(); 2124 invalidate(); 2125 } 2126 2127 /** 2128 * Makes the TextView exactly this many lines tall 2129 * 2130 * @attr ref android.R.styleable#TextView_lines 2131 */ 2132 @android.view.RemotableViewMethod 2133 public void setLines(int lines) { 2134 mMaximum = mMinimum = lines; 2135 mMaxMode = mMinMode = LINES; 2136 2137 requestLayout(); 2138 invalidate(); 2139 } 2140 2141 /** 2142 * Makes the TextView exactly this many pixels tall. 2143 * You could do the same thing by specifying this number in the 2144 * LayoutParams. 2145 * 2146 * @attr ref android.R.styleable#TextView_height 2147 */ 2148 @android.view.RemotableViewMethod 2149 public void setHeight(int pixels) { 2150 mMaximum = mMinimum = pixels; 2151 mMaxMode = mMinMode = PIXELS; 2152 2153 requestLayout(); 2154 invalidate(); 2155 } 2156 2157 /** 2158 * Makes the TextView at least this many ems wide 2159 * 2160 * @attr ref android.R.styleable#TextView_minEms 2161 */ 2162 @android.view.RemotableViewMethod 2163 public void setMinEms(int minems) { 2164 mMinWidth = minems; 2165 mMinWidthMode = EMS; 2166 2167 requestLayout(); 2168 invalidate(); 2169 } 2170 2171 /** 2172 * Makes the TextView at least this many pixels wide 2173 * 2174 * @attr ref android.R.styleable#TextView_minWidth 2175 */ 2176 @android.view.RemotableViewMethod 2177 public void setMinWidth(int minpixels) { 2178 mMinWidth = minpixels; 2179 mMinWidthMode = PIXELS; 2180 2181 requestLayout(); 2182 invalidate(); 2183 } 2184 2185 /** 2186 * Makes the TextView at most this many ems wide 2187 * 2188 * @attr ref android.R.styleable#TextView_maxEms 2189 */ 2190 @android.view.RemotableViewMethod 2191 public void setMaxEms(int maxems) { 2192 mMaxWidth = maxems; 2193 mMaxWidthMode = EMS; 2194 2195 requestLayout(); 2196 invalidate(); 2197 } 2198 2199 /** 2200 * Makes the TextView at most this many pixels wide 2201 * 2202 * @attr ref android.R.styleable#TextView_maxWidth 2203 */ 2204 @android.view.RemotableViewMethod 2205 public void setMaxWidth(int maxpixels) { 2206 mMaxWidth = maxpixels; 2207 mMaxWidthMode = PIXELS; 2208 2209 requestLayout(); 2210 invalidate(); 2211 } 2212 2213 /** 2214 * Makes the TextView exactly this many ems wide 2215 * 2216 * @attr ref android.R.styleable#TextView_ems 2217 */ 2218 @android.view.RemotableViewMethod 2219 public void setEms(int ems) { 2220 mMaxWidth = mMinWidth = ems; 2221 mMaxWidthMode = mMinWidthMode = EMS; 2222 2223 requestLayout(); 2224 invalidate(); 2225 } 2226 2227 /** 2228 * Makes the TextView exactly this many pixels wide. 2229 * You could do the same thing by specifying this number in the 2230 * LayoutParams. 2231 * 2232 * @attr ref android.R.styleable#TextView_width 2233 */ 2234 @android.view.RemotableViewMethod 2235 public void setWidth(int pixels) { 2236 mMaxWidth = mMinWidth = pixels; 2237 mMaxWidthMode = mMinWidthMode = PIXELS; 2238 2239 requestLayout(); 2240 invalidate(); 2241 } 2242 2243 2244 /** 2245 * Sets line spacing for this TextView. Each line will have its height 2246 * multiplied by <code>mult</code> and have <code>add</code> added to it. 2247 * 2248 * @attr ref android.R.styleable#TextView_lineSpacingExtra 2249 * @attr ref android.R.styleable#TextView_lineSpacingMultiplier 2250 */ 2251 public void setLineSpacing(float add, float mult) { 2252 mSpacingMult = mult; 2253 mSpacingAdd = add; 2254 2255 if (mLayout != null) { 2256 nullLayouts(); 2257 requestLayout(); 2258 invalidate(); 2259 } 2260 } 2261 2262 /** 2263 * Convenience method: Append the specified text to the TextView's 2264 * display buffer, upgrading it to BufferType.EDITABLE if it was 2265 * not already editable. 2266 */ 2267 public final void append(CharSequence text) { 2268 append(text, 0, text.length()); 2269 } 2270 2271 /** 2272 * Convenience method: Append the specified text slice to the TextView's 2273 * display buffer, upgrading it to BufferType.EDITABLE if it was 2274 * not already editable. 2275 */ 2276 public void append(CharSequence text, int start, int end) { 2277 if (!(mText instanceof Editable)) { 2278 setText(mText, BufferType.EDITABLE); 2279 } 2280 2281 ((Editable) mText).append(text, start, end); 2282 } 2283 2284 private void updateTextColors() { 2285 boolean inval = false; 2286 int color = mTextColor.getColorForState(getDrawableState(), 0); 2287 if (color != mCurTextColor) { 2288 mCurTextColor = color; 2289 inval = true; 2290 } 2291 if (mLinkTextColor != null) { 2292 color = mLinkTextColor.getColorForState(getDrawableState(), 0); 2293 if (color != mTextPaint.linkColor) { 2294 mTextPaint.linkColor = color; 2295 inval = true; 2296 } 2297 } 2298 if (mHintTextColor != null) { 2299 color = mHintTextColor.getColorForState(getDrawableState(), 0); 2300 if (color != mCurHintTextColor && mText.length() == 0) { 2301 mCurHintTextColor = color; 2302 inval = true; 2303 } 2304 } 2305 if (inval) { 2306 invalidate(); 2307 } 2308 } 2309 2310 @Override 2311 protected void drawableStateChanged() { 2312 super.drawableStateChanged(); 2313 if (mTextColor != null && mTextColor.isStateful() 2314 || (mHintTextColor != null && mHintTextColor.isStateful()) 2315 || (mLinkTextColor != null && mLinkTextColor.isStateful())) { 2316 updateTextColors(); 2317 } 2318 2319 final Drawables dr = mDrawables; 2320 if (dr != null) { 2321 int[] state = getDrawableState(); 2322 if (dr.mDrawableTop != null && dr.mDrawableTop.isStateful()) { 2323 dr.mDrawableTop.setState(state); 2324 } 2325 if (dr.mDrawableBottom != null && dr.mDrawableBottom.isStateful()) { 2326 dr.mDrawableBottom.setState(state); 2327 } 2328 if (dr.mDrawableLeft != null && dr.mDrawableLeft.isStateful()) { 2329 dr.mDrawableLeft.setState(state); 2330 } 2331 if (dr.mDrawableRight != null && dr.mDrawableRight.isStateful()) { 2332 dr.mDrawableRight.setState(state); 2333 } 2334 } 2335 } 2336 2337 /** 2338 * User interface state that is stored by TextView for implementing 2339 * {@link View#onSaveInstanceState}. 2340 */ 2341 public static class SavedState extends BaseSavedState { 2342 int selStart; 2343 int selEnd; 2344 CharSequence text; 2345 boolean frozenWithFocus; 2346 CharSequence error; 2347 2348 SavedState(Parcelable superState) { 2349 super(superState); 2350 } 2351 2352 @Override 2353 public void writeToParcel(Parcel out, int flags) { 2354 super.writeToParcel(out, flags); 2355 out.writeInt(selStart); 2356 out.writeInt(selEnd); 2357 out.writeInt(frozenWithFocus ? 1 : 0); 2358 TextUtils.writeToParcel(text, out, flags); 2359 2360 if (error == null) { 2361 out.writeInt(0); 2362 } else { 2363 out.writeInt(1); 2364 TextUtils.writeToParcel(error, out, flags); 2365 } 2366 } 2367 2368 @Override 2369 public String toString() { 2370 String str = "TextView.SavedState{" 2371 + Integer.toHexString(System.identityHashCode(this)) 2372 + " start=" + selStart + " end=" + selEnd; 2373 if (text != null) { 2374 str += " text=" + text; 2375 } 2376 return str + "}"; 2377 } 2378 2379 @SuppressWarnings("hiding") 2380 public static final Parcelable.Creator<SavedState> CREATOR 2381 = new Parcelable.Creator<SavedState>() { 2382 public SavedState createFromParcel(Parcel in) { 2383 return new SavedState(in); 2384 } 2385 2386 public SavedState[] newArray(int size) { 2387 return new SavedState[size]; 2388 } 2389 }; 2390 2391 private SavedState(Parcel in) { 2392 super(in); 2393 selStart = in.readInt(); 2394 selEnd = in.readInt(); 2395 frozenWithFocus = (in.readInt() != 0); 2396 text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 2397 2398 if (in.readInt() != 0) { 2399 error = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in); 2400 } 2401 } 2402 } 2403 2404 @Override 2405 public Parcelable onSaveInstanceState() { 2406 Parcelable superState = super.onSaveInstanceState(); 2407 2408 // Save state if we are forced to 2409 boolean save = mFreezesText; 2410 int start = 0; 2411 int end = 0; 2412 2413 if (mText != null) { 2414 start = getSelectionStart(); 2415 end = getSelectionEnd(); 2416 if (start >= 0 || end >= 0) { 2417 // Or save state if there is a selection 2418 save = true; 2419 } 2420 } 2421 2422 if (save) { 2423 SavedState ss = new SavedState(superState); 2424 // XXX Should also save the current scroll position! 2425 ss.selStart = start; 2426 ss.selEnd = end; 2427 2428 if (mText instanceof Spanned) { 2429 /* 2430 * Calling setText() strips off any ChangeWatchers; 2431 * strip them now to avoid leaking references. 2432 * But do it to a copy so that if there are any 2433 * further changes to the text of this view, it 2434 * won't get into an inconsistent state. 2435 */ 2436 2437 Spannable sp = new SpannableString(mText); 2438 2439 for (ChangeWatcher cw : 2440 sp.getSpans(0, sp.length(), ChangeWatcher.class)) { 2441 sp.removeSpan(cw); 2442 } 2443 2444 ss.text = sp; 2445 } else { 2446 ss.text = mText.toString(); 2447 } 2448 2449 if (isFocused() && start >= 0 && end >= 0) { 2450 ss.frozenWithFocus = true; 2451 } 2452 2453 ss.error = mError; 2454 2455 return ss; 2456 } 2457 2458 return superState; 2459 } 2460 2461 @Override 2462 public void onRestoreInstanceState(Parcelable state) { 2463 if (!(state instanceof SavedState)) { 2464 super.onRestoreInstanceState(state); 2465 return; 2466 } 2467 2468 SavedState ss = (SavedState)state; 2469 super.onRestoreInstanceState(ss.getSuperState()); 2470 2471 // XXX restore buffer type too, as well as lots of other stuff 2472 if (ss.text != null) { 2473 setText(ss.text); 2474 } 2475 2476 if (ss.selStart >= 0 && ss.selEnd >= 0) { 2477 if (mText instanceof Spannable) { 2478 int len = mText.length(); 2479 2480 if (ss.selStart > len || ss.selEnd > len) { 2481 String restored = ""; 2482 2483 if (ss.text != null) { 2484 restored = "(restored) "; 2485 } 2486 2487 Log.e(LOG_TAG, "Saved cursor position " + ss.selStart + 2488 "/" + ss.selEnd + " out of range for " + restored + 2489 "text " + mText); 2490 } else { 2491 Selection.setSelection((Spannable) mText, ss.selStart, 2492 ss.selEnd); 2493 2494 if (ss.frozenWithFocus) { 2495 mFrozenWithFocus = true; 2496 } 2497 } 2498 } 2499 } 2500 2501 if (ss.error != null) { 2502 final CharSequence error = ss.error; 2503 // Display the error later, after the first layout pass 2504 post(new Runnable() { 2505 public void run() { 2506 setError(error); 2507 } 2508 }); 2509 } 2510 } 2511 2512 /** 2513 * Control whether this text view saves its entire text contents when 2514 * freezing to an icicle, in addition to dynamic state such as cursor 2515 * position. By default this is false, not saving the text. Set to true 2516 * if the text in the text view is not being saved somewhere else in 2517 * persistent storage (such as in a content provider) so that if the 2518 * view is later thawed the user will not lose their data. 2519 * 2520 * @param freezesText Controls whether a frozen icicle should include the 2521 * entire text data: true to include it, false to not. 2522 * 2523 * @attr ref android.R.styleable#TextView_freezesText 2524 */ 2525 @android.view.RemotableViewMethod 2526 public void setFreezesText(boolean freezesText) { 2527 mFreezesText = freezesText; 2528 } 2529 2530 /** 2531 * Return whether this text view is including its entire text contents 2532 * in frozen icicles. 2533 * 2534 * @return Returns true if text is included, false if it isn't. 2535 * 2536 * @see #setFreezesText 2537 */ 2538 public boolean getFreezesText() { 2539 return mFreezesText; 2540 } 2541 2542 /////////////////////////////////////////////////////////////////////////// 2543 2544 /** 2545 * Sets the Factory used to create new Editables. 2546 */ 2547 public final void setEditableFactory(Editable.Factory factory) { 2548 mEditableFactory = factory; 2549 setText(mText); 2550 } 2551 2552 /** 2553 * Sets the Factory used to create new Spannables. 2554 */ 2555 public final void setSpannableFactory(Spannable.Factory factory) { 2556 mSpannableFactory = factory; 2557 setText(mText); 2558 } 2559 2560 /** 2561 * Sets the string value of the TextView. TextView <em>does not</em> accept 2562 * HTML-like formatting, which you can do with text strings in XML resource files. 2563 * To style your strings, attach android.text.style.* objects to a 2564 * {@link android.text.SpannableString SpannableString}, or see the 2565 * <a href="{@docRoot}guide/topics/resources/available-resources.html#stringresources"> 2566 * Available Resource Types</a> documentation for an example of setting 2567 * formatted text in the XML resource file. 2568 * 2569 * @attr ref android.R.styleable#TextView_text 2570 */ 2571 @android.view.RemotableViewMethod 2572 public final void setText(CharSequence text) { 2573 setText(text, mBufferType); 2574 } 2575 2576 /** 2577 * Like {@link #setText(CharSequence)}, 2578 * except that the cursor position (if any) is retained in the new text. 2579 * 2580 * @param text The new text to place in the text view. 2581 * 2582 * @see #setText(CharSequence) 2583 */ 2584 @android.view.RemotableViewMethod 2585 public final void setTextKeepState(CharSequence text) { 2586 setTextKeepState(text, mBufferType); 2587 } 2588 2589 /** 2590 * Sets the text that this TextView is to display (see 2591 * {@link #setText(CharSequence)}) and also sets whether it is stored 2592 * in a styleable/spannable buffer and whether it is editable. 2593 * 2594 * @attr ref android.R.styleable#TextView_text 2595 * @attr ref android.R.styleable#TextView_bufferType 2596 */ 2597 public void setText(CharSequence text, BufferType type) { 2598 setText(text, type, true, 0); 2599 2600 if (mCharWrapper != null) { 2601 mCharWrapper.mChars = null; 2602 } 2603 } 2604 2605 private void setText(CharSequence text, BufferType type, 2606 boolean notifyBefore, int oldlen) { 2607 if (text == null) { 2608 text = ""; 2609 } 2610 2611 if (!mUserSetTextScaleX) mTextPaint.setTextScaleX(1.0f); 2612 2613 if (text instanceof Spanned && 2614 ((Spanned) text).getSpanStart(TextUtils.TruncateAt.MARQUEE) >= 0) { 2615 setHorizontalFadingEdgeEnabled(true); 2616 setEllipsize(TextUtils.TruncateAt.MARQUEE); 2617 } 2618 2619 int n = mFilters.length; 2620 for (int i = 0; i < n; i++) { 2621 CharSequence out = mFilters[i].filter(text, 0, text.length(), 2622 EMPTY_SPANNED, 0, 0); 2623 if (out != null) { 2624 text = out; 2625 } 2626 } 2627 2628 if (notifyBefore) { 2629 if (mText != null) { 2630 oldlen = mText.length(); 2631 sendBeforeTextChanged(mText, 0, oldlen, text.length()); 2632 } else { 2633 sendBeforeTextChanged("", 0, 0, text.length()); 2634 } 2635 } 2636 2637 boolean needEditableForNotification = false; 2638 2639 if (mListeners != null && mListeners.size() != 0) { 2640 needEditableForNotification = true; 2641 } 2642 2643 if (type == BufferType.EDITABLE || mInput != null || 2644 needEditableForNotification) { 2645 Editable t = mEditableFactory.newEditable(text); 2646 text = t; 2647 setFilters(t, mFilters); 2648 InputMethodManager imm = InputMethodManager.peekInstance(); 2649 if (imm != null) imm.restartInput(this); 2650 } else if (type == BufferType.SPANNABLE || mMovement != null) { 2651 text = mSpannableFactory.newSpannable(text); 2652 } else if (!(text instanceof CharWrapper)) { 2653 text = TextUtils.stringOrSpannedString(text); 2654 } 2655 2656 if (mAutoLinkMask != 0) { 2657 Spannable s2; 2658 2659 if (type == BufferType.EDITABLE || text instanceof Spannable) { 2660 s2 = (Spannable) text; 2661 } else { 2662 s2 = mSpannableFactory.newSpannable(text); 2663 } 2664 2665 if (Linkify.addLinks(s2, mAutoLinkMask)) { 2666 text = s2; 2667 type = (type == BufferType.EDITABLE) ? BufferType.EDITABLE : BufferType.SPANNABLE; 2668 2669 /* 2670 * We must go ahead and set the text before changing the 2671 * movement method, because setMovementMethod() may call 2672 * setText() again to try to upgrade the buffer type. 2673 */ 2674 mText = text; 2675 2676 if (mLinksClickable) { 2677 setMovementMethod(LinkMovementMethod.getInstance()); 2678 } 2679 } 2680 } 2681 2682 mBufferType = type; 2683 mText = text; 2684 2685 if (mTransformation == null) 2686 mTransformed = text; 2687 else 2688 mTransformed = mTransformation.getTransformation(text, this); 2689 2690 final int textLength = text.length(); 2691 2692 if (text instanceof Spannable) { 2693 Spannable sp = (Spannable) text; 2694 2695 // Remove any ChangeWatchers that might have come 2696 // from other TextViews. 2697 final ChangeWatcher[] watchers = sp.getSpans(0, sp.length(), ChangeWatcher.class); 2698 final int count = watchers.length; 2699 for (int i = 0; i < count; i++) 2700 sp.removeSpan(watchers[i]); 2701 2702 if (mChangeWatcher == null) 2703 mChangeWatcher = new ChangeWatcher(); 2704 2705 sp.setSpan(mChangeWatcher, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE | 2706 (PRIORITY << Spanned.SPAN_PRIORITY_SHIFT)); 2707 2708 if (mInput != null) { 2709 sp.setSpan(mInput, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 2710 } 2711 2712 if (mTransformation != null) { 2713 sp.setSpan(mTransformation, 0, textLength, Spanned.SPAN_INCLUSIVE_INCLUSIVE); 2714 2715 } 2716 2717 if (mMovement != null) { 2718 mMovement.initialize(this, (Spannable) text); 2719 2720 /* 2721 * Initializing the movement method will have set the 2722 * selection, so reset mSelectionMoved to keep that from 2723 * interfering with the normal on-focus selection-setting. 2724 */ 2725 mSelectionMoved = false; 2726 } 2727 } 2728 2729 if (mLayout != null) { 2730 checkForRelayout(); 2731 } 2732 2733 sendOnTextChanged(text, 0, oldlen, textLength); 2734 onTextChanged(text, 0, oldlen, textLength); 2735 2736 if (needEditableForNotification) { 2737 sendAfterTextChanged((Editable) text); 2738 } 2739 2740 // SelectionModifierCursorController depends on textCanBeSelected, which depends on text 2741 prepareCursorControllers(); 2742 } 2743 2744 /** 2745 * Sets the TextView to display the specified slice of the specified 2746 * char array. You must promise that you will not change the contents 2747 * of the array except for right before another call to setText(), 2748 * since the TextView has no way to know that the text 2749 * has changed and that it needs to invalidate and re-layout. 2750 */ 2751 public final void setText(char[] text, int start, int len) { 2752 int oldlen = 0; 2753 2754 if (start < 0 || len < 0 || start + len > text.length) { 2755 throw new IndexOutOfBoundsException(start + ", " + len); 2756 } 2757 2758 /* 2759 * We must do the before-notification here ourselves because if 2760 * the old text is a CharWrapper we destroy it before calling 2761 * into the normal path. 2762 */ 2763 if (mText != null) { 2764 oldlen = mText.length(); 2765 sendBeforeTextChanged(mText, 0, oldlen, len); 2766 } else { 2767 sendBeforeTextChanged("", 0, 0, len); 2768 } 2769 2770 if (mCharWrapper == null) { 2771 mCharWrapper = new CharWrapper(text, start, len); 2772 } else { 2773 mCharWrapper.set(text, start, len); 2774 } 2775 2776 setText(mCharWrapper, mBufferType, false, oldlen); 2777 } 2778 2779 private static class CharWrapper 2780 implements CharSequence, GetChars, GraphicsOperations { 2781 private char[] mChars; 2782 private int mStart, mLength; 2783 2784 public CharWrapper(char[] chars, int start, int len) { 2785 mChars = chars; 2786 mStart = start; 2787 mLength = len; 2788 } 2789 2790 /* package */ void set(char[] chars, int start, int len) { 2791 mChars = chars; 2792 mStart = start; 2793 mLength = len; 2794 } 2795 2796 public int length() { 2797 return mLength; 2798 } 2799 2800 public char charAt(int off) { 2801 return mChars[off + mStart]; 2802 } 2803 2804 @Override 2805 public String toString() { 2806 return new String(mChars, mStart, mLength); 2807 } 2808 2809 public CharSequence subSequence(int start, int end) { 2810 if (start < 0 || end < 0 || start > mLength || end > mLength) { 2811 throw new IndexOutOfBoundsException(start + ", " + end); 2812 } 2813 2814 return new String(mChars, start + mStart, end - start); 2815 } 2816 2817 public void getChars(int start, int end, char[] buf, int off) { 2818 if (start < 0 || end < 0 || start > mLength || end > mLength) { 2819 throw new IndexOutOfBoundsException(start + ", " + end); 2820 } 2821 2822 System.arraycopy(mChars, start + mStart, buf, off, end - start); 2823 } 2824 2825 public void drawText(Canvas c, int start, int end, 2826 float x, float y, Paint p) { 2827 c.drawText(mChars, start + mStart, end - start, x, y, p); 2828 } 2829 2830 public float measureText(int start, int end, Paint p) { 2831 return p.measureText(mChars, start + mStart, end - start); 2832 } 2833 2834 public int getTextWidths(int start, int end, float[] widths, Paint p) { 2835 return p.getTextWidths(mChars, start + mStart, end - start, widths); 2836 } 2837 } 2838 2839 /** 2840 * Like {@link #setText(CharSequence, android.widget.TextView.BufferType)}, 2841 * except that the cursor position (if any) is retained in the new text. 2842 * 2843 * @see #setText(CharSequence, android.widget.TextView.BufferType) 2844 */ 2845 public final void setTextKeepState(CharSequence text, BufferType type) { 2846 int start = getSelectionStart(); 2847 int end = getSelectionEnd(); 2848 int len = text.length(); 2849 2850 setText(text, type); 2851 2852 if (start >= 0 || end >= 0) { 2853 if (mText instanceof Spannable) { 2854 Selection.setSelection((Spannable) mText, 2855 Math.max(0, Math.min(start, len)), 2856 Math.max(0, Math.min(end, len))); 2857 } 2858 } 2859 } 2860 2861 @android.view.RemotableViewMethod 2862 public final void setText(int resid) { 2863 setText(getContext().getResources().getText(resid)); 2864 } 2865 2866 public final void setText(int resid, BufferType type) { 2867 setText(getContext().getResources().getText(resid), type); 2868 } 2869 2870 /** 2871 * Sets the text to be displayed when the text of the TextView is empty. 2872 * Null means to use the normal empty text. The hint does not currently 2873 * participate in determining the size of the view. 2874 * 2875 * @attr ref android.R.styleable#TextView_hint 2876 */ 2877 @android.view.RemotableViewMethod 2878 public final void setHint(CharSequence hint) { 2879 mHint = TextUtils.stringOrSpannedString(hint); 2880 2881 if (mLayout != null) { 2882 checkForRelayout(); 2883 } 2884 2885 if (mText.length() == 0) { 2886 invalidate(); 2887 } 2888 } 2889 2890 /** 2891 * Sets the text to be displayed when the text of the TextView is empty, 2892 * from a resource. 2893 * 2894 * @attr ref android.R.styleable#TextView_hint 2895 */ 2896 @android.view.RemotableViewMethod 2897 public final void setHint(int resid) { 2898 setHint(getContext().getResources().getText(resid)); 2899 } 2900 2901 /** 2902 * Returns the hint that is displayed when the text of the TextView 2903 * is empty. 2904 * 2905 * @attr ref android.R.styleable#TextView_hint 2906 */ 2907 @ViewDebug.CapturedViewProperty 2908 public CharSequence getHint() { 2909 return mHint; 2910 } 2911 2912 /** 2913 * Set the type of the content with a constant as defined for 2914 * {@link EditorInfo#inputType}. This will take care of changing 2915 * the key listener, by calling {@link #setKeyListener(KeyListener)}, to 2916 * match the given content type. If the given content type is 2917 * {@link EditorInfo#TYPE_NULL} then a soft keyboard will 2918 * not be displayed for this text view. 2919 * 2920 * @see #getInputType() 2921 * @see #setRawInputType(int) 2922 * @see android.text.InputType 2923 * @attr ref android.R.styleable#TextView_inputType 2924 */ 2925 public void setInputType(int type) { 2926 final boolean wasPassword = isPasswordInputType(mInputType); 2927 final boolean wasVisiblePassword = isVisiblePasswordInputType(mInputType); 2928 setInputType(type, false); 2929 final boolean isPassword = isPasswordInputType(type); 2930 final boolean isVisiblePassword = isVisiblePasswordInputType(type); 2931 boolean forceUpdate = false; 2932 if (isPassword) { 2933 setTransformationMethod(PasswordTransformationMethod.getInstance()); 2934 setTypefaceByIndex(MONOSPACE, 0); 2935 } else if (isVisiblePassword) { 2936 if (mTransformation == PasswordTransformationMethod.getInstance()) { 2937 forceUpdate = true; 2938 } 2939 setTypefaceByIndex(MONOSPACE, 0); 2940 } else if (wasPassword || wasVisiblePassword) { 2941 // not in password mode, clean up typeface and transformation 2942 setTypefaceByIndex(-1, -1); 2943 if (mTransformation == PasswordTransformationMethod.getInstance()) { 2944 forceUpdate = true; 2945 } 2946 } 2947 2948 boolean multiLine = (type&(EditorInfo.TYPE_MASK_CLASS 2949 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE)) == 2950 (EditorInfo.TYPE_CLASS_TEXT 2951 | EditorInfo.TYPE_TEXT_FLAG_MULTI_LINE); 2952 2953 // We need to update the single line mode if it has changed or we 2954 // were previously in password mode. 2955 if (mSingleLine == multiLine || forceUpdate) { 2956 // Change single line mode, but only change the transformation if 2957 // we are not in password mode. 2958 applySingleLine(!multiLine, !isPassword); 2959 } 2960 2961 InputMethodManager imm = InputMethodManager.peekInstance(); 2962 if (imm != null) imm.restartInput(this); 2963 } 2964 2965 /** 2966 * It would be better to rely on the input type for everything. A password inputType should have 2967 * a password transformation. We should hence use isPasswordInputType instead of this method. 2968 * 2969 * We should: 2970 * - Call setInputType in setKeyListener instead of changing the input type directly (which 2971 * would install the correct transformation). 2972 * - Refuse the installation of a non-password transformation in setTransformation if the input 2973 * type is password. 2974 * 2975 * However, this is like this for legacy reasons and we cannot break existing apps. This method 2976 * is useful since it matches what the user can see (obfuscated text or not). 2977 * 2978 * @return true if the current transformation method is of the password type. 2979 */ 2980 private boolean hasPasswordTransformationMethod() { 2981 return mTransformation instanceof PasswordTransformationMethod; 2982 } 2983 2984 private boolean isPasswordInputType(int inputType) { 2985 final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS 2986 | EditorInfo.TYPE_MASK_VARIATION); 2987 return variation 2988 == (EditorInfo.TYPE_CLASS_TEXT 2989 | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD); 2990 } 2991 2992 private boolean isVisiblePasswordInputType(int inputType) { 2993 final int variation = inputType & (EditorInfo.TYPE_MASK_CLASS 2994 | EditorInfo.TYPE_MASK_VARIATION); 2995 return variation 2996 == (EditorInfo.TYPE_CLASS_TEXT 2997 | EditorInfo.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD); 2998 } 2999 3000 /** 3001 * Directly change the content type integer of the text view, without 3002 * modifying any other state. 3003 * @see #setInputType(int) 3004 * @see android.text.InputType 3005 * @attr ref android.R.styleable#TextView_inputType 3006 */ 3007 public void setRawInputType(int type) { 3008 mInputType = type; 3009 } 3010 3011 private void setInputType(int type, boolean direct) { 3012 final int cls = type & EditorInfo.TYPE_MASK_CLASS; 3013 KeyListener input; 3014 if (cls == EditorInfo.TYPE_CLASS_TEXT) { 3015 boolean autotext = (type & EditorInfo.TYPE_TEXT_FLAG_AUTO_CORRECT) != 0; 3016 TextKeyListener.Capitalize cap; 3017 if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS) != 0) { 3018 cap = TextKeyListener.Capitalize.CHARACTERS; 3019 } else if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS) != 0) { 3020 cap = TextKeyListener.Capitalize.WORDS; 3021 } else if ((type & EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES) != 0) { 3022 cap = TextKeyListener.Capitalize.SENTENCES; 3023 } else { 3024 cap = TextKeyListener.Capitalize.NONE; 3025 } 3026 input = TextKeyListener.getInstance(autotext, cap); 3027 } else if (cls == EditorInfo.TYPE_CLASS_NUMBER) { 3028 input = DigitsKeyListener.getInstance( 3029 (type & EditorInfo.TYPE_NUMBER_FLAG_SIGNED) != 0, 3030 (type & EditorInfo.TYPE_NUMBER_FLAG_DECIMAL) != 0); 3031 } else if (cls == EditorInfo.TYPE_CLASS_DATETIME) { 3032 switch (type & EditorInfo.TYPE_MASK_VARIATION) { 3033 case EditorInfo.TYPE_DATETIME_VARIATION_DATE: 3034 input = DateKeyListener.getInstance(); 3035 break; 3036 case EditorInfo.TYPE_DATETIME_VARIATION_TIME: 3037 input = TimeKeyListener.getInstance(); 3038 break; 3039 default: 3040 input = DateTimeKeyListener.getInstance(); 3041 break; 3042 } 3043 } else if (cls == EditorInfo.TYPE_CLASS_PHONE) { 3044 input = DialerKeyListener.getInstance(); 3045 } else { 3046 input = TextKeyListener.getInstance(); 3047 } 3048 setRawInputType(type); 3049 if (direct) mInput = input; 3050 else { 3051 setKeyListenerOnly(input); 3052 } 3053 } 3054 3055 /** 3056 * Get the type of the content. 3057 * 3058 * @see #setInputType(int) 3059 * @see android.text.InputType 3060 */ 3061 public int getInputType() { 3062 return mInputType; 3063 } 3064 3065 /** 3066 * Change the editor type integer associated with the text view, which 3067 * will be reported to an IME with {@link EditorInfo#imeOptions} when it 3068 * has focus. 3069 * @see #getImeOptions 3070 * @see android.view.inputmethod.EditorInfo 3071 * @attr ref android.R.styleable#TextView_imeOptions 3072 */ 3073 public void setImeOptions(int imeOptions) { 3074 if (mInputContentType == null) { 3075 mInputContentType = new InputContentType(); 3076 } 3077 mInputContentType.imeOptions = imeOptions; 3078 } 3079 3080 /** 3081 * Get the type of the IME editor. 3082 * 3083 * @see #setImeOptions(int) 3084 * @see android.view.inputmethod.EditorInfo 3085 */ 3086 public int getImeOptions() { 3087 return mInputContentType != null 3088 ? mInputContentType.imeOptions : EditorInfo.IME_NULL; 3089 } 3090 3091 /** 3092 * Change the custom IME action associated with the text view, which 3093 * will be reported to an IME with {@link EditorInfo#actionLabel} 3094 * and {@link EditorInfo#actionId} when it has focus. 3095 * @see #getImeActionLabel 3096 * @see #getImeActionId 3097 * @see android.view.inputmethod.EditorInfo 3098 * @attr ref android.R.styleable#TextView_imeActionLabel 3099 * @attr ref android.R.styleable#TextView_imeActionId 3100 */ 3101 public void setImeActionLabel(CharSequence label, int actionId) { 3102 if (mInputContentType == null) { 3103 mInputContentType = new InputContentType(); 3104 } 3105 mInputContentType.imeActionLabel = label; 3106 mInputContentType.imeActionId = actionId; 3107 } 3108 3109 /** 3110 * Get the IME action label previous set with {@link #setImeActionLabel}. 3111 * 3112 * @see #setImeActionLabel 3113 * @see android.view.inputmethod.EditorInfo 3114 */ 3115 public CharSequence getImeActionLabel() { 3116 return mInputContentType != null 3117 ? mInputContentType.imeActionLabel : null; 3118 } 3119 3120 /** 3121 * Get the IME action ID previous set with {@link #setImeActionLabel}. 3122 * 3123 * @see #setImeActionLabel 3124 * @see android.view.inputmethod.EditorInfo 3125 */ 3126 public int getImeActionId() { 3127 return mInputContentType != null 3128 ? mInputContentType.imeActionId : 0; 3129 } 3130 3131 /** 3132 * Set a special listener to be called when an action is performed 3133 * on the text view. This will be called when the enter key is pressed, 3134 * or when an action supplied to the IME is selected by the user. Setting 3135 * this means that the normal hard key event will not insert a newline 3136 * into the text view, even if it is multi-line; holding down the ALT 3137 * modifier will, however, allow the user to insert a newline character. 3138 */ 3139 public void setOnEditorActionListener(OnEditorActionListener l) { 3140 if (mInputContentType == null) { 3141 mInputContentType = new InputContentType(); 3142 } 3143 mInputContentType.onEditorActionListener = l; 3144 } 3145 3146 /** 3147 * Called when an attached input method calls 3148 * {@link InputConnection#performEditorAction(int) 3149 * InputConnection.performEditorAction()} 3150 * for this text view. The default implementation will call your action 3151 * listener supplied to {@link #setOnEditorActionListener}, or perform 3152 * a standard operation for {@link EditorInfo#IME_ACTION_NEXT 3153 * EditorInfo.IME_ACTION_NEXT} or {@link EditorInfo#IME_ACTION_DONE 3154 * EditorInfo.IME_ACTION_DONE}. 3155 * 3156 * <p>For backwards compatibility, if no IME options have been set and the 3157 * text view would not normally advance focus on enter, then 3158 * the NEXT and DONE actions received here will be turned into an enter 3159 * key down/up pair to go through the normal key handling. 3160 * 3161 * @param actionCode The code of the action being performed. 3162 * 3163 * @see #setOnEditorActionListener 3164 */ 3165 public void onEditorAction(int actionCode) { 3166 final InputContentType ict = mInputContentType; 3167 if (ict != null) { 3168 if (ict.onEditorActionListener != null) { 3169 if (ict.onEditorActionListener.onEditorAction(this, 3170 actionCode, null)) { 3171 return; 3172 } 3173 } 3174 3175 // This is the handling for some default action. 3176 // Note that for backwards compatibility we don't do this 3177 // default handling if explicit ime options have not been given, 3178 // instead turning this into the normal enter key codes that an 3179 // app may be expecting. 3180 if (actionCode == EditorInfo.IME_ACTION_NEXT) { 3181 View v = focusSearch(FOCUS_DOWN); 3182 if (v != null) { 3183 if (!v.requestFocus(FOCUS_DOWN)) { 3184 throw new IllegalStateException("focus search returned a view " + 3185 "that wasn't able to take focus!"); 3186 } 3187 } 3188 return; 3189 3190 } else if (actionCode == EditorInfo.IME_ACTION_DONE) { 3191 InputMethodManager imm = InputMethodManager.peekInstance(); 3192 if (imm != null) { 3193 imm.hideSoftInputFromWindow(getWindowToken(), 0); 3194 } 3195 return; 3196 } 3197 } 3198 3199 Handler h = getHandler(); 3200 if (h != null) { 3201 long eventTime = SystemClock.uptimeMillis(); 3202 h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, 3203 new KeyEvent(eventTime, eventTime, 3204 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, 3205 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE 3206 | KeyEvent.FLAG_EDITOR_ACTION))); 3207 h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME, 3208 new KeyEvent(SystemClock.uptimeMillis(), eventTime, 3209 KeyEvent.ACTION_UP, KeyEvent.KEYCODE_ENTER, 0, 0, 0, 0, 3210 KeyEvent.FLAG_SOFT_KEYBOARD | KeyEvent.FLAG_KEEP_TOUCH_MODE 3211 | KeyEvent.FLAG_EDITOR_ACTION))); 3212 } 3213 } 3214 3215 /** 3216 * Set the private content type of the text, which is the 3217 * {@link EditorInfo#privateImeOptions EditorInfo.privateImeOptions} 3218 * field that will be filled in when creating an input connection. 3219 * 3220 * @see #getPrivateImeOptions() 3221 * @see EditorInfo#privateImeOptions 3222 * @attr ref android.R.styleable#TextView_privateImeOptions 3223 */ 3224 public void setPrivateImeOptions(String type) { 3225 if (mInputContentType == null) mInputContentType = new InputContentType(); 3226 mInputContentType.privateImeOptions = type; 3227 } 3228 3229 /** 3230 * Get the private type of the content. 3231 * 3232 * @see #setPrivateImeOptions(String) 3233 * @see EditorInfo#privateImeOptions 3234 */ 3235 public String getPrivateImeOptions() { 3236 return mInputContentType != null 3237 ? mInputContentType.privateImeOptions : null; 3238 } 3239 3240 /** 3241 * Set the extra input data of the text, which is the 3242 * {@link EditorInfo#extras TextBoxAttribute.extras} 3243 * Bundle that will be filled in when creating an input connection. The 3244 * given integer is the resource ID of an XML resource holding an 3245 * {@link android.R.styleable#InputExtras <input-extras>} XML tree. 3246 * 3247 * @see #getInputExtras(boolean) 3248 * @see EditorInfo#extras 3249 * @attr ref android.R.styleable#TextView_editorExtras 3250 */ 3251 public void setInputExtras(int xmlResId) 3252 throws XmlPullParserException, IOException { 3253 XmlResourceParser parser = getResources().getXml(xmlResId); 3254 if (mInputContentType == null) mInputContentType = new InputContentType(); 3255 mInputContentType.extras = new Bundle(); 3256 getResources().parseBundleExtras(parser, mInputContentType.extras); 3257 } 3258 3259 /** 3260 * Retrieve the input extras currently associated with the text view, which 3261 * can be viewed as well as modified. 3262 * 3263 * @param create If true, the extras will be created if they don't already 3264 * exist. Otherwise, null will be returned if none have been created. 3265 * @see #setInputExtras(int) 3266 * @see EditorInfo#extras 3267 * @attr ref android.R.styleable#TextView_editorExtras 3268 */ 3269 public Bundle getInputExtras(boolean create) { 3270 if (mInputContentType == null) { 3271 if (!create) return null; 3272 mInputContentType = new InputContentType(); 3273 } 3274 if (mInputContentType.extras == null) { 3275 if (!create) return null; 3276 mInputContentType.extras = new Bundle(); 3277 } 3278 return mInputContentType.extras; 3279 } 3280 3281 /** 3282 * Returns the error message that was set to be displayed with 3283 * {@link #setError}, or <code>null</code> if no error was set 3284 * or if it the error was cleared by the widget after user input. 3285 */ 3286 public CharSequence getError() { 3287 return mError; 3288 } 3289 3290 /** 3291 * Sets the right-hand compound drawable of the TextView to the "error" 3292 * icon and sets an error message that will be displayed in a popup when 3293 * the TextView has focus. The icon and error message will be reset to 3294 * null when any key events cause changes to the TextView's text. If the 3295 * <code>error</code> is <code>null</code>, the error message and icon 3296 * will be cleared. 3297 */ 3298 @android.view.RemotableViewMethod 3299 public void setError(CharSequence error) { 3300 if (error == null) { 3301 setError(null, null); 3302 } else { 3303 Drawable dr = getContext().getResources(). 3304 getDrawable(com.android.internal.R.drawable. 3305 indicator_input_error); 3306 3307 dr.setBounds(0, 0, dr.getIntrinsicWidth(), dr.getIntrinsicHeight()); 3308 setError(error, dr); 3309 } 3310 } 3311 3312 /** 3313 * Sets the right-hand compound drawable of the TextView to the specified 3314 * icon and sets an error message that will be displayed in a popup when 3315 * the TextView has focus. The icon and error message will be reset to 3316 * null when any key events cause changes to the TextView's text. The 3317 * drawable must already have had {@link Drawable#setBounds} set on it. 3318 * If the <code>error</code> is <code>null</code>, the error message will 3319 * be cleared (and you should provide a <code>null</code> icon as well). 3320 */ 3321 public void setError(CharSequence error, Drawable icon) { 3322 error = TextUtils.stringOrSpannedString(error); 3323 3324 mError = error; 3325 mErrorWasChanged = true; 3326 final Drawables dr = mDrawables; 3327 if (dr != null) { 3328 setCompoundDrawables(dr.mDrawableLeft, dr.mDrawableTop, 3329 icon, dr.mDrawableBottom); 3330 } else { 3331 setCompoundDrawables(null, null, icon, null); 3332 } 3333 3334 if (error == null) { 3335 if (mPopup != null) { 3336 if (mPopup.isShowing()) { 3337 mPopup.dismiss(); 3338 } 3339 3340 mPopup = null; 3341 } 3342 } else { 3343 if (isFocused()) { 3344 showError(); 3345 } 3346 } 3347 } 3348 3349 private void showError() { 3350 if (getWindowToken() == null) { 3351 mShowErrorAfterAttach = true; 3352 return; 3353 } 3354 3355 if (mPopup == null) { 3356 LayoutInflater inflater = LayoutInflater.from(getContext()); 3357 final TextView err = (TextView) inflater.inflate(com.android.internal.R.layout.textview_hint, 3358 null); 3359 3360 final float scale = getResources().getDisplayMetrics().density; 3361 mPopup = new ErrorPopup(err, (int) (200 * scale + 0.5f), 3362 (int) (50 * scale + 0.5f)); 3363 mPopup.setFocusable(false); 3364 // The user is entering text, so the input method is needed. We 3365 // don't want the popup to be displayed on top of it. 3366 mPopup.setInputMethodMode(PopupWindow.INPUT_METHOD_NEEDED); 3367 } 3368 3369 TextView tv = (TextView) mPopup.getContentView(); 3370 chooseSize(mPopup, mError, tv); 3371 tv.setText(mError); 3372 3373 mPopup.showAsDropDown(this, getErrorX(), getErrorY()); 3374 mPopup.fixDirection(mPopup.isAboveAnchor()); 3375 } 3376 3377 private static class ErrorPopup extends PopupWindow { 3378 private boolean mAbove = false; 3379 private final TextView mView; 3380 3381 ErrorPopup(TextView v, int width, int height) { 3382 super(v, width, height); 3383 mView = v; 3384 } 3385 3386 void fixDirection(boolean above) { 3387 mAbove = above; 3388 3389 if (above) { 3390 mView.setBackgroundResource(com.android.internal.R.drawable.popup_inline_error_above); 3391 } else { 3392 mView.setBackgroundResource(com.android.internal.R.drawable.popup_inline_error); 3393 } 3394 } 3395 3396 @Override 3397 public void update(int x, int y, int w, int h, boolean force) { 3398 super.update(x, y, w, h, force); 3399 3400 boolean above = isAboveAnchor(); 3401 if (above != mAbove) { 3402 fixDirection(above); 3403 } 3404 } 3405 } 3406 3407 /** 3408 * Returns the Y offset to make the pointy top of the error point 3409 * at the middle of the error icon. 3410 */ 3411 private int getErrorX() { 3412 /* 3413 * The "25" is the distance between the point and the right edge 3414 * of the background 3415 */ 3416 final float scale = getResources().getDisplayMetrics().density; 3417 3418 final Drawables dr = mDrawables; 3419 return getWidth() - mPopup.getWidth() 3420 - getPaddingRight() 3421 - (dr != null ? dr.mDrawableSizeRight : 0) / 2 + (int) (25 * scale + 0.5f); 3422 } 3423 3424 /** 3425 * Returns the Y offset to make the pointy top of the error point 3426 * at the bottom of the error icon. 3427 */ 3428 private int getErrorY() { 3429 /* 3430 * Compound, not extended, because the icon is not clipped 3431 * if the text height is smaller. 3432 */ 3433 int vspace = mBottom - mTop - 3434 getCompoundPaddingBottom() - getCompoundPaddingTop(); 3435 3436 final Drawables dr = mDrawables; 3437 int icontop = getCompoundPaddingTop() 3438 + (vspace - (dr != null ? dr.mDrawableHeightRight : 0)) / 2; 3439 3440 /* 3441 * The "2" is the distance between the point and the top edge 3442 * of the background. 3443 */ 3444 3445 return icontop + (dr != null ? dr.mDrawableHeightRight : 0) 3446 - getHeight() - 2; 3447 } 3448 3449 private void hideError() { 3450 if (mPopup != null) { 3451 if (mPopup.isShowing()) { 3452 mPopup.dismiss(); 3453 } 3454 } 3455 3456 mShowErrorAfterAttach = false; 3457 } 3458 3459 private void chooseSize(PopupWindow pop, CharSequence text, TextView tv) { 3460 int wid = tv.getPaddingLeft() + tv.getPaddingRight(); 3461 int ht = tv.getPaddingTop() + tv.getPaddingBottom(); 3462 3463 /* 3464 * Figure out how big the text would be if we laid it out to the 3465 * full width of this view minus the border. 3466 */ 3467 int cap = getWidth() - wid; 3468 if (cap < 0) { 3469 cap = 200; // We must not be measured yet -- setFrame() will fix it. 3470 } 3471 3472 Layout l = new StaticLayout(text, tv.getPaint(), cap, 3473 Layout.Alignment.ALIGN_NORMAL, 1, 0, true); 3474 float max = 0; 3475 for (int i = 0; i < l.getLineCount(); i++) { 3476 max = Math.max(max, l.getLineWidth(i)); 3477 } 3478 3479 /* 3480 * Now set the popup size to be big enough for the text plus the border. 3481 */ 3482 pop.setWidth(wid + (int) Math.ceil(max)); 3483 pop.setHeight(ht + l.getHeight()); 3484 } 3485 3486 3487 @Override 3488 protected boolean setFrame(int l, int t, int r, int b) { 3489 boolean result = super.setFrame(l, t, r, b); 3490 3491 if (mPopup != null) { 3492 TextView tv = (TextView) mPopup.getContentView(); 3493 chooseSize(mPopup, mError, tv); 3494 mPopup.update(this, getErrorX(), getErrorY(), 3495 mPopup.getWidth(), mPopup.getHeight()); 3496 } 3497 3498 restartMarqueeIfNeeded(); 3499 3500 return result; 3501 } 3502 3503 private void restartMarqueeIfNeeded() { 3504 if (mRestartMarquee && mEllipsize == TextUtils.TruncateAt.MARQUEE) { 3505 mRestartMarquee = false; 3506 startMarquee(); 3507 } 3508 } 3509 3510 /** 3511 * Sets the list of input filters that will be used if the buffer is 3512 * Editable. Has no effect otherwise. 3513 * 3514 * @attr ref android.R.styleable#TextView_maxLength 3515 */ 3516 public void setFilters(InputFilter[] filters) { 3517 if (filters == null) { 3518 throw new IllegalArgumentException(); 3519 } 3520 3521 mFilters = filters; 3522 3523 if (mText instanceof Editable) { 3524 setFilters((Editable) mText, filters); 3525 } 3526 } 3527 3528 /** 3529 * Sets the list of input filters on the specified Editable, 3530 * and includes mInput in the list if it is an InputFilter. 3531 */ 3532 private void setFilters(Editable e, InputFilter[] filters) { 3533 if (mInput instanceof InputFilter) { 3534 InputFilter[] nf = new InputFilter[filters.length + 1]; 3535 3536 System.arraycopy(filters, 0, nf, 0, filters.length); 3537 nf[filters.length] = (InputFilter) mInput; 3538 3539 e.setFilters(nf); 3540 } else { 3541 e.setFilters(filters); 3542 } 3543 } 3544 3545 /** 3546 * Returns the current list of input filters. 3547 */ 3548 public InputFilter[] getFilters() { 3549 return mFilters; 3550 } 3551 3552 ///////////////////////////////////////////////////////////////////////// 3553 3554 private int getVerticalOffset(boolean forceNormal) { 3555 int voffset = 0; 3556 final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 3557 3558 Layout l = mLayout; 3559 if (!forceNormal && mText.length() == 0 && mHintLayout != null) { 3560 l = mHintLayout; 3561 } 3562 3563 if (gravity != Gravity.TOP) { 3564 int boxht; 3565 3566 if (l == mHintLayout) { 3567 boxht = getMeasuredHeight() - getCompoundPaddingTop() - 3568 getCompoundPaddingBottom(); 3569 } else { 3570 boxht = getMeasuredHeight() - getExtendedPaddingTop() - 3571 getExtendedPaddingBottom(); 3572 } 3573 int textht = l.getHeight(); 3574 3575 if (textht < boxht) { 3576 if (gravity == Gravity.BOTTOM) 3577 voffset = boxht - textht; 3578 else // (gravity == Gravity.CENTER_VERTICAL) 3579 voffset = (boxht - textht) >> 1; 3580 } 3581 } 3582 return voffset; 3583 } 3584 3585 private int getBottomVerticalOffset(boolean forceNormal) { 3586 int voffset = 0; 3587 final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK; 3588 3589 Layout l = mLayout; 3590 if (!forceNormal && mText.length() == 0 && mHintLayout != null) { 3591 l = mHintLayout; 3592 } 3593 3594 if (gravity != Gravity.BOTTOM) { 3595 int boxht; 3596 3597 if (l == mHintLayout) { 3598 boxht = getMeasuredHeight() - getCompoundPaddingTop() - 3599 getCompoundPaddingBottom(); 3600 } else { 3601 boxht = getMeasuredHeight() - getExtendedPaddingTop() - 3602 getExtendedPaddingBottom(); 3603 } 3604 int textht = l.getHeight(); 3605 3606 if (textht < boxht) { 3607 if (gravity == Gravity.TOP) 3608 voffset = boxht - textht; 3609 else // (gravity == Gravity.CENTER_VERTICAL) 3610 voffset = (boxht - textht) >> 1; 3611 } 3612 } 3613 return voffset; 3614 } 3615 3616 private void invalidateCursorPath() { 3617 if (mHighlightPathBogus) { 3618 invalidateCursor(); 3619 } else { 3620 synchronized (sTempRect) { 3621 /* 3622 * The reason for this concern about the thickness of the 3623 * cursor and doing the floor/ceil on the coordinates is that 3624 * some EditTexts (notably textfields in the Browser) have 3625 * anti-aliased text where not all the characters are 3626 * necessarily at integer-multiple locations. This should 3627 * make sure the entire cursor gets invalidated instead of 3628 * sometimes missing half a pixel. 3629 */ 3630 3631 float thick = FloatMath.ceil(mTextPaint.getStrokeWidth()); 3632 if (thick < 1.0f) { 3633 thick = 1.0f; 3634 } 3635 3636 thick /= 2; 3637 3638 mHighlightPath.computeBounds(sTempRect, false); 3639 3640 int left = getCompoundPaddingLeft(); 3641 int top = getExtendedPaddingTop() + getVerticalOffset(true); 3642 3643 invalidate((int) FloatMath.floor(left + sTempRect.left - thick), 3644 (int) FloatMath.floor(top + sTempRect.top - thick), 3645 (int) FloatMath.ceil(left + sTempRect.right + thick), 3646 (int) FloatMath.ceil(top + sTempRect.bottom + thick)); 3647 } 3648 } 3649 } 3650 3651 private void invalidateCursor() { 3652 int where = getSelectionEnd(); 3653 3654 invalidateCursor(where, where, where); 3655 } 3656 3657 private void invalidateCursor(int a, int b, int c) { 3658 if (mLayout == null) { 3659 invalidate(); 3660 } else { 3661 if (a >= 0 || b >= 0 || c >= 0) { 3662 int first = Math.min(Math.min(a, b), c); 3663 int last = Math.max(Math.max(a, b), c); 3664 3665 int line = mLayout.getLineForOffset(first); 3666 int top = mLayout.getLineTop(line); 3667 3668 // This is ridiculous, but the descent from the line above 3669 // can hang down into the line we really want to redraw, 3670 // so we have to invalidate part of the line above to make 3671 // sure everything that needs to be redrawn really is. 3672 // (But not the whole line above, because that would cause 3673 // the same problem with the descenders on the line above it!) 3674 if (line > 0) { 3675 top -= mLayout.getLineDescent(line - 1); 3676 } 3677 3678 int line2; 3679 3680 if (first == last) 3681 line2 = line; 3682 else 3683 line2 = mLayout.getLineForOffset(last); 3684 3685 int bottom = mLayout.getLineTop(line2 + 1); 3686 int voffset = getVerticalOffset(true); 3687 3688 int left = getCompoundPaddingLeft() + mScrollX; 3689 invalidate(left, top + voffset + getExtendedPaddingTop(), 3690 left + getWidth() - getCompoundPaddingLeft() - 3691 getCompoundPaddingRight(), 3692 bottom + voffset + getExtendedPaddingTop()); 3693 } 3694 } 3695 } 3696 3697 private void registerForPreDraw() { 3698 final ViewTreeObserver observer = getViewTreeObserver(); 3699 if (observer == null) { 3700 return; 3701 } 3702 3703 if (mPreDrawState == PREDRAW_NOT_REGISTERED) { 3704 observer.addOnPreDrawListener(this); 3705 mPreDrawState = PREDRAW_PENDING; 3706 } else if (mPreDrawState == PREDRAW_DONE) { 3707 mPreDrawState = PREDRAW_PENDING; 3708 } 3709 3710 // else state is PREDRAW_PENDING, so keep waiting. 3711 } 3712 3713 /** 3714 * {@inheritDoc} 3715 */ 3716 public boolean onPreDraw() { 3717 if (mPreDrawState != PREDRAW_PENDING) { 3718 return true; 3719 } 3720 3721 if (mLayout == null) { 3722 assumeLayout(); 3723 } 3724 3725 boolean changed = false; 3726 3727 SelectionModifierCursorController selectionController = null; 3728 if (mSelectionModifierCursorController != null) { 3729 selectionController = (SelectionModifierCursorController) 3730 mSelectionModifierCursorController; 3731 } 3732 3733 3734 if (mMovement != null) { 3735 /* This code also provides auto-scrolling when a cursor is moved using a 3736 * CursorController (insertion point or selection limits). 3737 * For selection, ensure start or end is visible depending on controller's state. 3738 */ 3739 int curs = getSelectionEnd(); 3740 if (selectionController != null && selectionController.isSelectionStartDragged()) { 3741 curs = getSelectionStart(); 3742 } 3743 3744 /* 3745 * TODO: This should really only keep the end in view if 3746 * it already was before the text changed. I'm not sure 3747 * of a good way to tell from here if it was. 3748 */ 3749 if (curs < 0 && 3750 (mGravity & Gravity.VERTICAL_GRAVITY_MASK) == Gravity.BOTTOM) { 3751 curs = mText.length(); 3752 } 3753 3754 if (curs >= 0) { 3755 changed = bringPointIntoView(curs); 3756 } 3757 } else { 3758 changed = bringTextIntoView(); 3759 } 3760 3761 // This has to be checked here since: 3762 // - onFocusChanged cannot start it when focus is given to a view with selected text (after 3763 // a screen rotation) since layout is not yet initialized at that point. 3764 // - ExtractEditText does not call onFocus when it is displayed. Fixing this issue would 3765 // allow to test for hasSelection in onFocusChanged, which would trigger a 3766 // startTextSelectionMode here. TODO 3767 if (mCreatedWithASelection || 3768 (this instanceof ExtractEditText && selectionController != null && hasSelection())) { 3769 startTextSelectionMode(); 3770 mCreatedWithASelection = false; 3771 } 3772 3773 mPreDrawState = PREDRAW_DONE; 3774 return !changed; 3775 } 3776 3777 @Override 3778 protected void onAttachedToWindow() { 3779 super.onAttachedToWindow(); 3780 3781 mTemporaryDetach = false; 3782 3783 if (mShowErrorAfterAttach) { 3784 showError(); 3785 mShowErrorAfterAttach = false; 3786 } 3787 3788 final ViewTreeObserver observer = getViewTreeObserver(); 3789 if (observer != null) { 3790 if (mInsertionPointCursorController != null) { 3791 observer.addOnTouchModeChangeListener(mInsertionPointCursorController); 3792 } 3793 if (mSelectionModifierCursorController != null) { 3794 observer.addOnTouchModeChangeListener(mSelectionModifierCursorController); 3795 } 3796 } 3797 } 3798 3799 @Override 3800 protected void onDetachedFromWindow() { 3801 super.onDetachedFromWindow(); 3802 3803 final ViewTreeObserver observer = getViewTreeObserver(); 3804 if (observer != null) { 3805 if (mPreDrawState != PREDRAW_NOT_REGISTERED) { 3806 observer.removeOnPreDrawListener(this); 3807 mPreDrawState = PREDRAW_NOT_REGISTERED; 3808 } 3809 if (mInsertionPointCursorController != null) { 3810 observer.removeOnTouchModeChangeListener(mInsertionPointCursorController); 3811 } 3812 if (mSelectionModifierCursorController != null) { 3813 observer.removeOnTouchModeChangeListener(mSelectionModifierCursorController); 3814 } 3815 } 3816 3817 if (mError != null) { 3818 hideError(); 3819 } 3820 3821 if (mBlink != null) { 3822 mBlink.cancel(); 3823 } 3824 3825 if (mInsertionPointCursorController != null) { 3826 mInsertionPointCursorController.onDetached(); 3827 } 3828 3829 if (mSelectionModifierCursorController != null) { 3830 mSelectionModifierCursorController.onDetached(); 3831 } 3832 3833 hideControllers(); 3834 } 3835 3836 @Override 3837 protected boolean isPaddingOffsetRequired() { 3838 return mShadowRadius != 0 || mDrawables != null; 3839 } 3840 3841 @Override 3842 protected int getLeftPaddingOffset() { 3843 return getCompoundPaddingLeft() - mPaddingLeft + 3844 (int) Math.min(0, mShadowDx - mShadowRadius); 3845 } 3846 3847 @Override 3848 protected int getTopPaddingOffset() { 3849 return (int) Math.min(0, mShadowDy - mShadowRadius); 3850 } 3851 3852 @Override 3853 protected int getBottomPaddingOffset() { 3854 return (int) Math.max(0, mShadowDy + mShadowRadius); 3855 } 3856 3857 @Override 3858 protected int getRightPaddingOffset() { 3859 return -(getCompoundPaddingRight() - mPaddingRight) + 3860 (int) Math.max(0, mShadowDx + mShadowRadius); 3861 } 3862 3863 @Override 3864 protected boolean verifyDrawable(Drawable who) { 3865 final boolean verified = super.verifyDrawable(who); 3866 if (!verified && mDrawables != null) { 3867 return who == mDrawables.mDrawableLeft || who == mDrawables.mDrawableTop || 3868 who == mDrawables.mDrawableRight || who == mDrawables.mDrawableBottom; 3869 } 3870 return verified; 3871 } 3872 3873 @Override 3874 public void invalidateDrawable(Drawable drawable) { 3875 if (verifyDrawable(drawable)) { 3876 final Rect dirty = drawable.getBounds(); 3877 int scrollX = mScrollX; 3878 int scrollY = mScrollY; 3879 3880 // IMPORTANT: The coordinates below are based on the coordinates computed 3881 // for each compound drawable in onDraw(). Make sure to update each section 3882 // accordingly. 3883 final TextView.Drawables drawables = mDrawables; 3884 if (drawables != null) { 3885 if (drawable == drawables.mDrawableLeft) { 3886 final int compoundPaddingTop = getCompoundPaddingTop(); 3887 final int compoundPaddingBottom = getCompoundPaddingBottom(); 3888 final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; 3889 3890 scrollX += mPaddingLeft; 3891 scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightLeft) / 2; 3892 } else if (drawable == drawables.mDrawableRight) { 3893 final int compoundPaddingTop = getCompoundPaddingTop(); 3894 final int compoundPaddingBottom = getCompoundPaddingBottom(); 3895 final int vspace = mBottom - mTop - compoundPaddingBottom - compoundPaddingTop; 3896 3897 scrollX += (mRight - mLeft - mPaddingRight - drawables.mDrawableSizeRight); 3898 scrollY += compoundPaddingTop + (vspace - drawables.mDrawableHeightRight) / 2; 3899 } else if (drawable == drawables.mDrawableTop) { 3900 final int compoundPaddingLeft = getCompoundPaddingLeft(); 3901 final int compoundPaddingRight = getCompoundPaddingRight(); 3902 final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; 3903 3904 scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthTop) / 2; 3905 scrollY += mPaddingTop; 3906 } else if (drawable == drawables.mDrawableBottom) { 3907 final int compoundPaddingLeft = getCompoundPaddingLeft(); 3908 final int compoundPaddingRight = getCompoundPaddingRight(); 3909 final int hspace = mRight - mLeft - compoundPaddingRight - compoundPaddingLeft; 3910 3911 scrollX += compoundPaddingLeft + (hspace - drawables.mDrawableWidthBottom) / 2; 3912 scrollY += (mBottom - mTop - mPaddingBottom - drawables.mDrawableSizeBottom); 3913 } 3914 } 3915 3916 invalidate(dirty.left + scrollX, dirty.top + scrollY, 3917 dirty.right + scrollX, dirty.bottom + scrollY); 3918 } 3919 } 3920 3921 @Override 3922 protected void onDraw(Canvas canvas) { 3923 restartMarqueeIfNeeded(); 3924 3925 // Draw the background for this view 3926 super.onDraw(canvas); 3927 3928 final int compoundPaddingLeft = getCompoundPaddingLeft(); 3929 final int compoundPaddingTop = getCompoundPaddingTop(); 3930 final int compoundPaddingRight = getCompoundPaddingRight(); 3931 final int compoundPaddingBottom = getCompoundPaddingBottom(); 3932 final int scrollX = mScrollX; 3933 final int scrollY = mScrollY; 3934 final int right = mRight; 3935 final int left = mLeft; 3936 final int bottom = mBottom; 3937 final int top = mTop; 3938 3939 final Drawables dr = mDrawables; 3940 if (dr != null) { 3941 /* 3942 * Compound, not extended, because the icon is not clipped 3943 * if the text height is smaller. 3944 */ 3945 3946 int vspace = bottom - top - compoundPaddingBottom - compoundPaddingTop; 3947 int hspace = right - left - compoundPaddingRight - compoundPaddingLeft; 3948 3949 // IMPORTANT: The coordinates computed are also used in invalidateDrawable() 3950 // Make sure to update invalidateDrawable() when changing this code. 3951 if (dr.mDrawableLeft != null) { 3952 canvas.save(); 3953 canvas.translate(scrollX + mPaddingLeft, 3954 scrollY + compoundPaddingTop + 3955 (vspace - dr.mDrawableHeightLeft) / 2); 3956 dr.mDrawableLeft.draw(canvas); 3957 canvas.restore(); 3958 } 3959 3960 // IMPORTANT: The coordinates computed are also used in invalidateDrawable() 3961 // Make sure to update invalidateDrawable() when changing this code. 3962 if (dr.mDrawableRight != null) { 3963 canvas.save(); 3964 canvas.translate(scrollX + right - left - mPaddingRight - dr.mDrawableSizeRight, 3965 scrollY + compoundPaddingTop + (vspace - dr.mDrawableHeightRight) / 2); 3966 dr.mDrawableRight.draw(canvas); 3967 canvas.restore(); 3968 } 3969 3970 // IMPORTANT: The coordinates computed are also used in invalidateDrawable() 3971 // Make sure to update invalidateDrawable() when changing this code. 3972 if (dr.mDrawableTop != null) { 3973 canvas.save(); 3974 canvas.translate(scrollX + compoundPaddingLeft + (hspace - dr.mDrawableWidthTop) / 2, 3975 scrollY + mPaddingTop); 3976 dr.mDrawableTop.draw(canvas); 3977 canvas.restore(); 3978 } 3979 3980 // IMPORTANT: The coordinates computed are also used in invalidateDrawable() 3981 // Make sure to update invalidateDrawable() when changing this code. 3982 if (dr.mDrawableBottom != null) { 3983 canvas.save(); 3984 canvas.translate(scrollX + compoundPaddingLeft + 3985 (hspace - dr.mDrawableWidthBottom) / 2, 3986 scrollY + bottom - top - mPaddingBottom - dr.mDrawableSizeBottom); 3987 dr.mDrawableBottom.draw(canvas); 3988 canvas.restore(); 3989 } 3990 } 3991 3992 if (mPreDrawState == PREDRAW_DONE) { 3993 final ViewTreeObserver observer = getViewTreeObserver(); 3994 if (observer != null) { 3995 observer.removeOnPreDrawListener(this); 3996 mPreDrawState = PREDRAW_NOT_REGISTERED; 3997 } 3998 } 3999 4000 int color = mCurTextColor; 4001 4002 if (mLayout == null) { 4003 assumeLayout(); 4004 } 4005 4006 Layout layout = mLayout; 4007 int cursorcolor = color; 4008 4009 if (mHint != null && mText.length() == 0) { 4010 if (mHintTextColor != null) { 4011 color = mCurHintTextColor; 4012 } 4013 4014 layout = mHintLayout; 4015 } 4016 4017 mTextPaint.setColor(color); 4018 mTextPaint.drawableState = getDrawableState(); 4019 4020 canvas.save(); 4021 /* Would be faster if we didn't have to do this. Can we chop the 4022 (displayable) text so that we don't need to do this ever? 4023 */ 4024 4025 int extendedPaddingTop = getExtendedPaddingTop(); 4026 int extendedPaddingBottom = getExtendedPaddingBottom(); 4027 4028 float clipLeft = compoundPaddingLeft + scrollX; 4029 float clipTop = extendedPaddingTop + scrollY; 4030 float clipRight = right - left - compoundPaddingRight + scrollX; 4031 float clipBottom = bottom - top - extendedPaddingBottom + scrollY; 4032 4033 if (mShadowRadius != 0) { 4034 clipLeft += Math.min(0, mShadowDx - mShadowRadius); 4035 clipRight += Math.max(0, mShadowDx + mShadowRadius); 4036 4037 clipTop += Math.min(0, mShadowDy - mShadowRadius); 4038 clipBottom += Math.max(0, mShadowDy + mShadowRadius); 4039 } 4040 4041 canvas.clipRect(clipLeft, clipTop, clipRight, clipBottom); 4042 4043 int voffsetText = 0; 4044 int voffsetCursor = 0; 4045 4046 // translate in by our padding 4047 { 4048 /* shortcircuit calling getVerticaOffset() */ 4049 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { 4050 voffsetText = getVerticalOffset(false); 4051 voffsetCursor = getVerticalOffset(true); 4052 } 4053 canvas.translate(compoundPaddingLeft, extendedPaddingTop + voffsetText); 4054 } 4055 4056 if (mEllipsize == TextUtils.TruncateAt.MARQUEE) { 4057 if (!mSingleLine && getLineCount() == 1 && canMarquee() && 4058 (mGravity & Gravity.HORIZONTAL_GRAVITY_MASK) != Gravity.LEFT) { 4059 canvas.translate(mLayout.getLineRight(0) - (mRight - mLeft - 4060 getCompoundPaddingLeft() - getCompoundPaddingRight()), 0.0f); 4061 } 4062 4063 if (mMarquee != null && mMarquee.isRunning()) { 4064 canvas.translate(-mMarquee.mScroll, 0.0f); 4065 } 4066 } 4067 4068 Path highlight = null; 4069 int selStart = -1, selEnd = -1; 4070 4071 // If there is no movement method, then there can be no selection. 4072 // Check that first and attempt to skip everything having to do with 4073 // the cursor. 4074 // XXX This is not strictly true -- a program could set the 4075 // selection manually if it really wanted to. 4076 if (mMovement != null && (isFocused() || isPressed())) { 4077 selStart = getSelectionStart(); 4078 selEnd = getSelectionEnd(); 4079 4080 if (mCursorVisible && selStart >= 0 && isEnabled()) { 4081 if (mHighlightPath == null) 4082 mHighlightPath = new Path(); 4083 4084 if (selStart == selEnd) { 4085 if ((SystemClock.uptimeMillis() - mShowCursor) % (2 * BLINK) < BLINK) { 4086 if (mHighlightPathBogus) { 4087 mHighlightPath.reset(); 4088 mLayout.getCursorPath(selStart, mHighlightPath, mText); 4089 mHighlightPathBogus = false; 4090 } 4091 4092 // XXX should pass to skin instead of drawing directly 4093 mHighlightPaint.setColor(cursorcolor); 4094 mHighlightPaint.setStyle(Paint.Style.STROKE); 4095 4096 highlight = mHighlightPath; 4097 } 4098 } else { 4099 if (mHighlightPathBogus) { 4100 mHighlightPath.reset(); 4101 mLayout.getSelectionPath(selStart, selEnd, mHighlightPath); 4102 mHighlightPathBogus = false; 4103 } 4104 4105 // XXX should pass to skin instead of drawing directly 4106 mHighlightPaint.setColor(mHighlightColor); 4107 mHighlightPaint.setStyle(Paint.Style.FILL); 4108 4109 highlight = mHighlightPath; 4110 } 4111 } 4112 } 4113 4114 /* Comment out until we decide what to do about animations 4115 boolean isLinearTextOn = false; 4116 if (currentTransformation != null) { 4117 isLinearTextOn = mTextPaint.isLinearTextOn(); 4118 Matrix m = currentTransformation.getMatrix(); 4119 if (!m.isIdentity()) { 4120 // mTextPaint.setLinearTextOn(true); 4121 } 4122 } 4123 */ 4124 4125 final InputMethodState ims = mInputMethodState; 4126 if (ims != null && ims.mBatchEditNesting == 0) { 4127 InputMethodManager imm = InputMethodManager.peekInstance(); 4128 if (imm != null) { 4129 if (imm.isActive(this)) { 4130 boolean reported = false; 4131 if (ims.mContentChanged || ims.mSelectionModeChanged) { 4132 // We are in extract mode and the content has changed 4133 // in some way... just report complete new text to the 4134 // input method. 4135 reported = reportExtractedText(); 4136 } 4137 if (!reported && highlight != null) { 4138 int candStart = -1; 4139 int candEnd = -1; 4140 if (mText instanceof Spannable) { 4141 Spannable sp = (Spannable)mText; 4142 candStart = EditableInputConnection.getComposingSpanStart(sp); 4143 candEnd = EditableInputConnection.getComposingSpanEnd(sp); 4144 } 4145 imm.updateSelection(this, selStart, selEnd, candStart, candEnd); 4146 } 4147 } 4148 4149 if (imm.isWatchingCursor(this) && highlight != null) { 4150 highlight.computeBounds(ims.mTmpRectF, true); 4151 ims.mTmpOffset[0] = ims.mTmpOffset[1] = 0; 4152 4153 canvas.getMatrix().mapPoints(ims.mTmpOffset); 4154 ims.mTmpRectF.offset(ims.mTmpOffset[0], ims.mTmpOffset[1]); 4155 4156 ims.mTmpRectF.offset(0, voffsetCursor - voffsetText); 4157 4158 ims.mCursorRectInWindow.set((int)(ims.mTmpRectF.left + 0.5), 4159 (int)(ims.mTmpRectF.top + 0.5), 4160 (int)(ims.mTmpRectF.right + 0.5), 4161 (int)(ims.mTmpRectF.bottom + 0.5)); 4162 4163 imm.updateCursor(this, 4164 ims.mCursorRectInWindow.left, ims.mCursorRectInWindow.top, 4165 ims.mCursorRectInWindow.right, ims.mCursorRectInWindow.bottom); 4166 } 4167 } 4168 } 4169 4170 layout.draw(canvas, highlight, mHighlightPaint, voffsetCursor - voffsetText); 4171 4172 if (mMarquee != null && mMarquee.shouldDrawGhost()) { 4173 canvas.translate((int) mMarquee.getGhostOffset(), 0.0f); 4174 layout.draw(canvas, highlight, mHighlightPaint, voffsetCursor - voffsetText); 4175 } 4176 4177 /* Comment out until we decide what to do about animations 4178 if (currentTransformation != null) { 4179 mTextPaint.setLinearTextOn(isLinearTextOn); 4180 } 4181 */ 4182 4183 canvas.restore(); 4184 4185 updateCursorControllerPositions(); 4186 } 4187 4188 /** 4189 * Update the positions of the CursorControllers. Needed by WebTextView, 4190 * which does not draw. 4191 * @hide 4192 */ 4193 protected void updateCursorControllerPositions() { 4194 if (mInsertionPointCursorController != null && 4195 mInsertionPointCursorController.isShowing()) { 4196 mInsertionPointCursorController.updatePosition(); 4197 } 4198 4199 if (mSelectionModifierCursorController != null && 4200 mSelectionModifierCursorController.isShowing()) { 4201 mSelectionModifierCursorController.updatePosition(); 4202 } 4203 } 4204 4205 @Override 4206 public void getFocusedRect(Rect r) { 4207 if (mLayout == null) { 4208 super.getFocusedRect(r); 4209 return; 4210 } 4211 4212 int sel = getSelectionEnd(); 4213 if (sel < 0) { 4214 super.getFocusedRect(r); 4215 return; 4216 } 4217 4218 int line = mLayout.getLineForOffset(sel); 4219 r.top = mLayout.getLineTop(line); 4220 r.bottom = mLayout.getLineBottom(line); 4221 4222 r.left = (int) mLayout.getPrimaryHorizontal(sel); 4223 r.right = r.left + 1; 4224 4225 // Adjust for padding and gravity. 4226 int paddingLeft = getCompoundPaddingLeft(); 4227 int paddingTop = getExtendedPaddingTop(); 4228 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { 4229 paddingTop += getVerticalOffset(false); 4230 } 4231 r.offset(paddingLeft, paddingTop); 4232 } 4233 4234 /** 4235 * Return the number of lines of text, or 0 if the internal Layout has not 4236 * been built. 4237 */ 4238 public int getLineCount() { 4239 return mLayout != null ? mLayout.getLineCount() : 0; 4240 } 4241 4242 /** 4243 * Return the baseline for the specified line (0...getLineCount() - 1) 4244 * If bounds is not null, return the top, left, right, bottom extents 4245 * of the specified line in it. If the internal Layout has not been built, 4246 * return 0 and set bounds to (0, 0, 0, 0) 4247 * @param line which line to examine (0..getLineCount() - 1) 4248 * @param bounds Optional. If not null, it returns the extent of the line 4249 * @return the Y-coordinate of the baseline 4250 */ 4251 public int getLineBounds(int line, Rect bounds) { 4252 if (mLayout == null) { 4253 if (bounds != null) { 4254 bounds.set(0, 0, 0, 0); 4255 } 4256 return 0; 4257 } 4258 else { 4259 int baseline = mLayout.getLineBounds(line, bounds); 4260 4261 int voffset = getExtendedPaddingTop(); 4262 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { 4263 voffset += getVerticalOffset(true); 4264 } 4265 if (bounds != null) { 4266 bounds.offset(getCompoundPaddingLeft(), voffset); 4267 } 4268 return baseline + voffset; 4269 } 4270 } 4271 4272 @Override 4273 public int getBaseline() { 4274 if (mLayout == null) { 4275 return super.getBaseline(); 4276 } 4277 4278 int voffset = 0; 4279 if ((mGravity & Gravity.VERTICAL_GRAVITY_MASK) != Gravity.TOP) { 4280 voffset = getVerticalOffset(true); 4281 } 4282 4283 return getExtendedPaddingTop() + voffset + mLayout.getLineBaseline(0); 4284 } 4285 4286 @Override 4287 public boolean onKeyDown(int keyCode, KeyEvent event) { 4288 int which = doKeyDown(keyCode, event, null); 4289 if (which == 0) { 4290 // Go through default dispatching. 4291 return super.onKeyDown(keyCode, event); 4292 } 4293 4294 return true; 4295 } 4296 4297 @Override 4298 public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) { 4299 KeyEvent down = KeyEvent.changeAction(event, KeyEvent.ACTION_DOWN); 4300 4301 int which = doKeyDown(keyCode, down, event); 4302 if (which == 0) { 4303 // Go through default dispatching. 4304 return super.onKeyMultiple(keyCode, repeatCount, event); 4305 } 4306 if (which == -1) { 4307 // Consumed the whole thing. 4308 return true; 4309 } 4310 4311 repeatCount--; 4312 4313 // We are going to dispatch the remaining events to either the input 4314 // or movement method. To do this, we will just send a repeated stream 4315 // of down and up events until we have done the complete repeatCount. 4316 // It would be nice if those interfaces had an onKeyMultiple() method, 4317 // but adding that is a more complicated change. 4318 KeyEvent up = KeyEvent.changeAction(event, KeyEvent.ACTION_UP); 4319 if (which == 1) { 4320 mInput.onKeyUp(this, (Editable)mText, keyCode, up); 4321 while (--repeatCount > 0) { 4322 mInput.onKeyDown(this, (Editable)mText, keyCode, down); 4323 mInput.onKeyUp(this, (Editable)mText, keyCode, up); 4324 } 4325 if (mError != null && !mErrorWasChanged) { 4326 setError(null, null); 4327 } 4328 4329 } else if (which == 2) { 4330 mMovement.onKeyUp(this, (Spannable)mText, keyCode, up); 4331 while (--repeatCount > 0) { 4332 mMovement.onKeyDown(this, (Spannable)mText, keyCode, down); 4333 mMovement.onKeyUp(this, (Spannable)mText, keyCode, up); 4334 } 4335 } 4336 4337 return true; 4338 } 4339 4340 /** 4341 * Returns true if pressing ENTER in this field advances focus instead 4342 * of inserting the character. This is true mostly in single-line fields, 4343 * but also in mail addresses and subjects which will display on multiple 4344 * lines but where it doesn't make sense to insert newlines. 4345 */ 4346 private boolean shouldAdvanceFocusOnEnter() { 4347 if (mInput == null) { 4348 return false; 4349 } 4350 4351 if (mSingleLine) { 4352 return true; 4353 } 4354 4355 if ((mInputType & EditorInfo.TYPE_MASK_CLASS) == EditorInfo.TYPE_CLASS_TEXT) { 4356 int variation = mInputType & EditorInfo.TYPE_MASK_VARIATION; 4357 4358 if (variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_ADDRESS || 4359 variation == EditorInfo.TYPE_TEXT_VARIATION_EMAIL_SUBJECT) { 4360 return true; 4361 } 4362 } 4363 4364 return false; 4365 } 4366 4367 private int doKeyDown(int keyCode, KeyEvent event, KeyEvent otherEvent) { 4368 if (!isEnabled()) { 4369 return 0; 4370 } 4371 4372 switch (keyCode) { 4373 case KeyEvent.KEYCODE_ENTER: 4374 mEnterKeyIsDown = true; 4375 // If ALT modifier is held, then we always insert a 4376 // newline character. 4377 if ((event.getMetaState()&KeyEvent.META_ALT_ON) == 0) { 4378 4379 // When mInputContentType is set, we know that we are 4380 // running in a "modern" cupcake environment, so don't need 4381 // to worry about the application trying to capture 4382 // enter key events. 4383 if (mInputContentType != null) { 4384 4385 // If there is an action listener, given them a 4386 // chance to consume the event. 4387 if (mInputContentType.onEditorActionListener != null && 4388 mInputContentType.onEditorActionListener.onEditorAction( 4389 this, EditorInfo.IME_NULL, event)) { 4390 mInputContentType.enterDown = true; 4391 // We are consuming the enter key for them. 4392 return -1; 4393 } 4394 } 4395 4396 // If our editor should move focus when enter is pressed, or 4397 // this is a generated event from an IME action button, then 4398 // don't let it be inserted into the text. 4399 if ((event.getFlags()&KeyEvent.FLAG_EDITOR_ACTION) != 0 4400 || shouldAdvanceFocusOnEnter()) { 4401 return -1; 4402 } 4403 } 4404 break; 4405 4406 case<