源码是棒棒糖,很甜不假,可是牙口不好的,想贪多咬下一块可不易,还是慢慢添吧,更甜更持久
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
* Copyright (C) 2006 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.widget;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_LENGTH;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_ARG_START_INDEX;
import static android.view.accessibility.AccessibilityNodeInfo.EXTRA_DATA_TEXT_CHARACTER_LOCATION_KEY;
import static android.view.inputmethod.CursorAnchorInfo.FLAG_HAS_VISIBLE_REGION;
import android.R;
import android.annotation.ColorInt;
import android.annotation.DrawableRes;
import android.annotation.FloatRange;
import android.annotation.IntDef;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.Size;
import android.annotation.StringRes;
import android.annotation.StyleRes;
import android.annotation.XmlRes;
import android.app.Activity;
import android.app.assist.AssistStructure;
import android.content.ClipData;
import android.content.ClipDescription;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.content.UndoManager;
import android.content.res.ColorStateList;
import android.content.res.CompatibilityInfo;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.graphics.BaseCanvas;
import android.graphics.Canvas;
import android.graphics.Insets;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.graphics.fonts.FontVariationAxis;
import android.icu.text.DecimalFormatSymbols;
import android.os.AsyncTask;
import android.os.Build.VERSION_CODES;
import android.os.Bundle;
import android.os.LocaleList;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.ParcelableParcel;
import android.os.SystemClock;
import android.provider.Settings;
import android.text.BoringLayout;
import android.text.DynamicLayout;
import android.text.Editable;
import android.text.GetChars;
import android.text.GraphicsOperations;
import android.text.InputFilter;
import android.text.InputType;
import android.text.Layout;
import android.text.ParcelableSpan;
import android.text.Selection;
import android.text.SpanWatcher;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.Spanned;
import android.text.SpannedString;
import android.text.StaticLayout;
import android.text.TextDirectionHeuristic;
import android.text.TextDirectionHeuristics;
import android.text.TextPaint;
import android.text.TextUtils;
import android.text.TextUtils.TruncateAt;
import android.text.TextWatcher;
import android.text.method.AllCapsTransformationMethod;
import android.text.method.ArrowKeyMovementMethod;
import android.text.method.DateKeyListener;
import android.text.method.DateTimeKeyListener;
import android.text.method.DialerKeyListener;
import android.text.method.DigitsKeyListener;
import android.text.method.KeyListener;
import android.text.method.LinkMovementMethod;
import android.text.method.MetaKeyKeyListener;
import android.text.method.MovementMethod;
import android.text.method.PasswordTransformationMethod;
import android.text.method.SingleLineTransformationMethod;
import android.text.method.TextKeyListener;
import android.text.method.TimeKeyListener;
import android.text.method.TransformationMethod;
import android.text.method.TransformationMethod2;
import android.text.method.WordIterator;
import android.text.style.CharacterStyle;
import android.text.style.ClickableSpan;
import android.text.style.ParagraphStyle;
import android.text.style.SpellCheckSpan;
import android.text.style.SuggestionSpan;
import android.text.style.URLSpan;
import android.text.style.UpdateAppearance;
import android.text.util.Linkify;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.IntArray;
import android.util.Log;
import android.util.TypedValue;
import android.view.AccessibilityIterators.TextSegmentIterator;
import android.view.ActionMode;
import android.view.Choreographer;
import android.view.ContextMenu;
import android.view.DragEvent;
import android.view.Gravity;
import android.view.HapticFeedbackConstants;
import android.view.InputDevice;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.PointerIcon;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewDebug;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewHierarchyEncoder;
import android.view.ViewParent;
import android.view.ViewRootImpl;
import android.view.ViewStructure;
import android.view.ViewTreeObserver;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityManager;
import android.view.accessibility.AccessibilityNodeInfo;
import android.view.animation.AnimationUtils;
import android.view.autofill.AutofillManager;
import android.view.autofill.AutofillValue;
import android.view.inputmethod.BaseInputConnection;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.CorrectionInfo;
import android.view.inputmethod.CursorAnchorInfo;
import android.view.inputmethod.EditorInfo;
import android.view.inputmethod.ExtractedText;
import android.view.inputmethod.ExtractedTextRequest;
import android.view.inputmethod.InputConnection;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import android.view.textclassifier.TextClassifier;
import android.view.textservice.SpellCheckerSubtype;
import android.view.textservice.TextServicesManager;
import android.widget.RemoteViews.RemoteView;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.FastMath;
import com.android.internal.widget.EditableInputConnection;
import libcore.util.EmptyArray;
import org.xmlpull.v1.XmlPullParserException;
import java.io.IOException;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Locale;
/**
* A user interface element that displays text to the user.
* To provide user-editable text, see {@link EditText}.
* <p>
* The following code sample shows a typical use, with an XML layout
* and code to modify the contents of the text view:
* </p>
* <pre>
* <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
* <TextView
* android:id="@+id/text_view_id"
* android:layout_height="wrap_content"
* android:layout_width="wrap_content"
* android:text="@string/hello" />
* </LinearLayout>
* </pre>
* <p>
* This code sample demonstrates how to modify the contents of the text view
* defined in the previous XML layout:
* </p>
* <pre>
* public class MainActivity extends Activity {
*
* protected void onCreate(Bundle savedInstanceState) {
* super.onCreate(savedInstanceState);
* setContentView(R.layout.activity_main);
* final TextView helloTextView = (TextView) findViewById(R.id.text_view_id);
* helloTextView.setText(R.string.user_greeting);
* }
* }
* </pre>
* <p>
* To customize the appearance of TextView, see <a href="https://developer.android.com/guide/topics/ui/themes.html">Styles and Themes</a>.
* </p>
* <p>
* <b>XML attributes</b>
* <p>
* See {@link android.R.styleable#TextView TextView Attributes},
* {@link android.R.styleable#View View Attributes}
*
* @attr ref android.R.styleable#TextView_text
* @attr ref android.R.styleable#TextView_bufferType
* @attr ref android.R.styleable#TextView_hint
* @attr ref android.R.styleable#TextView_textColor
* @attr ref android.R.styleable#TextView_textColorHighlight
* @attr ref android.R.styleable#TextView_textColorHint
* @attr ref android.R.styleable#TextView_textAppearance
* @attr ref android.R.styleable#TextView_textColorLink
* @attr ref android.R.styleable#TextView_textSize
* @attr ref android.R.styleable#TextView_textScaleX
* @attr ref android.R.styleable#TextView_fontFamily
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
* @attr ref android.R.styleable#TextView_cursorVisible
* @attr ref android.R.styleable#TextView_maxLines
* @attr ref android.R.styleable#TextView_maxHeight
* @attr ref android.R.styleable#TextView_lines
* @attr ref android.R.styleable#TextView_height
* @attr ref android.R.styleable#TextView_minLines
* @attr ref android.R.styleable#TextView_minHeight
* @attr ref android.R.styleable#TextView_maxEms
* @attr ref android.R.styleable#TextView_maxWidth
* @attr ref android.R.styleable#TextView_ems
* @attr ref android.R.styleable#TextView_width
* @attr ref android.R.styleable#TextView_minEms
* @attr ref android.R.styleable#TextView_minWidth
* @attr ref android.R.styleable#TextView_gravity
* @attr ref android.R.styleable#TextView_scrollHorizontally
* @attr ref android.R.styleable#TextView_password
* @attr ref android.R.styleable#TextView_singleLine
* @attr ref android.R.styleable#TextView_selectAllOnFocus
* @attr ref android.R.styleable#TextView_includeFontPadding
* @attr ref android.R.styleable#TextView_maxLength
* @attr ref android.R.styleable#TextView_shadowColor
* @attr ref android.R.styleable#TextView_shadowDx
* @attr ref android.R.styleable#TextView_shadowDy
* @attr ref android.R.styleable#TextView_shadowRadius
* @attr ref android.R.styleable#TextView_autoLink
* @attr ref android.R.styleable#TextView_linksClickable
* @attr ref android.R.styleable#TextView_numeric
* @attr ref android.R.styleable#TextView_digits
* @attr ref android.R.styleable#TextView_phoneNumber
* @attr ref android.R.styleable#TextView_inputMethod
* @attr ref android.R.styleable#TextView_capitalize
* @attr ref android.R.styleable#TextView_autoText
* @attr ref android.R.styleable#TextView_editable
* @attr ref android.R.styleable#TextView_freezesText
* @attr ref android.R.styleable#TextView_ellipsize
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableBottom
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableLeft
* @attr ref android.R.styleable#TextView_drawableStart
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawablePadding
* @attr ref android.R.styleable#TextView_drawableTint
* @attr ref android.R.styleable#TextView_drawableTintMode
* @attr ref android.R.styleable#TextView_lineSpacingExtra
* @attr ref android.R.styleable#TextView_lineSpacingMultiplier
* @attr ref android.R.styleable#TextView_marqueeRepeatLimit
* @attr ref android.R.styleable#TextView_inputType
* @attr ref android.R.styleable#TextView_imeOptions
* @attr ref android.R.styleable#TextView_privateImeOptions
* @attr ref android.R.styleable#TextView_imeActionLabel
* @attr ref android.R.styleable#TextView_imeActionId
* @attr ref android.R.styleable#TextView_editorExtras
* @attr ref android.R.styleable#TextView_elegantTextHeight
* @attr ref android.R.styleable#TextView_letterSpacing
* @attr ref android.R.styleable#TextView_fontFeatureSettings
* @attr ref android.R.styleable#TextView_breakStrategy
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @attr ref android.R.styleable#TextView_autoSizeTextType
* @attr ref android.R.styleable#TextView_autoSizeMinTextSize
* @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
* @attr ref android.R.styleable#TextView_autoSizeStepGranularity
* @attr ref android.R.styleable#TextView_autoSizePresetSizes
*/
@RemoteView
public class TextView extends View implements ViewTreeObserver.OnPreDrawListener {
static final String LOG_TAG = "TextView";
static final boolean DEBUG_EXTRACT = false;
static final boolean DEBUG_AUTOFILL = false;
private static final float[] TEMP_POSITION = new float[2];
// Enum for the "typeface" XML parameter.
// TODO: How can we get this from the XML instead of hardcoding it here?
private static final int SANS = 1;
private static final int SERIF = 2;
private static final int MONOSPACE = 3;
// Bitfield for the "numeric" XML parameter.
// TODO: How can we get this from the XML instead of hardcoding it here?
private static final int SIGNED = 2;
private static final int DECIMAL = 4;
/**
* Draw marquee text with fading edges as usual
*/
private static final int MARQUEE_FADE_NORMAL = 0;
/**
* Draw marquee text as ellipsize end while inactive instead of with the fade.
* (Useful for devices where the fade can be expensive if overdone)
*/
private static final int MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS = 1;
/**
* Draw marquee text with fading edges because it is currently active/animating.
*/
private static final int MARQUEE_FADE_SWITCH_SHOW_FADE = 2;
private static final int LINES = 1;
private static final int EMS = LINES;
private static final int PIXELS = 2;
private static final RectF TEMP_RECTF = new RectF();
/** @hide */
static final int VERY_WIDE = 1024 * 1024; // XXX should be much larger
private static final int ANIMATED_SCROLL_GAP = 250;
private static final InputFilter[] NO_FILTERS = new InputFilter[0];
private static final Spanned EMPTY_SPANNED = new SpannedString("");
private static final int CHANGE_WATCHER_PRIORITY = 100;
// New state used to change background based on whether this TextView is multiline.
private static final int[] MULTILINE_STATE_SET = { R.attr.state_multiline };
// Accessibility action to share selected text.
private static final int ACCESSIBILITY_ACTION_SHARE = 0x10000000;
/**
* @hide
*/
// Accessibility action start id for "process text" actions.
static final int ACCESSIBILITY_ACTION_PROCESS_TEXT_START_ID = 0x10000100;
/**
* @hide
*/
static final int PROCESS_TEXT_REQUEST_CODE = 100;
/**
* Return code of {@link #doKeyDown}.
*/
private static final int KEY_EVENT_NOT_HANDLED = 0;
private static final int KEY_EVENT_HANDLED = -1;
private static final int KEY_DOWN_HANDLED_BY_KEY_LISTENER = 1;
private static final int KEY_DOWN_HANDLED_BY_MOVEMENT_METHOD = 2;
// System wide time for last cut, copy or text changed action.
static long sLastCutCopyOrTextChangedTime;
private ColorStateList mTextColor;
private ColorStateList mHintTextColor;
private ColorStateList mLinkTextColor;
@ViewDebug.ExportedProperty(category = "text")
private int mCurTextColor;
private int mCurHintTextColor;
private boolean mFreezesText;
private Editable.Factory mEditableFactory = Editable.Factory.getInstance();
private Spannable.Factory mSpannableFactory = Spannable.Factory.getInstance();
private float mShadowRadius, mShadowDx, mShadowDy;
private int mShadowColor;
private boolean mPreDrawRegistered;
private boolean mPreDrawListenerDetached;
private TextClassifier mTextClassifier;
// A flag to prevent repeated movements from escaping the enclosing text view. The idea here is
// that if a user is holding down a movement key to traverse text, we shouldn't also traverse
// the view hierarchy. On the other hand, if the user is using the movement key to traverse
// views (i.e. the first movement was to traverse out of this view, or this view was traversed
// into by the user holding the movement key down) then we shouldn't prevent the focus from
// changing.
private boolean mPreventDefaultMovement;
private TextUtils.TruncateAt mEllipsize;
//静态内部类
static class Drawables {
static final int LEFT = 0;//左
static final int TOP = 1;//上
static final int RIGHT = 2;//右
static final int BOTTOM = 3;//下
static final int DRAWABLE_NONE = -1;
static final int DRAWABLE_RIGHT = 0;
static final int DRAWABLE_LEFT = 1;
final Rect mCompoundRect = new Rect();
final Drawable[] mShowing = new Drawable[4];
ColorStateList mTintList;
PorterDuff.Mode mTintMode;
boolean mHasTint;
boolean mHasTintMode;
Drawable mDrawableStart, mDrawableEnd, mDrawableError, mDrawableTemp;
Drawable mDrawableLeftInitial, mDrawableRightInitial;
boolean mIsRtlCompatibilityMode;
boolean mOverride;
int mDrawableSizeTop, mDrawableSizeBottom, mDrawableSizeLeft, mDrawableSizeRight,
mDrawableSizeStart, mDrawableSizeEnd, mDrawableSizeError, mDrawableSizeTemp;
int mDrawableWidthTop, mDrawableWidthBottom, mDrawableHeightLeft, mDrawableHeightRight,
mDrawableHeightStart, mDrawableHeightEnd, mDrawableHeightError, mDrawableHeightTemp;
int mDrawablePadding;
int mDrawableSaved = DRAWABLE_NONE;
public Drawables(Context context) {
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
mIsRtlCompatibilityMode = targetSdkVersion < VERSION_CODES.JELLY_BEAN_MR1
|| !context.getApplicationInfo().hasRtlSupport();
mOverride = false;
}
/**
* @return {@code true} if this object contains metadata that needs to
* be retained, {@code false} otherwise
*/
public boolean hasMetadata() {
return mDrawablePadding != 0 || mHasTintMode || mHasTint;
}
/**
* Updates the list of displayed drawables to account for the current
* layout direction.
*
* @param layoutDirection the current layout direction
* @return {@code true} if the displayed drawables changed
*/
public boolean resolveWithLayoutDirection(int layoutDirection) {
final Drawable previousLeft = mShowing[Drawables.LEFT];
final Drawable previousRight = mShowing[Drawables.RIGHT];
// First reset "left" and "right" drawables to their initial values
mShowing[Drawables.LEFT] = mDrawableLeftInitial;
mShowing[Drawables.RIGHT] = mDrawableRightInitial;
if (mIsRtlCompatibilityMode) {
// Use "start" drawable as "left" drawable if the "left" drawable was not defined
if (mDrawableStart != null && mShowing[Drawables.LEFT] == null) {
mShowing[Drawables.LEFT] = mDrawableStart;
mDrawableSizeLeft = mDrawableSizeStart;
mDrawableHeightLeft = mDrawableHeightStart;
}
// Use "end" drawable as "right" drawable if the "right" drawable was not defined
if (mDrawableEnd != null && mShowing[Drawables.RIGHT] == null) {
mShowing[Drawables.RIGHT] = mDrawableEnd;
mDrawableSizeRight = mDrawableSizeEnd;
mDrawableHeightRight = mDrawableHeightEnd;
}
} else {
// JB-MR1+ normal case: "start" / "end" drawables are overriding "left" / "right"
// drawable if and only if they have been defined
switch(layoutDirection) {
case LAYOUT_DIRECTION_RTL:
if (mOverride) {
mShowing[Drawables.RIGHT] = mDrawableStart;
mDrawableSizeRight = mDrawableSizeStart;
mDrawableHeightRight = mDrawableHeightStart;
mShowing[Drawables.LEFT] = mDrawableEnd;
mDrawableSizeLeft = mDrawableSizeEnd;
mDrawableHeightLeft = mDrawableHeightEnd;
}
break;
case LAYOUT_DIRECTION_LTR:
default:
if (mOverride) {
mShowing[Drawables.LEFT] = mDrawableStart;
mDrawableSizeLeft = mDrawableSizeStart;
mDrawableHeightLeft = mDrawableHeightStart;
mShowing[Drawables.RIGHT] = mDrawableEnd;
mDrawableSizeRight = mDrawableSizeEnd;
mDrawableHeightRight = mDrawableHeightEnd;
}
break;
}
}
applyErrorDrawableIfNeeded(layoutDirection);
return mShowing[Drawables.LEFT] != previousLeft
|| mShowing[Drawables.RIGHT] != previousRight;
}
public void setErrorDrawable(Drawable dr, TextView tv) {
if (mDrawableError != dr && mDrawableError != null) {
mDrawableError.setCallback(null);
}
mDrawableError = dr;
if (mDrawableError != null) {
final Rect compoundRect = mCompoundRect;
final int[] state = tv.getDrawableState();
mDrawableError.setState(state);
mDrawableError.copyBounds(compoundRect);
mDrawableError.setCallback(tv);
mDrawableSizeError = compoundRect.width();
mDrawableHeightError = compoundRect.height();
} else {
mDrawableSizeError = mDrawableHeightError = 0;
}
}
private void applyErrorDrawableIfNeeded(int layoutDirection) {
// first restore the initial state if needed
switch (mDrawableSaved) {
case DRAWABLE_LEFT:
mShowing[Drawables.LEFT] = mDrawableTemp;
mDrawableSizeLeft = mDrawableSizeTemp;
mDrawableHeightLeft = mDrawableHeightTemp;
break;
case DRAWABLE_RIGHT:
mShowing[Drawables.RIGHT] = mDrawableTemp;
mDrawableSizeRight = mDrawableSizeTemp;
mDrawableHeightRight = mDrawableHeightTemp;
break;
case DRAWABLE_NONE:
default:
}
// then, if needed, assign the Error drawable to the correct location
if (mDrawableError != null) {
switch(layoutDirection) {
case LAYOUT_DIRECTION_RTL:
mDrawableSaved = DRAWABLE_LEFT;
mDrawableTemp = mShowing[Drawables.LEFT];
mDrawableSizeTemp = mDrawableSizeLeft;
mDrawableHeightTemp = mDrawableHeightLeft;
mShowing[Drawables.LEFT] = mDrawableError;
mDrawableSizeLeft = mDrawableSizeError;
mDrawableHeightLeft = mDrawableHeightError;
break;
case LAYOUT_DIRECTION_LTR:
default:
mDrawableSaved = DRAWABLE_RIGHT;
mDrawableTemp = mShowing[Drawables.RIGHT];
mDrawableSizeTemp = mDrawableSizeRight;
mDrawableHeightTemp = mDrawableHeightRight;
mShowing[Drawables.RIGHT] = mDrawableError;
mDrawableSizeRight = mDrawableSizeError;
mDrawableHeightRight = mDrawableHeightError;
break;
}
}
}
}
Drawables mDrawables;
private CharWrapper mCharWrapper;
private Marquee mMarquee;
private boolean mRestartMarquee;
private int mMarqueeRepeatLimit = 3;
private int mLastLayoutDirection = -1;
/**
* On some devices the fading edges add a performance penalty if used
* extensively in the same layout. This mode indicates how the marquee
* is currently being shown, if applicable. (mEllipsize will == MARQUEE)
*/
private int mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
/**
* When mMarqueeFadeMode is not MARQUEE_FADE_NORMAL, this stores
* the layout that should be used when the mode switches.
*/
private Layout mSavedMarqueeModeLayout;
@ViewDebug.ExportedProperty(category = "text")
private CharSequence mText;
private CharSequence mTransformed;
private BufferType mBufferType = BufferType.NORMAL;
private CharSequence mHint;
private Layout mHintLayout;
private MovementMethod mMovement;
private TransformationMethod mTransformation;
private boolean mAllowTransformationLengthChange;
private ChangeWatcher mChangeWatcher;
private ArrayList<TextWatcher> mListeners;
// display attributes
private final TextPaint mTextPaint;
private boolean mUserSetTextScaleX;
private Layout mLayout;
private boolean mLocalesChanged = false;
// True if setKeyListener() has been explicitly called
private boolean mListenerChanged = false;
// True if internationalized input should be used for numbers and date and time.
private final boolean mUseInternationalizedInput;
@ViewDebug.ExportedProperty(category = "text")
private int mGravity = Gravity.TOP | Gravity.START;
private boolean mHorizontallyScrolling;
private int mAutoLinkMask;
private boolean mLinksClickable = true;
private float mSpacingMult = 1.0f;
private float mSpacingAdd = 0.0f;
private int mBreakStrategy;
private int mHyphenationFrequency;
private int mJustificationMode;
private int mMaximum = Integer.MAX_VALUE;
private int mMaxMode = LINES;
private int mMinimum = 0;
private int mMinMode = LINES;
private int mOldMaximum = mMaximum;
private int mOldMaxMode = mMaxMode;
private int mMaxWidth = Integer.MAX_VALUE;
private int mMaxWidthMode = PIXELS;
private int mMinWidth = 0;
private int mMinWidthMode = PIXELS;
private boolean mSingleLine;
private int mDesiredHeightAtMeasure = -1;
private boolean mIncludePad = true;
private int mDeferScroll = -1;
// tmp primitives, so we don't alloc them on each draw
private Rect mTempRect;
private long mLastScroll;
private Scroller mScroller;
private TextPaint mTempTextPaint;
private BoringLayout.Metrics mBoring, mHintBoring;
private BoringLayout mSavedLayout, mSavedHintLayout;
private TextDirectionHeuristic mTextDir;
private InputFilter[] mFilters = NO_FILTERS;
private volatile Locale mCurrentSpellCheckerLocaleCache;
// It is possible to have a selection even when mEditor is null (programmatically set, like when
// a link is pressed). These highlight-related fields do not go in mEditor.
int mHighlightColor = 0x6633B5E5;
private Path mHighlightPath;
private final Paint mHighlightPaint;
private boolean mHighlightPathBogus = true;
// Although these fields are specific to editable text, they are not added to Editor because
// they are defined by the TextView's style and are theme-dependent.
int mCursorDrawableRes;
// These six fields, could be moved to Editor, since we know their default values and we
// could condition the creation of the Editor to a non standard value. This is however
// brittle since the hardcoded values here (such as
// com.android.internal.R.drawable.text_select_handle_left) would have to be updated if the
// default style is modified.
int mTextSelectHandleLeftRes;
int mTextSelectHandleRightRes;
int mTextSelectHandleRes;
int mTextEditSuggestionItemLayout;
int mTextEditSuggestionContainerLayout;
int mTextEditSuggestionHighlightStyle;
/**
* {@link EditText} specific data, created on demand when one of the Editor fields is used.
* See {@link #createEditorIfNeeded()}.
*/
private Editor mEditor;
private static final int DEVICE_PROVISIONED_UNKNOWN = 0;
private static final int DEVICE_PROVISIONED_NO = 1;
private static final int DEVICE_PROVISIONED_YES = 2;
/**
* Some special options such as sharing selected text should only be shown if the device
* is provisioned. Only check the provisioned state once for a given view instance.
*/
private int mDeviceProvisionedState = DEVICE_PROVISIONED_UNKNOWN;
/**
* The TextView does not auto-size text (default).
*/
public static final int AUTO_SIZE_TEXT_TYPE_NONE = 0;
/**
* The TextView scales text size both horizontally and vertically to fit within the
* container.
*/
public static final int AUTO_SIZE_TEXT_TYPE_UNIFORM = 1;
/** @hide */
@IntDef({AUTO_SIZE_TEXT_TYPE_NONE, AUTO_SIZE_TEXT_TYPE_UNIFORM})
@Retention(RetentionPolicy.SOURCE)
public @interface AutoSizeTextType {}
// Default minimum size for auto-sizing text in scaled pixels.
private static final int DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP = 12;
// Default maximum size for auto-sizing text in scaled pixels.
private static final int DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP = 112;
// Default value for the step size in pixels.
private static final int DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX = 1;
// Use this to specify that any of the auto-size configuration int values have not been set.
private static final float UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE = -1f;
// Auto-size text type.
private int mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
// Specify if auto-size text is needed.
private boolean mNeedsAutoSizeText = false;
// Step size for auto-sizing in pixels.
private float mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
// Minimum text size for auto-sizing in pixels.
private float mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
// Maximum text size for auto-sizing in pixels.
private float mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
// Contains a (specified or computed) distinct sorted set of text sizes in pixels to pick from
// when auto-sizing text.
private int[] mAutoSizeTextSizesInPx = EmptyArray.INT;
// Specifies whether auto-size should use the provided auto size steps set or if it should
// build the steps set using mAutoSizeMinTextSizeInPx, mAutoSizeMaxTextSizeInPx and
// mAutoSizeStepGranularityInPx.
private boolean mHasPresetAutoSizeValues = false;
// Indicates whether the text was set from resources or dynamically, so it can be used to
// sanitize autofill requests.
private boolean mTextFromResource = false;
/**
* Kick-start the font cache for the zygote process (to pay the cost of
* initializing freetype for our default font only once).
* @hide
*/
public static void preloadFontCache() {
Paint p = new Paint();
p.setAntiAlias(true);
// Ensure that the Typeface is loaded here.
// Typically, Typeface is preloaded by zygote but not on all devices, e.g. Android Auto.
// So, sets Typeface.DEFAULT explicitly here for ensuring that the Typeface is loaded here
// since Paint.measureText can not be called without Typeface static initializer.
p.setTypeface(Typeface.DEFAULT);
// We don't care about the result, just the side-effect of measuring.
p.measureText("H");
}
/**
* Interface definition for a callback to be invoked when an action is
* performed on the editor.
*/
public interface OnEditorActionListener {
/**
* Called when an action is being performed.
*
* @param v The view that was clicked.
* @param actionId Identifier of the action. This will be either the
* identifier you supplied, or {@link EditorInfo#IME_NULL
* EditorInfo.IME_NULL} if being called due to the enter key
* being pressed.
* @param event If triggered by an enter key, this is the event;
* otherwise, this is null.
* @return Return true if you have consumed the action, else false.
*/
boolean onEditorAction(TextView v, int actionId, KeyEvent event);
}
//这里是方法重载(方法名相同,参数个数或参数类型不同)
public TextView(Context context) {
this(context, null);
}
public TextView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, com.android.internal.R.attr.textViewStyle);
}
public TextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
@SuppressWarnings("deprecation")
public TextView(
Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
// TextView is important by default, unless app developer overrode attribute.
if (getImportantForAutofill() == IMPORTANT_FOR_AUTOFILL_AUTO) {
setImportantForAutofill(IMPORTANT_FOR_AUTOFILL_YES);
}
mText = "";
final Resources res = getResources();
final CompatibilityInfo compat = res.getCompatibilityInfo();
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.density = res.getDisplayMetrics().density;
mTextPaint.setCompatibilityScaling(compat.applicationScale);
mHighlightPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHighlightPaint.setCompatibilityScaling(compat.applicationScale);
mMovement = getDefaultMovementMethod();
mTransformation = null;
int textColorHighlight = 0;
ColorStateList textColor = null;
ColorStateList textColorHint = null;
ColorStateList textColorLink = null;
int textSize = 15;
String fontFamily = null;
Typeface fontTypeface = null;
boolean fontFamilyExplicit = false;
int typefaceIndex = -1;
int styleIndex = -1;
boolean allCaps = false;
int shadowcolor = 0;
float dx = 0, dy = 0, r = 0;
boolean elegant = false;
float letterSpacing = 0;
String fontFeatureSettings = null;
mBreakStrategy = Layout.BREAK_STRATEGY_SIMPLE;
mHyphenationFrequency = Layout.HYPHENATION_FREQUENCY_NONE;
mJustificationMode = Layout.JUSTIFICATION_MODE_NONE;
final Resources.Theme theme = context.getTheme();
/*
* Look the appearance up without checking first if it exists because
* almost every TextView has one and it greatly simplifies the logic
* to be able to parse the appearance first and then let specific tags
* for this View override it.
*/
TypedArray a = theme.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.TextViewAppearance, defStyleAttr, defStyleRes);
TypedArray appearance = null;
int ap = a.getResourceId(
com.android.internal.R.styleable.TextViewAppearance_textAppearance, -1);
a.recycle();
if (ap != -1) {
appearance = theme.obtainStyledAttributes(
ap, com.android.internal.R.styleable.TextAppearance);
}
if (appearance != null) {
int n = appearance.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = appearance.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.TextAppearance_textColorHighlight:
textColorHighlight = appearance.getColor(attr, textColorHighlight);
break;
case com.android.internal.R.styleable.TextAppearance_textColor:
textColor = appearance.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextAppearance_textColorHint:
textColorHint = appearance.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextAppearance_textColorLink:
textColorLink = appearance.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextAppearance_textSize:
textSize = appearance.getDimensionPixelSize(attr, textSize);
break;
case com.android.internal.R.styleable.TextAppearance_typeface:
typefaceIndex = appearance.getInt(attr, -1);
break;
case com.android.internal.R.styleable.TextAppearance_fontFamily:
if (!context.isRestricted() && context.canLoadUnsafeResources()) {
try {
fontTypeface = appearance.getFont(attr);
} catch (UnsupportedOperationException
| Resources.NotFoundException e) {
// Expected if it is not a font resource.
}
}
if (fontTypeface == null) {
fontFamily = appearance.getString(attr);
}
break;
case com.android.internal.R.styleable.TextAppearance_textStyle:
styleIndex = appearance.getInt(attr, -1);
break;
case com.android.internal.R.styleable.TextAppearance_textAllCaps:
allCaps = appearance.getBoolean(attr, false);
break;
case com.android.internal.R.styleable.TextAppearance_shadowColor:
shadowcolor = appearance.getInt(attr, 0);
break;
case com.android.internal.R.styleable.TextAppearance_shadowDx:
dx = appearance.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextAppearance_shadowDy:
dy = appearance.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextAppearance_shadowRadius:
r = appearance.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextAppearance_elegantTextHeight:
elegant = appearance.getBoolean(attr, false);
break;
case com.android.internal.R.styleable.TextAppearance_letterSpacing:
letterSpacing = appearance.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextAppearance_fontFeatureSettings:
fontFeatureSettings = appearance.getString(attr);
break;
}
}
appearance.recycle();
}
boolean editable = getDefaultEditable();
CharSequence inputMethod = null;
int numeric = 0;
CharSequence digits = null;
boolean phone = false;
boolean autotext = false;
int autocap = -1;
int buffertype = 0;
boolean selectallonfocus = false;
Drawable drawableLeft = null, drawableTop = null, drawableRight = null,
drawableBottom = null, drawableStart = null, drawableEnd = null;
ColorStateList drawableTint = null;
PorterDuff.Mode drawableTintMode = null;
int drawablePadding = 0;
int ellipsize = -1;
boolean singleLine = false;
int maxlength = -1;
CharSequence text = "";
CharSequence hint = null;
boolean password = false;
float autoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
float autoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
float autoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
int inputType = EditorInfo.TYPE_NULL;
a = theme.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.TextView, defStyleAttr, defStyleRes);
int n = a.getIndexCount();
boolean fromResourceId = false;
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.TextView_editable:
editable = a.getBoolean(attr, editable);
break;
case com.android.internal.R.styleable.TextView_inputMethod:
inputMethod = a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_numeric:
numeric = a.getInt(attr, numeric);
break;
case com.android.internal.R.styleable.TextView_digits:
digits = a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_phoneNumber:
phone = a.getBoolean(attr, phone);
break;
case com.android.internal.R.styleable.TextView_autoText:
autotext = a.getBoolean(attr, autotext);
break;
case com.android.internal.R.styleable.TextView_capitalize:
autocap = a.getInt(attr, autocap);
break;
case com.android.internal.R.styleable.TextView_bufferType:
buffertype = a.getInt(attr, buffertype);
break;
case com.android.internal.R.styleable.TextView_selectAllOnFocus:
selectallonfocus = a.getBoolean(attr, selectallonfocus);
break;
case com.android.internal.R.styleable.TextView_autoLink:
mAutoLinkMask = a.getInt(attr, 0);
break;
case com.android.internal.R.styleable.TextView_linksClickable:
mLinksClickable = a.getBoolean(attr, true);
break;
case com.android.internal.R.styleable.TextView_drawableLeft:
drawableLeft = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.TextView_drawableTop:
drawableTop = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.TextView_drawableRight:
drawableRight = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.TextView_drawableBottom:
drawableBottom = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.TextView_drawableStart:
drawableStart = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.TextView_drawableEnd:
drawableEnd = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.TextView_drawableTint:
drawableTint = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_drawableTintMode:
drawableTintMode = Drawable.parseTintMode(a.getInt(attr, -1), drawableTintMode);
break;
case com.android.internal.R.styleable.TextView_drawablePadding:
drawablePadding = a.getDimensionPixelSize(attr, drawablePadding);
break;
case com.android.internal.R.styleable.TextView_maxLines:
setMaxLines(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_maxHeight:
setMaxHeight(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_lines:
setLines(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_height:
setHeight(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_minLines:
setMinLines(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_minHeight:
setMinHeight(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_maxEms:
setMaxEms(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_maxWidth:
setMaxWidth(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_ems:
setEms(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_width:
setWidth(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_minEms:
setMinEms(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_minWidth:
setMinWidth(a.getDimensionPixelSize(attr, -1));
break;
case com.android.internal.R.styleable.TextView_gravity:
setGravity(a.getInt(attr, -1));
break;
case com.android.internal.R.styleable.TextView_hint:
hint = a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_text:
fromResourceId = true;
text = a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_scrollHorizontally:
if (a.getBoolean(attr, false)) {
setHorizontallyScrolling(true);
}
break;
case com.android.internal.R.styleable.TextView_singleLine:
singleLine = a.getBoolean(attr, singleLine);
break;
case com.android.internal.R.styleable.TextView_ellipsize:
ellipsize = a.getInt(attr, ellipsize);
break;
case com.android.internal.R.styleable.TextView_marqueeRepeatLimit:
setMarqueeRepeatLimit(a.getInt(attr, mMarqueeRepeatLimit));
break;
case com.android.internal.R.styleable.TextView_includeFontPadding:
if (!a.getBoolean(attr, true)) {
setIncludeFontPadding(false);
}
break;
case com.android.internal.R.styleable.TextView_cursorVisible:
if (!a.getBoolean(attr, true)) {
setCursorVisible(false);
}
break;
case com.android.internal.R.styleable.TextView_maxLength:
maxlength = a.getInt(attr, -1);
break;
case com.android.internal.R.styleable.TextView_textScaleX:
setTextScaleX(a.getFloat(attr, 1.0f));
break;
case com.android.internal.R.styleable.TextView_freezesText:
mFreezesText = a.getBoolean(attr, false);
break;
case com.android.internal.R.styleable.TextView_shadowColor:
shadowcolor = a.getInt(attr, 0);
break;
case com.android.internal.R.styleable.TextView_shadowDx:
dx = a.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextView_shadowDy:
dy = a.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextView_shadowRadius:
r = a.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextView_enabled:
setEnabled(a.getBoolean(attr, isEnabled()));
break;
case com.android.internal.R.styleable.TextView_textColorHighlight:
textColorHighlight = a.getColor(attr, textColorHighlight);
break;
case com.android.internal.R.styleable.TextView_textColor:
textColor = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_textColorHint:
textColorHint = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_textColorLink:
textColorLink = a.getColorStateList(attr);
break;
case com.android.internal.R.styleable.TextView_textSize:
textSize = a.getDimensionPixelSize(attr, textSize);
break;
case com.android.internal.R.styleable.TextView_typeface:
typefaceIndex = a.getInt(attr, typefaceIndex);
break;
case com.android.internal.R.styleable.TextView_textStyle:
styleIndex = a.getInt(attr, styleIndex);
break;
case com.android.internal.R.styleable.TextView_fontFamily:
if (!context.isRestricted() && context.canLoadUnsafeResources()) {
try {
fontTypeface = a.getFont(attr);
} catch (UnsupportedOperationException | Resources.NotFoundException e) {
// Expected if it is not a resource reference or if it is a reference to
// another resource type.
}
}
if (fontTypeface == null) {
fontFamily = a.getString(attr);
}
fontFamilyExplicit = true;
break;
case com.android.internal.R.styleable.TextView_password:
password = a.getBoolean(attr, password);
break;
case com.android.internal.R.styleable.TextView_lineSpacingExtra:
mSpacingAdd = a.getDimensionPixelSize(attr, (int) mSpacingAdd);
break;
case com.android.internal.R.styleable.TextView_lineSpacingMultiplier:
mSpacingMult = a.getFloat(attr, mSpacingMult);
break;
case com.android.internal.R.styleable.TextView_inputType:
inputType = a.getInt(attr, EditorInfo.TYPE_NULL);
break;
case com.android.internal.R.styleable.TextView_allowUndo:
createEditorIfNeeded();
mEditor.mAllowUndo = a.getBoolean(attr, true);
break;
case com.android.internal.R.styleable.TextView_imeOptions:
createEditorIfNeeded();
mEditor.createInputContentTypeIfNeeded();
mEditor.mInputContentType.imeOptions = a.getInt(attr,
mEditor.mInputContentType.imeOptions);
break;
case com.android.internal.R.styleable.TextView_imeActionLabel:
createEditorIfNeeded();
mEditor.createInputContentTypeIfNeeded();
mEditor.mInputContentType.imeActionLabel = a.getText(attr);
break;
case com.android.internal.R.styleable.TextView_imeActionId:
createEditorIfNeeded();
mEditor.createInputContentTypeIfNeeded();
mEditor.mInputContentType.imeActionId = a.getInt(attr,
mEditor.mInputContentType.imeActionId);
break;
case com.android.internal.R.styleable.TextView_privateImeOptions:
setPrivateImeOptions(a.getString(attr));
break;
case com.android.internal.R.styleable.TextView_editorExtras:
try {
setInputExtras(a.getResourceId(attr, 0));
} catch (XmlPullParserException e) {
Log.w(LOG_TAG, "Failure reading input extras", e);
} catch (IOException e) {
Log.w(LOG_TAG, "Failure reading input extras", e);
}
break;
case com.android.internal.R.styleable.TextView_textCursorDrawable:
mCursorDrawableRes = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textSelectHandleLeft:
mTextSelectHandleLeftRes = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textSelectHandleRight:
mTextSelectHandleRightRes = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textSelectHandle:
mTextSelectHandleRes = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textEditSuggestionItemLayout:
mTextEditSuggestionItemLayout = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textEditSuggestionContainerLayout:
mTextEditSuggestionContainerLayout = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textEditSuggestionHighlightStyle:
mTextEditSuggestionHighlightStyle = a.getResourceId(attr, 0);
break;
case com.android.internal.R.styleable.TextView_textIsSelectable:
setTextIsSelectable(a.getBoolean(attr, false));
break;
case com.android.internal.R.styleable.TextView_textAllCaps:
allCaps = a.getBoolean(attr, false);
break;
case com.android.internal.R.styleable.TextView_elegantTextHeight:
elegant = a.getBoolean(attr, false);
break;
case com.android.internal.R.styleable.TextView_letterSpacing:
letterSpacing = a.getFloat(attr, 0);
break;
case com.android.internal.R.styleable.TextView_fontFeatureSettings:
fontFeatureSettings = a.getString(attr);
break;
case com.android.internal.R.styleable.TextView_breakStrategy:
mBreakStrategy = a.getInt(attr, Layout.BREAK_STRATEGY_SIMPLE);
break;
case com.android.internal.R.styleable.TextView_hyphenationFrequency:
mHyphenationFrequency = a.getInt(attr, Layout.HYPHENATION_FREQUENCY_NONE);
break;
case com.android.internal.R.styleable.TextView_autoSizeTextType:
mAutoSizeTextType = a.getInt(attr, AUTO_SIZE_TEXT_TYPE_NONE);
break;
case com.android.internal.R.styleable.TextView_autoSizeStepGranularity:
autoSizeStepGranularityInPx = a.getDimension(attr,
UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
break;
case com.android.internal.R.styleable.TextView_autoSizeMinTextSize:
autoSizeMinTextSizeInPx = a.getDimension(attr,
UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
break;
case com.android.internal.R.styleable.TextView_autoSizeMaxTextSize:
autoSizeMaxTextSizeInPx = a.getDimension(attr,
UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE);
break;
case com.android.internal.R.styleable.TextView_autoSizePresetSizes:
final int autoSizeStepSizeArrayResId = a.getResourceId(attr, 0);
if (autoSizeStepSizeArrayResId > 0) {
final TypedArray autoSizePresetTextSizes = a.getResources()
.obtainTypedArray(autoSizeStepSizeArrayResId);
setupAutoSizeUniformPresetSizes(autoSizePresetTextSizes);
autoSizePresetTextSizes.recycle();
}
break;
case com.android.internal.R.styleable.TextView_justificationMode:
mJustificationMode = a.getInt(attr, Layout.JUSTIFICATION_MODE_NONE);
break;
}
}
a.recycle();
BufferType bufferType = BufferType.EDITABLE;
final int variation =
inputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION);
final boolean passwordInputType = variation
== (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
final boolean webPasswordInputType = variation
== (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_WEB_PASSWORD);
final boolean numberPasswordInputType = variation
== (EditorInfo.TYPE_CLASS_NUMBER | EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD);
mUseInternationalizedInput =
context.getApplicationInfo().targetSdkVersion >= VERSION_CODES.O;
if (inputMethod != null) {
Class<?> c;
try {
c = Class.forName(inputMethod.toString());
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
}
try {
createEditorIfNeeded();
mEditor.mKeyListener = (KeyListener) c.newInstance();
} catch (InstantiationException ex) {
throw new RuntimeException(ex);
} catch (IllegalAccessException ex) {
throw new RuntimeException(ex);
}
try {
mEditor.mInputType = inputType != EditorInfo.TYPE_NULL
? inputType
: mEditor.mKeyListener.getInputType();
} catch (IncompatibleClassChangeError e) {
mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
}
} else if (digits != null) {
createEditorIfNeeded();
mEditor.mKeyListener = DigitsKeyListener.getInstance(digits.toString());
// If no input type was specified, we will default to generic
// text, since we can't tell the IME about the set of digits
// that was selected.
mEditor.mInputType = inputType != EditorInfo.TYPE_NULL
? inputType : EditorInfo.TYPE_CLASS_TEXT;
} else if (inputType != EditorInfo.TYPE_NULL) {
setInputType(inputType, true);
// If set, the input type overrides what was set using the deprecated singleLine flag.
singleLine = !isMultilineInputType(inputType);
} else if (phone) {
createEditorIfNeeded();
mEditor.mKeyListener = DialerKeyListener.getInstance();
mEditor.mInputType = inputType = EditorInfo.TYPE_CLASS_PHONE;
} else if (numeric != 0) {
createEditorIfNeeded();
mEditor.mKeyListener = DigitsKeyListener.getInstance(
null, // locale
(numeric & SIGNED) != 0,
(numeric & DECIMAL) != 0);
inputType = mEditor.mKeyListener.getInputType();
mEditor.mInputType = inputType;
} else if (autotext || autocap != -1) {
TextKeyListener.Capitalize cap;
inputType = EditorInfo.TYPE_CLASS_TEXT;
switch (autocap) {
case 1:
cap = TextKeyListener.Capitalize.SENTENCES;
inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_SENTENCES;
break;
case 2:
cap = TextKeyListener.Capitalize.WORDS;
inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_WORDS;
break;
case 3:
cap = TextKeyListener.Capitalize.CHARACTERS;
inputType |= EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS;
break;
default:
cap = TextKeyListener.Capitalize.NONE;
break;
}
createEditorIfNeeded();
mEditor.mKeyListener = TextKeyListener.getInstance(autotext, cap);
mEditor.mInputType = inputType;
} else if (editable) {
createEditorIfNeeded();
mEditor.mKeyListener = TextKeyListener.getInstance();
mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
} else if (isTextSelectable()) {
// Prevent text changes from keyboard.
if (mEditor != null) {
mEditor.mKeyListener = null;
mEditor.mInputType = EditorInfo.TYPE_NULL;
}
bufferType = BufferType.SPANNABLE;
// So that selection can be changed using arrow keys and touch is handled.
setMovementMethod(ArrowKeyMovementMethod.getInstance());
} else {
if (mEditor != null) mEditor.mKeyListener = null;
switch (buffertype) {
case 0:
bufferType = BufferType.NORMAL;
break;
case 1:
bufferType = BufferType.SPANNABLE;
break;
case 2:
bufferType = BufferType.EDITABLE;
break;
}
}
if (mEditor != null) {
mEditor.adjustInputType(password, passwordInputType, webPasswordInputType,
numberPasswordInputType);
}
if (selectallonfocus) {
createEditorIfNeeded();
mEditor.mSelectAllOnFocus = true;
if (bufferType == BufferType.NORMAL) {
bufferType = BufferType.SPANNABLE;
}
}
// Set up the tint (if needed) before setting the drawables so that it
// gets applied correctly.
if (drawableTint != null || drawableTintMode != null) {
if (mDrawables == null) {
mDrawables = new Drawables(context);
}
if (drawableTint != null) {
mDrawables.mTintList = drawableTint;
mDrawables.mHasTint = true;
}
if (drawableTintMode != null) {
mDrawables.mTintMode = drawableTintMode;
mDrawables.mHasTintMode = true;
}
}
// This call will save the initial left/right drawables
setCompoundDrawablesWithIntrinsicBounds(
drawableLeft, drawableTop, drawableRight, drawableBottom);
setRelativeDrawablesIfNeeded(drawableStart, drawableEnd);
setCompoundDrawablePadding(drawablePadding);
// Same as setSingleLine(), but make sure the transformation method and the maximum number
// of lines of height are unchanged for multi-line TextViews.
setInputTypeSingleLine(singleLine);
applySingleLine(singleLine, singleLine, singleLine);
if (singleLine && getKeyListener() == null && ellipsize < 0) {
ellipsize = 3; // END
}
switch (ellipsize) {
case 1:
setEllipsize(TextUtils.TruncateAt.START);
break;
case 2:
setEllipsize(TextUtils.TruncateAt.MIDDLE);
break;
case 3:
setEllipsize(TextUtils.TruncateAt.END);
break;
case 4:
if (ViewConfiguration.get(context).isFadingMarqueeEnabled()) {
setHorizontalFadingEdgeEnabled(true);
mMarqueeFadeMode = MARQUEE_FADE_NORMAL;
} else {
setHorizontalFadingEdgeEnabled(false);
mMarqueeFadeMode = MARQUEE_FADE_SWITCH_SHOW_ELLIPSIS;
}
setEllipsize(TextUtils.TruncateAt.MARQUEE);
break;
}
setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
setHintTextColor(textColorHint);
setLinkTextColor(textColorLink);
if (textColorHighlight != 0) {
setHighlightColor(textColorHighlight);
}
setRawTextSize(textSize, true /* shouldRequestLayout */);
setElegantTextHeight(elegant);
setLetterSpacing(letterSpacing);
setFontFeatureSettings(fontFeatureSettings);
if (allCaps) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
}
if (password || passwordInputType || webPasswordInputType || numberPasswordInputType) {
setTransformationMethod(PasswordTransformationMethod.getInstance());
typefaceIndex = MONOSPACE;
} else if (mEditor != null
&& (mEditor.mInputType
& (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION))
== (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD)) {
typefaceIndex = MONOSPACE;
}
if (typefaceIndex != -1 && !fontFamilyExplicit) {
fontFamily = null;
}
setTypefaceFromAttrs(fontTypeface, fontFamily, typefaceIndex, styleIndex);
if (shadowcolor != 0) {
setShadowLayer(r, dx, dy, shadowcolor);
}
if (maxlength >= 0) {
setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
} else {
setFilters(NO_FILTERS);
}
setText(text, bufferType);
if (fromResourceId) {
mTextFromResource = true;
}
if (hint != null) setHint(hint);
/*
* Views are not normally clickable unless specified to be.
* However, TextViews that have input or movement methods *are*
* clickable by default. By setting clickable here, we implicitly set focusable as well
* if not overridden by the developer.
*/
a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
boolean canInputOrMove = (mMovement != null || getKeyListener() != null);
boolean clickable = canInputOrMove || isClickable();
boolean longClickable = canInputOrMove || isLongClickable();
int focusable = getFocusable();
n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_focusable:
TypedValue val = new TypedValue();
if (a.getValue(attr, val)) {
focusable = (val.type == TypedValue.TYPE_INT_BOOLEAN)
? (val.data == 0 ? NOT_FOCUSABLE : FOCUSABLE)
: val.data;
}
break;
case com.android.internal.R.styleable.View_clickable:
clickable = a.getBoolean(attr, clickable);
break;
case com.android.internal.R.styleable.View_longClickable:
longClickable = a.getBoolean(attr, longClickable);
break;
}
}
a.recycle();
// Some apps were relying on the undefined behavior of focusable winning over
// focusableInTouchMode != focusable in TextViews if both were specified in XML (usually
// when starting with EditText and setting only focusable=false). To keep those apps from
// breaking, re-apply the focusable attribute here.
if (focusable != getFocusable()) {
setFocusable(focusable);
}
setClickable(clickable);
setLongClickable(longClickable);
if (mEditor != null) mEditor.prepareCursorControllers();
// If not explicitly specified this view is important for accessibility.
if (getImportantForAccessibility() == IMPORTANT_FOR_ACCESSIBILITY_AUTO) {
setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
}
if (supportsAutoSizeText()) {
if (mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_UNIFORM) {
// If uniform auto-size has been specified but preset values have not been set then
// replace the auto-size configuration values that have not been specified with the
// defaults.
if (!mHasPresetAutoSizeValues) {
final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
if (autoSizeMinTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
autoSizeMinTextSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
displayMetrics);
}
if (autoSizeMaxTextSizeInPx == UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
displayMetrics);
}
if (autoSizeStepGranularityInPx
== UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE) {
autoSizeStepGranularityInPx = DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX;
}
validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
autoSizeMaxTextSizeInPx,
autoSizeStepGranularityInPx);
}
setupAutoSizeText();
}
} else {
mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
}
}
/**
* Specify whether this widget should automatically scale the text to try to perfectly fit
* within the layout bounds by using the default auto-size configuration.
*
* @param autoSizeTextType the type of auto-size. Must be one of
* {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
* {@link TextView#AUTO_SIZE_TEXT_TYPE_UNIFORM}
*
* @throws IllegalArgumentException if <code>autoSizeTextType</code> is none of the types above.
*
* @attr ref android.R.styleable#TextView_autoSizeTextType
*
* @see #getAutoSizeTextType()
*/
public void setAutoSizeTextTypeWithDefaults(@AutoSizeTextType int autoSizeTextType) {
if (supportsAutoSizeText()) {
switch (autoSizeTextType) {
case AUTO_SIZE_TEXT_TYPE_NONE:
clearAutoSizeConfiguration();
break;
case AUTO_SIZE_TEXT_TYPE_UNIFORM:
final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
final float autoSizeMinTextSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
DEFAULT_AUTO_SIZE_MIN_TEXT_SIZE_IN_SP,
displayMetrics);
final float autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_SP,
DEFAULT_AUTO_SIZE_MAX_TEXT_SIZE_IN_SP,
displayMetrics);
validateAndSetAutoSizeTextTypeUniformConfiguration(
autoSizeMinTextSizeInPx,
autoSizeMaxTextSizeInPx,
DEFAULT_AUTO_SIZE_GRANULARITY_IN_PX);
if (setupAutoSizeText()) {
autoSizeText();
invalidate();
}
break;
default:
throw new IllegalArgumentException(
"Unknown auto-size text type: " + autoSizeTextType);
}
}
}
/**
* Specify whether this widget should automatically scale the text to try to perfectly fit
* within the layout bounds. If all the configuration params are valid the type of auto-size is
* set to {@link #AUTO_SIZE_TEXT_TYPE_UNIFORM}.
*
* @param autoSizeMinTextSize the minimum text size available for auto-size
* @param autoSizeMaxTextSize the maximum text size available for auto-size
* @param autoSizeStepGranularity the auto-size step granularity. It is used in conjunction with
* the minimum and maximum text size in order to build the set of
* text sizes the system uses to choose from when auto-sizing
* @param unit the desired dimension unit for all sizes above. See {@link TypedValue} for the
* possible dimension units
*
* @throws IllegalArgumentException if any of the configuration params are invalid.
*
* @attr ref android.R.styleable#TextView_autoSizeTextType
* @attr ref android.R.styleable#TextView_autoSizeMinTextSize
* @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
* @attr ref android.R.styleable#TextView_autoSizeStepGranularity
*
* @see #setAutoSizeTextTypeWithDefaults(int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
* @see #getAutoSizeMinTextSize()
* @see #getAutoSizeMaxTextSize()
* @see #getAutoSizeStepGranularity()
* @see #getAutoSizeTextAvailableSizes()
*/
public void setAutoSizeTextTypeUniformWithConfiguration(int autoSizeMinTextSize,
int autoSizeMaxTextSize, int autoSizeStepGranularity, int unit) {
if (supportsAutoSizeText()) {
final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
final float autoSizeMinTextSizeInPx = TypedValue.applyDimension(
unit, autoSizeMinTextSize, displayMetrics);
final float autoSizeMaxTextSizeInPx = TypedValue.applyDimension(
unit, autoSizeMaxTextSize, displayMetrics);
final float autoSizeStepGranularityInPx = TypedValue.applyDimension(
unit, autoSizeStepGranularity, displayMetrics);
validateAndSetAutoSizeTextTypeUniformConfiguration(autoSizeMinTextSizeInPx,
autoSizeMaxTextSizeInPx,
autoSizeStepGranularityInPx);
if (setupAutoSizeText()) {
autoSizeText();
invalidate();
}
}
}
/**
* Specify whether this widget should automatically scale the text to try to perfectly fit
* within the layout bounds. If at least one value from the <code>presetSizes</code> is valid
* then the type of auto-size is set to {@link #AUTO_SIZE_TEXT_TYPE_UNIFORM}.
*
* @param presetSizes an {@code int} array of sizes in pixels
* @param unit the desired dimension unit for the preset sizes above. See {@link TypedValue} for
* the possible dimension units
*
* @throws IllegalArgumentException if all of the <code>presetSizes</code> are invalid.
*
* @attr ref android.R.styleable#TextView_autoSizeTextType
* @attr ref android.R.styleable#TextView_autoSizePresetSizes
*
* @see #setAutoSizeTextTypeWithDefaults(int)
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #getAutoSizeMinTextSize()
* @see #getAutoSizeMaxTextSize()
* @see #getAutoSizeTextAvailableSizes()
*/
public void setAutoSizeTextTypeUniformWithPresetSizes(@NonNull int[] presetSizes, int unit) {
if (supportsAutoSizeText()) {
final int presetSizesLength = presetSizes.length;
if (presetSizesLength > 0) {
int[] presetSizesInPx = new int[presetSizesLength];
if (unit == TypedValue.COMPLEX_UNIT_PX) {
presetSizesInPx = Arrays.copyOf(presetSizes, presetSizesLength);
} else {
final DisplayMetrics displayMetrics = getResources().getDisplayMetrics();
// Convert all to sizes to pixels.
for (int i = 0; i < presetSizesLength; i++) {
presetSizesInPx[i] = Math.round(TypedValue.applyDimension(unit,
presetSizes[i], displayMetrics));
}
}
mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(presetSizesInPx);
if (!setupAutoSizeUniformPresetSizesConfiguration()) {
throw new IllegalArgumentException("None of the preset sizes is valid: "
+ Arrays.toString(presetSizes));
}
} else {
mHasPresetAutoSizeValues = false;
}
if (setupAutoSizeText()) {
autoSizeText();
invalidate();
}
}
}
/**
* Returns the type of auto-size set for this widget.
*
* @return an {@code int} corresponding to one of the auto-size types:
* {@link TextView#AUTO_SIZE_TEXT_TYPE_NONE} or
* {@link TextView#AUTO_SIZE_TEXT_TYPE_UNIFORM}
*
* @attr ref android.R.styleable#TextView_autoSizeTextType
*
* @see #setAutoSizeTextTypeWithDefaults(int)
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
@AutoSizeTextType
public int getAutoSizeTextType() {
return mAutoSizeTextType;
}
/**
* @return the current auto-size step granularity in pixels.
*
* @attr ref android.R.styleable#TextView_autoSizeStepGranularity
*
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
*/
public int getAutoSizeStepGranularity() {
return Math.round(mAutoSizeStepGranularityInPx);
}
/**
* @return the current auto-size minimum text size in pixels (the default is 12sp). Note that
* if auto-size has not been configured this function returns {@code -1}.
*
* @attr ref android.R.styleable#TextView_autoSizeMinTextSize
*
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
public int getAutoSizeMinTextSize() {
return Math.round(mAutoSizeMinTextSizeInPx);
}
/**
* @return the current auto-size maximum text size in pixels (the default is 112sp). Note that
* if auto-size has not been configured this function returns {@code -1}.
*
* @attr ref android.R.styleable#TextView_autoSizeMaxTextSize
*
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
public int getAutoSizeMaxTextSize() {
return Math.round(mAutoSizeMaxTextSizeInPx);
}
/**
* @return the current auto-size {@code int} sizes array (in pixels).
*
* @see #setAutoSizeTextTypeUniformWithConfiguration(int, int, int, int)
* @see #setAutoSizeTextTypeUniformWithPresetSizes(int[], int)
*/
public int[] getAutoSizeTextAvailableSizes() {
return mAutoSizeTextSizesInPx;
}
private void setupAutoSizeUniformPresetSizes(TypedArray textSizes) {
final int textSizesLength = textSizes.length();
final int[] parsedSizes = new int[textSizesLength];
if (textSizesLength > 0) {
for (int i = 0; i < textSizesLength; i++) {
parsedSizes[i] = textSizes.getDimensionPixelSize(i, -1);
}
mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(parsedSizes);
setupAutoSizeUniformPresetSizesConfiguration();
}
}
private boolean setupAutoSizeUniformPresetSizesConfiguration() {
final int sizesLength = mAutoSizeTextSizesInPx.length;
mHasPresetAutoSizeValues = sizesLength > 0;
if (mHasPresetAutoSizeValues) {
mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
mAutoSizeMinTextSizeInPx = mAutoSizeTextSizesInPx[0];
mAutoSizeMaxTextSizeInPx = mAutoSizeTextSizesInPx[sizesLength - 1];
mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
}
return mHasPresetAutoSizeValues;
}
/**
* If all params are valid then save the auto-size configuration.
*
* @throws IllegalArgumentException if any of the params are invalid
*/
private void validateAndSetAutoSizeTextTypeUniformConfiguration(float autoSizeMinTextSizeInPx,
float autoSizeMaxTextSizeInPx, float autoSizeStepGranularityInPx) {
// First validate.
if (autoSizeMinTextSizeInPx <= 0) {
throw new IllegalArgumentException("Minimum auto-size text size ("
+ autoSizeMinTextSizeInPx + "px) is less or equal to (0px)");
}
if (autoSizeMaxTextSizeInPx <= autoSizeMinTextSizeInPx) {
throw new IllegalArgumentException("Maximum auto-size text size ("
+ autoSizeMaxTextSizeInPx + "px) is less or equal to minimum auto-size "
+ "text size (" + autoSizeMinTextSizeInPx + "px)");
}
if (autoSizeStepGranularityInPx <= 0) {
throw new IllegalArgumentException("The auto-size step granularity ("
+ autoSizeStepGranularityInPx + "px) is less or equal to (0px)");
}
// All good, persist the configuration.
mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_UNIFORM;
mAutoSizeMinTextSizeInPx = autoSizeMinTextSizeInPx;
mAutoSizeMaxTextSizeInPx = autoSizeMaxTextSizeInPx;
mAutoSizeStepGranularityInPx = autoSizeStepGranularityInPx;
mHasPresetAutoSizeValues = false;
}
private void clearAutoSizeConfiguration() {
mAutoSizeTextType = AUTO_SIZE_TEXT_TYPE_NONE;
mAutoSizeMinTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
mAutoSizeMaxTextSizeInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
mAutoSizeStepGranularityInPx = UNSET_AUTO_SIZE_UNIFORM_CONFIGURATION_VALUE;
mAutoSizeTextSizesInPx = EmptyArray.INT;
mNeedsAutoSizeText = false;
}
// Returns distinct sorted positive values.
private int[] cleanupAutoSizePresetSizes(int[] presetValues) {
final int presetValuesLength = presetValues.length;
if (presetValuesLength == 0) {
return presetValues;
}
Arrays.sort(presetValues);
final IntArray uniqueValidSizes = new IntArray();
for (int i = 0; i < presetValuesLength; i++) {
final int currentPresetValue = presetValues[i];
if (currentPresetValue > 0
&& uniqueValidSizes.binarySearch(currentPresetValue) < 0) {
uniqueValidSizes.add(currentPresetValue);
}
}
return presetValuesLength == uniqueValidSizes.size()
? presetValues
: uniqueValidSizes.toArray();
}
private boolean setupAutoSizeText() {
if (supportsAutoSizeText() && mAutoSizeTextType == AUTO_SIZE_TEXT_TYPE_UNIFORM) {
// Calculate the sizes set based on minimum size, maximum size and step size if we do
// not have a predefined set of sizes or if the current sizes array is empty.
if (!mHasPresetAutoSizeValues || mAutoSizeTextSizesInPx.length == 0) {
int autoSizeValuesLength = 1;
float currentSize = Math.round(mAutoSizeMinTextSizeInPx);
while (Math.round(currentSize + mAutoSizeStepGranularityInPx)
<= Math.round(mAutoSizeMaxTextSizeInPx)) {
autoSizeValuesLength++;
currentSize += mAutoSizeStepGranularityInPx;
}
int[] autoSizeTextSizesInPx = new int[autoSizeValuesLength];
float sizeToAdd = mAutoSizeMinTextSizeInPx;
for (int i = 0; i < autoSizeValuesLength; i++) {
autoSizeTextSizesInPx[i] = Math.round(sizeToAdd);
sizeToAdd += mAutoSizeStepGranularityInPx;
}
mAutoSizeTextSizesInPx = cleanupAutoSizePresetSizes(autoSizeTextSizesInPx);
}
mNeedsAutoSizeText = true;
} else {
mNeedsAutoSizeText = false;
}
return mNeedsAutoSizeText;
}
private int[] parseDimensionArray(TypedArray dimens) {
if (dimens == null) {
return null;
}
int[] result = new int[dimens.length()];
for (int i = 0; i < result.length; i++) {
result[i] = dimens.getDimensionPixelSize(i, 0);
}
return result;
}
/**
* @hide 这个这个看着眼熟,回调函数用来接收其他Activity返回的数据
*/
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == PROCESS_TEXT_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK && data != null) {
CharSequence result = data.getCharSequenceExtra(Intent.EXTRA_PROCESS_TEXT);
if (result != null) {
if (isTextEditable()) {
replaceSelectionWithText(result);
if (mEditor != null) {
mEditor.refreshTextActionMode();
}
} else {
if (result.length() > 0) {
Toast.makeText(getContext(), String.valueOf(result), Toast.LENGTH_LONG)
.show();
}
}
}
} else if (mText instanceof Spannable) {
// Reset the selection.
Selection.setSelection((Spannable) mText, getSelectionEnd());
}
}
}
private void setTypefaceFromAttrs(Typeface fontTypeface, String familyName, int typefaceIndex,
int styleIndex) {
Typeface tf = fontTypeface;
if (tf == null && familyName != null) {
tf = Typeface.create(familyName, styleIndex);
} else if (tf != null && tf.getStyle() != styleIndex) {
tf = Typeface.create(tf, styleIndex);
}
if (tf != null) {
setTypeface(tf);
return;
}
switch (typefaceIndex) {
case SANS:
tf = Typeface.SANS_SERIF;
break;
case SERIF:
tf = Typeface.SERIF;
break;
case MONOSPACE:
tf = Typeface.MONOSPACE;
break;
}
setTypeface(tf, styleIndex);
}
private void setRelativeDrawablesIfNeeded(Drawable start, Drawable end) {
boolean hasRelativeDrawables = (start != null) || (end != null);
if (hasRelativeDrawables) {
Drawables dr = mDrawables;
if (dr == null) {
mDrawables = dr = new Drawables(getContext());
}
mDrawables.mOverride = true;
final Rect compoundRect = dr.mCompoundRect;
int[] state = getDrawableState();
if (start != null) {
start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight());
start.setState(state);
start.copyBounds(compoundRect);
start.setCallback(this);
dr.mDrawableStart = start;
dr.mDrawableSizeStart = compoundRect.width();
dr.mDrawableHeightStart = compoundRect.height();
} else {
dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
}
if (end != null) {
end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight());
end.setState(state);
end.copyBounds(compoundRect);
end.setCallback(this);
dr.mDrawableEnd = end;
dr.mDrawableSizeEnd = compoundRect.width();
dr.mDrawableHeightEnd = compoundRect.height();
} else {
dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
}
resetResolvedDrawables();
resolveDrawables();
applyCompoundDrawableTint();
}
}
@android.view.RemotableViewMethod
@Override
public void setEnabled(boolean enabled) {
if (enabled == isEnabled()) {
return;
}
if (!enabled) {
// Hide the soft input if the currently active TextView is disabled
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null && imm.isActive(this)) {
imm.hideSoftInputFromWindow(getWindowToken(), 0);
}
}
super.setEnabled(enabled);
if (enabled) {
// Make sure IME is updated with current editor info.
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
}
// Will change text color
if (mEditor != null) {
mEditor.invalidateTextDisplayList();
mEditor.prepareCursorControllers();
// start or stop the cursor blinking as appropriate
mEditor.makeBlink();
}
}
/**
* Sets the typeface and style in which the text should be displayed,
* and turns on the fake bold and italic bits in the Paint if the
* Typeface that you provided does not have all the bits in the
* style that you specified.
*
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
public void setTypeface(Typeface tf, int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
/**
* Subclasses override this to specify that they have a KeyListener
* by default even if not specifically called for in the XML options.
*/
protected boolean getDefaultEditable() {
return false;
}
/**
* Subclasses override this to specify a default movement method.
*/
protected MovementMethod getDefaultMovementMethod() {
return null;
}
/**
* Return the text that TextView is displaying. If {@link #setText(CharSequence)} was called
* with an argument of {@link android.widget.TextView.BufferType#SPANNABLE BufferType.SPANNABLE}
* or {@link android.widget.TextView.BufferType#EDITABLE BufferType.EDITABLE}, you can cast
* the return value from this method to Spannable or Editable, respectively.
*
* <p>The content of the return value should not be modified. If you want a modifiable one, you
* should make your own copy first.</p>
*
* @return The text displayed by the text view.
* @attr ref android.R.styleable#TextView_text
*/
@ViewDebug.CapturedViewProperty
public CharSequence getText() {
return mText;
}
/**返回文本的长度
* Returns the length, in characters, of the text managed by this TextView
* @return The length of the text managed by the TextView in characters.
*/
public int length() {
return mText.length();
}
/**
返回文本视图作为可编辑对象显示的文本。
如果文本不是可编辑,则返回空值null
* Return the text that TextView is displaying as an Editable object. If the text is not
* editable, null is returned.
*
* @see #getText
*/
public Editable getEditableText() {
return (mText instanceof Editable) ? (Editable) mText : null;
}
/**
获取高度
* Gets the vertical distance between lines of text, in pixels.
* Note that markup within the text can cause individual lines
* to be taller or shorter than this height, and the layout may
* contain additional first-or last-line padding.
* @return The height of one standard line in pixels.
*/
public int getLineHeight() {
return FastMath.round(mTextPaint.getFontMetricsInt(null) * mSpacingMult + mSpacingAdd);
}
/**
返回显示当前文本的布局
* Gets the {@link android.text.Layout} that is currently being used to display the text.
* This value can be null if the text or width has recently changed.
* @return The Layout that is currently being used to display the text.
*/
public final Layout getLayout() {
return mLayout;
}
/**
返回当前用于显示提示文本的布局
* @return the {@link android.text.Layout} that is currently being used to
* display the hint text. This can be null.
*/
final Layout getHintLayout() {
return mHintLayout;
}
/**
* Retrieve the {@link android.content.UndoManager} that is currently associated
* with this TextView. By default there is no associated UndoManager, so null
* is returned. One can be associated with the TextView through
* {@link #setUndoManager(android.content.UndoManager, String)}
*
* @hide
*/
public final UndoManager getUndoManager() {
// TODO: Consider supporting a global undo manager.
throw new UnsupportedOperationException("not implemented");
}
/**
* @hide
*/
@VisibleForTesting
public final Editor getEditorForTesting() {
return mEditor;
}
/**
* Associate an {@link android.content.UndoManager} with this TextView. Once
* done, all edit operations on the TextView will result in appropriate
* {@link android.content.UndoOperation} objects pushed on the given UndoManager's
* stack.
*
* @param undoManager The {@link android.content.UndoManager} to associate with
* this TextView, or null to clear any existing association.
* @param tag String tag identifying this particular TextView owner in the
* UndoManager. This is used to keep the correct association with the
* {@link android.content.UndoOwner} of any operations inside of the UndoManager.
*
* @hide
*/
public final void setUndoManager(UndoManager undoManager, String tag) {
// TODO: Consider supporting a global undo manager. An implementation will need to:
// * createEditorIfNeeded()
// * Promote to BufferType.EDITABLE if needed.
// * Update the UndoManager and UndoOwner.
// Likewise it will need to be able to restore the default UndoManager.
throw new UnsupportedOperationException("not implemented");
}
/**
* Gets the current {@link KeyListener} for the TextView.
* This will frequently be null for non-EditText TextViews.
* @return the current key listener for this TextView.
*
* @attr ref android.R.styleable#TextView_numeric
* @attr ref android.R.styleable#TextView_digits
* @attr ref android.R.styleable#TextView_phoneNumber
* @attr ref android.R.styleable#TextView_inputMethod
* @attr ref android.R.styleable#TextView_capitalize
* @attr ref android.R.styleable#TextView_autoText
*/
public final KeyListener getKeyListener() {
return mEditor == null ? null : mEditor.mKeyListener;
}
/**
* Sets the key listener to be used with this TextView. This can be null
* to disallow user input. Note that this method has significant and
* subtle interactions with soft keyboards and other input method:
* see {@link KeyListener#getInputType() KeyListener.getContentType()}
* for important details. Calling this method will replace the current
* content type of the text view with the content type returned by the
* key listener.
* <p>
* Be warned that if you want a TextView with a key listener or movement
* method not to be focusable, or if you want a TextView without a
* key listener or movement method to be focusable, you must call
* {@link #setFocusable} again after calling this to get the focusability
* back the way you want it.
*
* @attr ref android.R.styleable#TextView_numeric
* @attr ref android.R.styleable#TextView_digits
* @attr ref android.R.styleable#TextView_phoneNumber
* @attr ref android.R.styleable#TextView_inputMethod
* @attr ref android.R.styleable#TextView_capitalize
* @attr ref android.R.styleable#TextView_autoText
*/
public void setKeyListener(KeyListener input) {
mListenerChanged = true;
setKeyListenerOnly(input);
fixFocusableAndClickableSettings();
if (input != null) {
createEditorIfNeeded();
setInputTypeFromEditor();
} else {
if (mEditor != null) mEditor.mInputType = EditorInfo.TYPE_NULL;
}
InputMethodManager imm = InputMethodManager.peekInstance();
if (imm != null) imm.restartInput(this);
}
private void setInputTypeFromEditor() {
try {
mEditor.mInputType = mEditor.mKeyListener.getInputType();
} catch (IncompatibleClassChangeError e) {
mEditor.mInputType = EditorInfo.TYPE_CLASS_TEXT;
}
// Change inputType, without affecting transformation.
// No need to applySingleLine since mSingleLine is unchanged.
setInputTypeSingleLine(mSingleLine);
}
private void setKeyListenerOnly(KeyListener input) {
if (mEditor == null && input == null) return; // null is the default value
createEditorIfNeeded();
if (mEditor.mKeyListener != input) {
mEditor.mKeyListener = input;
if (input != null && !(mText instanceof Editable)) {
setText(mText);
}
setFilters((Editable) mText, mFilters);
}
}
/**
* Gets the {@link android.text.method.MovementMethod} being used for this TextView,
* which provides positioning, scrolling, and text selection functionality.
* This will frequently be null for non-EditText TextViews.
* @return the movement method being used for this TextView.
* @see android.text.method.MovementMethod
*/
public final MovementMethod getMovementMethod() {
return mMovement;
}
/**
* Sets the {@link android.text.method.MovementMethod} for handling arrow key movement
* for this TextView. This can be null to disallow using the arrow keys to move the
* cursor or scroll the view.
* <p>
* Be warned that if you want a TextView with a key listener or movement
* method not to be focusable, or if you want a TextView without a
* key listener or movement method to be focusable, you must call
* {@link #setFocusable} again after calling this to get the focusability
* back the way you want it.
*/
public final void setMovementMethod(MovementMethod movement) {
if (mMovement != movement) {
mMovement = movement;
if (movement != null && !(mText instanceof Spannable)) {
setText(mText);
}
fixFocusableAndClickableSettings();
// SelectionModifierCursorController depends on textCanBeSelected, which depends on
// mMovement
if (mEditor != null) mEditor.prepareCursorControllers();
}
}
private void fixFocusableAndClickableSettings() {
if (mMovement != null || (mEditor != null && mEditor.mKeyListener != null)) {
setFocusable(FOCUSABLE);
setClickable(true);
setLongClickable(true);
} else {
setFocusable(FOCUSABLE_AUTO);
setClickable(false);
setLongClickable(false);
}
}
/**
* Gets the current {@link android.text.method.TransformationMethod} for the TextView.
* This is frequently null, except for single-line and password fields.
* @return the current transformation method for this TextView.
*
* @attr ref android.R.styleable#TextView_password
* @attr ref android.R.styleable#TextView_singleLine
*/
public final TransformationMethod getTransformationMethod() {
return mTransformation;
}
/**
* Sets the transformation that is applied to the text that this
* TextView is displaying.
*
* @attr ref android.R.styleable#TextView_password
* @attr ref android.R.styleable#TextView_singleLine
*/
public final void setTransformationMethod(TransformationMethod method) {
if (method == mTransformation) {
// Avoid the setText() below if the transformation is
// the same.
return;
}
if (mTransformation != null) {
if (mText instanceof Spannable) {
((Spannable) mText).removeSpan(mTransformation);
}
}
mTransformation = method;
if (method instanceof TransformationMethod2) {
TransformationMethod2 method2 = (TransformationMethod2) method;
mAllowTransformationLengthChange = !isTextSelectable() && !(mText instanceof Editable);
method2.setLengthChangesAllowed(mAllowTransformationLengthChange);
} else {
mAllowTransformationLengthChange = false;
}
setText(mText);
if (hasPasswordTransformationMethod()) {
notifyViewAccessibilityStateChangedIfNeeded(
AccessibilityEvent.CONTENT_CHANGE_TYPE_UNDEFINED);
}
// PasswordTransformationMethod always have LTR text direction heuristics returned by
// getTextDirectionHeuristic, needs reset
mTextDir = getTextDirectionHeuristic();
}
/**
* Returns the top padding of the view, plus space for the top
* Drawable if any.
*/
public int getCompoundPaddingTop() {
final Drawables dr = mDrawables;
if (dr == null || dr.mShowing[Drawables.TOP] == null) {
return mPaddingTop;
} else {
return mPaddingTop + dr.mDrawablePadding + dr.mDrawableSizeTop;
}
}
/**
* Returns the bottom padding of the view, plus space for the bottom
* Drawable if any.
*/
public int getCompoundPaddingBottom() {
final Drawables dr = mDrawables;
if (dr == null || dr.mShowing[Drawables.BOTTOM] == null) {
return mPaddingBottom;
} else {
return mPaddingBottom + dr.mDrawablePadding + dr.mDrawableSizeBottom;
}
}
/**
* Returns the left padding of the view, plus space for the left
* Drawable if any.
*/
public int getCompoundPaddingLeft() {
final Drawables dr = mDrawables;
if (dr == null || dr.mShowing[Drawables.LEFT] == null) {
return mPaddingLeft;
} else {
return mPaddingLeft + dr.mDrawablePadding + dr.mDrawableSizeLeft;
}
}
/**
* Returns the right padding of the view, plus space for the right
* Drawable if any.
*/
public int getCompoundPaddingRight() {
final Drawables dr = mDrawables;
if (dr == null || dr.mShowing[Drawables.RIGHT] == null) {
return mPaddingRight;
} else {
return mPaddingRight + dr.mDrawablePadding + dr.mDrawableSizeRight;
}
}
/**
* Returns the start padding of the view, plus space for the start
* Drawable if any.
*/
public int getCompoundPaddingStart() {
resolveDrawables();
switch(getLayoutDirection()) {
default:
case LAYOUT_DIRECTION_LTR:
return getCompoundPaddingLeft();
case LAYOUT_DIRECTION_RTL:
return getCompoundPaddingRight();
}
}
/**
* Returns the end padding of the view, plus space for the end
* Drawable if any.
*/
public int getCompoundPaddingEnd() {
resolveDrawables();
switch(getLayoutDirection()) {
default:
case LAYOUT_DIRECTION_LTR:
return getCompoundPaddingRight();
case LAYOUT_DIRECTION_RTL:
return getCompoundPaddingLeft();
}
}
/**
* Returns the extended top padding of the view, including both the
* top Drawable if any and any extra space to keep more than maxLines
* of text from showing. It is only valid to call this after measuring.
*/
public int getExtendedPaddingTop() {
if (mMaxMode != LINES) {
return getCompoundPaddingTop();
}
if (mLayout == null) {
assumeLayout();
}
if (mLayout.getLineCount() <= mMaximum) {
return getCompoundPaddingTop();
}
int top = getCompoundPaddingTop();
int bottom = getCompoundPaddingBottom();
int viewht = getHeight() - top - bottom;
int layoutht = mLayout.getLineTop(mMaximum);
if (layoutht >= viewht) {
return top;
}
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (gravity == Gravity.TOP) {
return top;
} else if (gravity == Gravity.BOTTOM) {
return top + viewht - layoutht;
} else { // (gravity == Gravity.CENTER_VERTICAL)
return top + (viewht - layoutht) / 2;
}
}
/**
* Returns the extended bottom padding of the view, including both the
* bottom Drawable if any and any extra space to keep more than maxLines
* of text from showing. It is only valid to call this after measuring.
*/
public int getExtendedPaddingBottom() {
if (mMaxMode != LINES) {
return getCompoundPaddingBottom();
}
if (mLayout == null) {
assumeLayout();
}
if (mLayout.getLineCount() <= mMaximum) {
return getCompoundPaddingBottom();
}
int top = getCompoundPaddingTop();
int bottom = getCompoundPaddingBottom();
int viewht = getHeight() - top - bottom;
int layoutht = mLayout.getLineTop(mMaximum);
if (layoutht >= viewht) {
return bottom;
}
final int gravity = mGravity & Gravity.VERTICAL_GRAVITY_MASK;
if (gravity == Gravity.TOP) {
return bottom + viewht - layoutht;
} else if (gravity == Gravity.BOTTOM) {
return bottom;
} else { // (gravity == Gravity.CENTER_VERTICAL)
return bottom + (viewht - layoutht) / 2;
}
}
/**
* Returns the total left padding of the view, including the left
* Drawable if any.
*/
public int getTotalPaddingLeft() {
return getCompoundPaddingLeft();
}
/**
* Returns the total right padding of the view, including the right
* Drawable if any.
*/
public int getTotalPaddingRight() {
return getCompoundPaddingRight();
}
/**
* Returns the total start padding of the view, including the start
* Drawable if any.
*/
public int getTotalPaddingStart() {
return getCompoundPaddingStart();
}
/**
* Returns the total end padding of the view, including the end
* Drawable if any.
*/
public int getTotalPaddingEnd() {
return getCompoundPaddingEnd();
}
/**
* Returns the total top padding of the view, including the top
* Drawable if any, the extra space to keep more than maxLines
* from showing, and the vertical offset for gravity, if any.
*/
public int getTotalPaddingTop() {
return getExtendedPaddingTop() + getVerticalOffset(true);
}
/**
* Returns the total bottom padding of the view, including the bottom
* Drawable if any, the extra space to keep more than maxLines
* from showing, and the vertical offset for gravity, if any.
*/
public int getTotalPaddingBottom() {
return getExtendedPaddingBottom() + getBottomVerticalOffset(true);
}
/**
* Sets the Drawables (if any) to appear to the left of, above, to the
* right of, and below the text. Use {@code null} if you do not want a
* Drawable there. The Drawables must already have had
* {@link Drawable#setBounds} called.
* <p>
* Calling this method will overwrite any Drawables previously set using
* {@link #setCompoundDrawablesRelative} or related methods.
*
* @attr ref android.R.styleable#TextView_drawableLeft
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableBottom
*/
public void setCompoundDrawables(@Nullable Drawable left, @Nullable Drawable top,
@Nullable Drawable right, @Nullable Drawable bottom) {
Drawables dr = mDrawables;
// We're switching to absolute, discard relative.
if (dr != null) {
if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null);
dr.mDrawableStart = null;
if (dr.mDrawableEnd != null) dr.mDrawableEnd.setCallback(null);
dr.mDrawableEnd = null;
dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
}
final boolean drawables = left != null || top != null || right != null || bottom != null;
if (!drawables) {
// Clearing drawables... can we free the data structure?
if (dr != null) {
if (!dr.hasMetadata()) {
mDrawables = null;
} else {
// We need to retain the last set padding, so just clear
// out all of the fields in the existing structure.
for (int i = dr.mShowing.length - 1; i >= 0; i--) {
if (dr.mShowing[i] != null) {
dr.mShowing[i].setCallback(null);
}
dr.mShowing[i] = null;
}
dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
}
}
} else {
if (dr == null) {
mDrawables = dr = new Drawables(getContext());
}
mDrawables.mOverride = false;
if (dr.mShowing[Drawables.LEFT] != left && dr.mShowing[Drawables.LEFT] != null) {
dr.mShowing[Drawables.LEFT].setCallback(null);
}
dr.mShowing[Drawables.LEFT] = left;
if (dr.mShowing[Drawables.TOP] != top && dr.mShowing[Drawables.TOP] != null) {
dr.mShowing[Drawables.TOP].setCallback(null);
}
dr.mShowing[Drawables.TOP] = top;
if (dr.mShowing[Drawables.RIGHT] != right && dr.mShowing[Drawables.RIGHT] != null) {
dr.mShowing[Drawables.RIGHT].setCallback(null);
}
dr.mShowing[Drawables.RIGHT] = right;
if (dr.mShowing[Drawables.BOTTOM] != bottom && dr.mShowing[Drawables.BOTTOM] != null) {
dr.mShowing[Drawables.BOTTOM].setCallback(null);
}
dr.mShowing[Drawables.BOTTOM] = bottom;
final Rect compoundRect = dr.mCompoundRect;
int[] state;
state = getDrawableState();
if (left != null) {
left.setState(state);
left.copyBounds(compoundRect);
left.setCallback(this);
dr.mDrawableSizeLeft = compoundRect.width();
dr.mDrawableHeightLeft = compoundRect.height();
} else {
dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
}
if (right != null) {
right.setState(state);
right.copyBounds(compoundRect);
right.setCallback(this);
dr.mDrawableSizeRight = compoundRect.width();
dr.mDrawableHeightRight = compoundRect.height();
} else {
dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
}
if (top != null) {
top.setState(state);
top.copyBounds(compoundRect);
top.setCallback(this);
dr.mDrawableSizeTop = compoundRect.height();
dr.mDrawableWidthTop = compoundRect.width();
} else {
dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
}
if (bottom != null) {
bottom.setState(state);
bottom.copyBounds(compoundRect);
bottom.setCallback(this);
dr.mDrawableSizeBottom = compoundRect.height();
dr.mDrawableWidthBottom = compoundRect.width();
} else {
dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
}
}
// Save initial left/right drawables
if (dr != null) {
dr.mDrawableLeftInitial = left;
dr.mDrawableRightInitial = right;
}
resetResolvedDrawables();
resolveDrawables();
applyCompoundDrawableTint();
invalidate();
requestLayout();
}
/**
* Sets the Drawables (if any) to appear to the left of, above, to the
* right of, and below the text. Use 0 if you do not want a Drawable there.
* The Drawables' bounds will be set to their intrinsic bounds.
* <p>
* Calling this method will overwrite any Drawables previously set using
* {@link #setCompoundDrawablesRelative} or related methods.
*
* @param left Resource identifier of the left Drawable.
* @param top Resource identifier of the top Drawable.
* @param right Resource identifier of the right Drawable.
* @param bottom Resource identifier of the bottom Drawable.
*
* @attr ref android.R.styleable#TextView_drawableLeft
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@android.view.RemotableViewMethod
public void setCompoundDrawablesWithIntrinsicBounds(@DrawableRes int left,
@DrawableRes int top, @DrawableRes int right, @DrawableRes int bottom) {
final Context context = getContext();
setCompoundDrawablesWithIntrinsicBounds(left != 0 ? context.getDrawable(left) : null,
top != 0 ? context.getDrawable(top) : null,
right != 0 ? context.getDrawable(right) : null,
bottom != 0 ? context.getDrawable(bottom) : null);
}
/**
* Sets the Drawables (if any) to appear to the left of, above, to the
* right of, and below the text. Use {@code null} if you do not want a
* Drawable there. The Drawables' bounds will be set to their intrinsic
* bounds.
* <p>
* Calling this method will overwrite any Drawables previously set using
* {@link #setCompoundDrawablesRelative} or related methods.
*
* @attr ref android.R.styleable#TextView_drawableLeft
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@android.view.RemotableViewMethod
public void setCompoundDrawablesWithIntrinsicBounds(@Nullable Drawable left,
@Nullable Drawable top, @Nullable Drawable right, @Nullable Drawable bottom) {
if (left != null) {
left.setBounds(0, 0, left.getIntrinsicWidth(), left.getIntrinsicHeight());
}
if (right != null) {
right.setBounds(0, 0, right.getIntrinsicWidth(), right.getIntrinsicHeight());
}
if (top != null) {
top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
}
if (bottom != null) {
bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
}
setCompoundDrawables(left, top, right, bottom);
}
/**
* Sets the Drawables (if any) to appear to the start of, above, to the end
* of, and below the text. Use {@code null} if you do not want a Drawable
* there. The Drawables must already have had {@link Drawable#setBounds}
* called.
* <p>
* Calling this method will overwrite any Drawables previously set using
* {@link #setCompoundDrawables} or related methods.
*
* @attr ref android.R.styleable#TextView_drawableStart
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@android.view.RemotableViewMethod
public void setCompoundDrawablesRelative(@Nullable Drawable start, @Nullable Drawable top,
@Nullable Drawable end, @Nullable Drawable bottom) {
Drawables dr = mDrawables;
// We're switching to relative, discard absolute.
if (dr != null) {
if (dr.mShowing[Drawables.LEFT] != null) {
dr.mShowing[Drawables.LEFT].setCallback(null);
}
dr.mShowing[Drawables.LEFT] = dr.mDrawableLeftInitial = null;
if (dr.mShowing[Drawables.RIGHT] != null) {
dr.mShowing[Drawables.RIGHT].setCallback(null);
}
dr.mShowing[Drawables.RIGHT] = dr.mDrawableRightInitial = null;
dr.mDrawableSizeLeft = dr.mDrawableHeightLeft = 0;
dr.mDrawableSizeRight = dr.mDrawableHeightRight = 0;
}
final boolean drawables = start != null || top != null
|| end != null || bottom != null;
if (!drawables) {
// Clearing drawables... can we free the data structure?
if (dr != null) {
if (!dr.hasMetadata()) {
mDrawables = null;
} else {
// We need to retain the last set padding, so just clear
// out all of the fields in the existing structure.
if (dr.mDrawableStart != null) dr.mDrawableStart.setCallback(null);
dr.mDrawableStart = null;
if (dr.mShowing[Drawables.TOP] != null) {
dr.mShowing[Drawables.TOP].setCallback(null);
}
dr.mShowing[Drawables.TOP] = null;
if (dr.mDrawableEnd != null) {
dr.mDrawableEnd.setCallback(null);
}
dr.mDrawableEnd = null;
if (dr.mShowing[Drawables.BOTTOM] != null) {
dr.mShowing[Drawables.BOTTOM].setCallback(null);
}
dr.mShowing[Drawables.BOTTOM] = null;
dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
}
}
} else {
if (dr == null) {
mDrawables = dr = new Drawables(getContext());
}
mDrawables.mOverride = true;
if (dr.mDrawableStart != start && dr.mDrawableStart != null) {
dr.mDrawableStart.setCallback(null);
}
dr.mDrawableStart = start;
if (dr.mShowing[Drawables.TOP] != top && dr.mShowing[Drawables.TOP] != null) {
dr.mShowing[Drawables.TOP].setCallback(null);
}
dr.mShowing[Drawables.TOP] = top;
if (dr.mDrawableEnd != end && dr.mDrawableEnd != null) {
dr.mDrawableEnd.setCallback(null);
}
dr.mDrawableEnd = end;
if (dr.mShowing[Drawables.BOTTOM] != bottom && dr.mShowing[Drawables.BOTTOM] != null) {
dr.mShowing[Drawables.BOTTOM].setCallback(null);
}
dr.mShowing[Drawables.BOTTOM] = bottom;
final Rect compoundRect = dr.mCompoundRect;
int[] state;
state = getDrawableState();
if (start != null) {
start.setState(state);
start.copyBounds(compoundRect);
start.setCallback(this);
dr.mDrawableSizeStart = compoundRect.width();
dr.mDrawableHeightStart = compoundRect.height();
} else {
dr.mDrawableSizeStart = dr.mDrawableHeightStart = 0;
}
if (end != null) {
end.setState(state);
end.copyBounds(compoundRect);
end.setCallback(this);
dr.mDrawableSizeEnd = compoundRect.width();
dr.mDrawableHeightEnd = compoundRect.height();
} else {
dr.mDrawableSizeEnd = dr.mDrawableHeightEnd = 0;
}
if (top != null) {
top.setState(state);
top.copyBounds(compoundRect);
top.setCallback(this);
dr.mDrawableSizeTop = compoundRect.height();
dr.mDrawableWidthTop = compoundRect.width();
} else {
dr.mDrawableSizeTop = dr.mDrawableWidthTop = 0;
}
if (bottom != null) {
bottom.setState(state);
bottom.copyBounds(compoundRect);
bottom.setCallback(this);
dr.mDrawableSizeBottom = compoundRect.height();
dr.mDrawableWidthBottom = compoundRect.width();
} else {
dr.mDrawableSizeBottom = dr.mDrawableWidthBottom = 0;
}
}
resetResolvedDrawables();
resolveDrawables();
invalidate();
requestLayout();
}
/**
* Sets the Drawables (if any) to appear to the start of, above, to the end
* of, and below the text. Use 0 if you do not want a Drawable there. The
* Drawables' bounds will be set to their intrinsic bounds.
* <p>
* Calling this method will overwrite any Drawables previously set using
* {@link #setCompoundDrawables} or related methods.
*
* @param start Resource identifier of the start Drawable.
* @param top Resource identifier of the top Drawable.
* @param end Resource identifier of the end Drawable.
* @param bottom Resource identifier of the bottom Drawable.
*
* @attr ref android.R.styleable#TextView_drawableStart
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@android.view.RemotableViewMethod
public void setCompoundDrawablesRelativeWithIntrinsicBounds(@DrawableRes int start,
@DrawableRes int top, @DrawableRes int end, @DrawableRes int bottom) {
final Context context = getContext();
setCompoundDrawablesRelativeWithIntrinsicBounds(
start != 0 ? context.getDrawable(start) : null,
top != 0 ? context.getDrawable(top) : null,
end != 0 ? context.getDrawable(end) : null,
bottom != 0 ? context.getDrawable(bottom) : null);
}
/**
* Sets the Drawables (if any) to appear to the start of, above, to the end
* of, and below the text. Use {@code null} if you do not want a Drawable
* there. The Drawables' bounds will be set to their intrinsic bounds.
* <p>
* Calling this method will overwrite any Drawables previously set using
* {@link #setCompoundDrawables} or related methods.
*
* @attr ref android.R.styleable#TextView_drawableStart
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@android.view.RemotableViewMethod
public void setCompoundDrawablesRelativeWithIntrinsicBounds(@Nullable Drawable start,
@Nullable Drawable top, @Nullable Drawable end, @Nullable Drawable bottom) {
if (start != null) {
start.setBounds(0, 0, start.getIntrinsicWidth(), start.getIntrinsicHeight());
}
if (end != null) {
end.setBounds(0, 0, end.getIntrinsicWidth(), end.getIntrinsicHeight());
}
if (top != null) {
top.setBounds(0, 0, top.getIntrinsicWidth(), top.getIntrinsicHeight());
}
if (bottom != null) {
bottom.setBounds(0, 0, bottom.getIntrinsicWidth(), bottom.getIntrinsicHeight());
}
setCompoundDrawablesRelative(start, top, end, bottom);
}
/**
* Returns drawables for the left, top, right, and bottom borders.
*
* @attr ref android.R.styleable#TextView_drawableLeft
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableRight
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@NonNull
public Drawable[] getCompoundDrawables() {
final Drawables dr = mDrawables;
if (dr != null) {
return dr.mShowing.clone();
} else {
return new Drawable[] { null, null, null, null };
}
}
/**
* Returns drawables for the start, top, end, and bottom borders.
*
* @attr ref android.R.styleable#TextView_drawableStart
* @attr ref android.R.styleable#TextView_drawableTop
* @attr ref android.R.styleable#TextView_drawableEnd
* @attr ref android.R.styleable#TextView_drawableBottom
*/
@NonNull
public Drawable[] getCompoundDrawablesRelative() {
final Drawables dr = mDrawables;
if (dr != null) {
return new Drawable[] {
dr.mDrawableStart, dr.mShowing[Drawables.TOP],
dr.mDrawableEnd, dr.mShowing[Drawables.BOTTOM]
};
} else {
return new Drawable[] { null, null, null, null };
}
}
/**
* Sets the size of the padding between the compound drawables and
* the text.
*
* @attr ref android.R.styleable#TextView_drawablePadding
*/
@android.view.RemotableViewMethod
public void setCompoundDrawablePadding(int pad) {
Drawables dr = mDrawables;
if (pad == 0) {
if (dr != null) {
dr.mDrawablePadding = pad;
}
} else {
if (dr == null) {
mDrawables = dr = new Drawables(getContext());
}
dr.mDrawablePadding = pad;
}
invalidate();
requestLayout();
}
/**
* Returns the padding between the compound drawables and the text.
*
* @attr ref android.R.styleable#TextView_drawablePadding
*/
public int getCompoundDrawablePadding() {
final Drawables dr = mDrawables;
return dr != null ? dr.mDrawablePadding : 0;
}
/**
* Applies a tint to the compound drawables. Does not modify the
* current tint mode, which is {@link PorterDuff.Mode#SRC_IN} by default.
* <p>
* Subsequent calls to
* {@link #setCompoundDrawables(Drawable, Drawable, Drawable, Drawable)}
* and related methods will automatically mutate the drawables and apply
* the specified tint and tint mode using
* {@link Drawable#setTintList(ColorStateList)}.
*
* @param tint the tint to apply, may be {@code null} to clear tint
*
* @attr ref android.R.styleable#TextView_drawableTint
* @see #getCompoundDrawableTintList()
* @see Drawable#setTintList(ColorStateList)
*/
public void setCompoundDrawableTintList(@Nullable ColorStateList tint) {
if (mDrawables == null) {
mDrawables = new Drawables(getContext());
}
mDrawables.mTintList = tint;
mDrawables.mHasTint = true;
applyCompoundDrawableTint();
}
/**
* @return the tint applied to the compound drawables
* @attr ref android.R.styleable#TextView_drawableTint
* @see #setCompoundDrawableTintList(ColorStateList)
*/
public ColorStateList getCompoundDrawableTintList() {
return mDrawables != null ? mDrawables.mTintList : null;
}
/**
* Specifies the blending mode used to apply the tint specified by
* {@link #setCompoundDrawableTintList(ColorStateList)} to the compound
* drawables. The default mode is {@link PorterDuff.Mode#SRC_IN}.
*
* @param tintMode the blending mode used to apply the tint, may be
* {@code null} to clear tint
* @attr ref android.R.styleable#TextView_drawableTintMode
* @see #setCompoundDrawableTintList(ColorStateList)
* @see Drawable#setTintMode(PorterDuff.Mode)
*/
public void setCompoundDrawableTintMode(@Nullable PorterDuff.Mode tintMode) {
if (mDrawables == null) {
mDrawables = new Drawables(getContext());
}
mDrawables.mTintMode = tintMode;
mDrawables.mHasTintMode = true;
applyCompoundDrawableTint();
}
/**
* Returns the blending mode used to apply the tint to the compound
* drawables, if specified.
*
* @return the blending mode used to apply the tint to the compound
* drawables
* @attr ref android.R.styleable#TextView_drawableTintMode
* @see #setCompoundDrawableTintMode(PorterDuff.Mode)
*/
public PorterDuff.Mode getCompoundDrawableTintMode() {
return mDrawables != null ? mDrawables.mTintMode : null;
}
private void applyCompoundDrawableTint() {
if (mDrawables == null) {
return;
}
if (mDrawables.mHasTint || mDrawables.mHasTintMode) {
final ColorStateList tintList = mDrawables.mTintList;
final PorterDuff.Mode tintMode = mDrawables.mTintMode;
final boolean hasTint = mDrawables.mHasTint;
final boolean hasTintMode = mDrawables.mHasTintMode;
final int[] state = getDrawableState();
for (Drawable dr : mDrawables.mShowing) {
if (dr == null) {
continue;
}
if (dr == mDrawables.mDrawableError) {
// From a developer's perspective, the error drawable isn't
// a compound drawable. Don't apply the generic compound
// drawable tint to it.
continue;
}
dr.mutate();
if (hasTint) {
dr.setTintList(tintList);
}
if (hasTintMode) {
dr.setTintMode(tintMode);
}
// The drawable (or one of its children) may not have been
// stateful before applying the tint, so let's try again.
if (dr.isStateful()) {
dr.setState(state);
}
}
}
}
@Override
public void setPadding(int left, int top, int right, int bottom) {
if (left != mPaddingLeft
|| right != mPaddingRight
|| top != mPaddingTop
|| bottom != mPaddingBottom) {
nullLayouts();
}
// the super call will requestLayout()
super.setPadding(left, top, right, bottom);
invalidate();
}
@Override
public void setPaddingRelative(int start, int top, int end, int bottom) {
if (start != getPaddingStart()
|| end != getPaddingEnd()
|| top != mPaddingTop
|| bottom != mPaddingBottom) {
nullLayouts();
}
// the super call will requestLayout()
super.setPaddingRelative(start, top, end, bottom);
invalidate();
}
/**
* Gets the autolink mask of the text. See {@link
* android.text.util.Linkify#ALL Linkify.ALL} and peers for
* possible values.
*
* @attr ref android.R.styleable#TextView_autoLink
*/
public final int getAutoLinkMask() {
return mAutoLinkMask;
}
/**
* Sets the text appearance from the specified style resource.
* <p>
* Use a framework-defined {@code TextAppearance} style like
* {@link android.R.style#TextAppearance_Material_Body1 @android:style/TextAppearance.Material.Body1}
* or see {@link android.R.styleable#TextAppearance TextAppearance} for the
* set of attributes that can be used in a custom style.
*
* @param resId the resource identifier of the style to apply
* @attr ref android.R.styleable#TextView_textAppearance
*/
@SuppressWarnings("deprecation")
public void setTextAppearance(@StyleRes int resId) {
setTextAppearance(mContext, resId);
}
/**
* Sets the text color, size, style, hint color, and highlight color
* from the specified TextAppearance resource.
*
* @deprecated Use {@link #setTextAppearance(int)} instead.
*/
@Deprecated
public void setTextAppearance(Context context, @StyleRes int resId) {
final TypedArray ta = context.obtainStyledAttributes(resId, R.styleable.TextAppearance);
final int textColorHighlight = ta.getColor(
R.styleable.TextAppearance_textColorHighlight, 0);
if (textColorHighlight != 0) {
setHighlightColor(textColorHighlight);
}
final ColorStateList textColor = ta.getColorStateList(R.styleable.TextAppearance_textColor);
if (textColor != null) {
setTextColor(textColor);
}
final int textSize = ta.getDimensionPixelSize(R.styleable.TextAppearance_textSize, 0);
if (textSize != 0) {
setRawTextSize(textSize, true /* shouldRequestLayout */);
}
final ColorStateList textColorHint = ta.getColorStateList(
R.styleable.TextAppearance_textColorHint);
if (textColorHint != null) {
setHintTextColor(textColorHint);
}
final ColorStateList textColorLink = ta.getColorStateList(
R.styleable.TextAppearance_textColorLink);
if (textColorLink != null) {
setLinkTextColor(textColorLink);
}
Typeface fontTypeface = null;
String fontFamily = null;
if (!context.isRestricted() && context.canLoadUnsafeResources()) {
try {
fontTypeface = ta.getFont(R.styleable.TextAppearance_fontFamily);
} catch (UnsupportedOperationException | Resources.NotFoundException e) {
// Expected if it is not a font resource.
}
}
if (fontTypeface == null) {
fontFamily = ta.getString(R.styleable.TextAppearance_fontFamily);
}
final int typefaceIndex = ta.getInt(R.styleable.TextAppearance_typeface, -1);
final int styleIndex = ta.getInt(R.styleable.TextAppearance_textStyle, -1);
setTypefaceFromAttrs(fontTypeface, fontFamily, typefaceIndex, styleIndex);
final int shadowColor = ta.getInt(R.styleable.TextAppearance_shadowColor, 0);
if (shadowColor != 0) {
final float dx = ta.getFloat(R.styleable.TextAppearance_shadowDx, 0);
final float dy = ta.getFloat(R.styleable.TextAppearance_shadowDy, 0);
final float r = ta.getFloat(R.styleable.TextAppearance_shadowRadius, 0);
setShadowLayer(r, dx, dy, shadowColor);
}
if (ta.getBoolean(R.styleable.TextAppearance_textAllCaps, false)) {
setTransformationMethod(new AllCapsTransformationMethod(getContext()));
}
if (ta.hasValue(R.styleable.TextAppearance_elegantTextHeight)) {
setElegantTextHeight(ta.getBoolean(
R.styleable.TextAppearance_elegantTextHeight, false));
}
if (ta.hasValue(R.styleable.TextAppearance_letterSpacing)) {
setLetterSpacing(ta.getFloat(
R.styleable.TextAppearance_letterSpacing, 0));
}
if (ta.hasValue(R.styleable.TextAppearance_fontFeatureSettings)) {
setFontFeatureSettings(ta.getString(
R.styleable.TextAppearance_fontFeatureSettings));
}
ta.recycle();
}
/**
* Get the default primary {@link Locale} of the text in this TextView. This will always be
* the first member of {@link #getTextLocales()}.
* @return the default primary {@link Locale} of the text in this TextView.
*/
@NonNull
public Locale getTextLocale() {
return mTextPaint.getTextLocale();
}
/**
* Get the default {@link LocaleList} of the text in this TextView.
* @return the default {@link LocaleList} of the text in this TextView.
*/
@NonNull @Size(min = 1)
public LocaleList getTextLocales() {
return mTextPaint.getTextLocales();
}
private void changeListenerLocaleTo(@Nullable Locale locale) {
if (mListenerChanged) {
// If a listener has been explicitly set, don't change it. We may break something.
return;
}
// The following null check is not absolutely necessary since all calling points of
// changeListenerLocaleTo() guarantee a non-null mEditor at the moment. But this is left
// here in case others would want to call this method in the future.
if (mEditor != null) {
KeyListener listener = mEditor.mKeyListener;
if (listener instanceof DigitsKeyListener) {
listener = DigitsKeyListener.getInstance(locale, (DigitsKeyListener) listener);
} else if (listener instanceof DateKeyListener) {
listener = DateKeyListener.getInstance(locale);
} else if (listener instanceof TimeKeyListener) {
listener = TimeKeyListener.getInstance(locale);
} else if (listener instanceof DateTimeKeyListener) {
listener = DateTimeKeyListener.getInstance(locale);
} else {
return;
}
final boolean wasPasswordType = isPasswordInputType(mEditor.mInputType);
setKeyListenerOnly(listener);
setInputTypeFromEditor();
if (wasPasswordType) {
final int newInputClass = mEditor.mInputType & EditorInfo.TYPE_MASK_CLASS;
if (newInputClass == EditorInfo.TYPE_CLASS_TEXT) {
mEditor.mInputType |= EditorInfo.TYPE_TEXT_VARIATION_PASSWORD;
} else if (newInputClass == EditorInfo.TYPE_CLASS_NUMBER) {
mEditor.mInputType |= EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD;
}
}
}
}
/**
* Set the default {@link Locale} of the text in this TextView to a one-member
* {@link LocaleList} containing just the given Locale.
*
* @param locale the {@link Locale} for drawing text, must not be null.
*
* @see #setTextLocales
*/
public void setTextLocale(@NonNull Locale locale) {
mLocalesChanged = true;
mTextPaint.setTextLocale(locale);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* Set the default {@link LocaleList} of the text in this TextView to the given value.
*
* This value is used to choose appropriate typefaces for ambiguous characters (typically used
* for CJK locales to disambiguate Hanzi/Kanji/Hanja characters). It also affects
* other aspects of text display, including line breaking.
*
* @param locales the {@link LocaleList} for drawing text, must not be null or empty.
*
* @see Paint#setTextLocales
*/
public void setTextLocales(@NonNull @Size(min = 1) LocaleList locales) {
mLocalesChanged = true;
mTextPaint.setTextLocales(locales);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (!mLocalesChanged) {
mTextPaint.setTextLocales(LocaleList.getDefault());
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* @return the size (in pixels) of the default text size in this TextView.
*/
@ViewDebug.ExportedProperty(category = "text")
public float getTextSize() {
return mTextPaint.getTextSize();
}
/**
* @return the size (in scaled pixels) of the default text size in this TextView.
* @hide
*/
@ViewDebug.ExportedProperty(category = "text")
public float getScaledTextSize() {
return mTextPaint.getTextSize() / mTextPaint.density;
}
/** @hide */
@ViewDebug.ExportedProperty(category = "text", mapping = {
@ViewDebug.IntToString(from = Typeface.NORMAL, to = "NORMAL"),
@ViewDebug.IntToString(from = Typeface.BOLD, to = "BOLD"),
@ViewDebug.IntToString(from = Typeface.ITALIC, to = "ITALIC"),
@ViewDebug.IntToString(from = Typeface.BOLD_ITALIC, to = "BOLD_ITALIC")
})
public int getTypefaceStyle() {
Typeface typeface = mTextPaint.getTypeface();
return typeface != null ? typeface.getStyle() : Typeface.NORMAL;
}
/**
* Set the default text size to the given value, interpreted as "scaled
* pixel" units. This size is adjusted based on the current density and
* user font size preference.
*
* <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
*
* @param size The scaled pixel size.
*
* @attr ref android.R.styleable#TextView_textSize
*/
@android.view.RemotableViewMethod
public void setTextSize(float size) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the default text size to a given unit and value. See {@link
* TypedValue} for the possible dimension units.
*
* <p>Note: if this TextView has the auto-size feature enabled than this function is no-op.
*
* @param unit The desired dimension unit.
* @param size The desired size in the given units.
*
* @attr ref android.R.styleable#TextView_textSize
*/
public void setTextSize(int unit, float size) {
if (!isAutoSizeEnabled()) {
setTextSizeInternal(unit, size, true /* shouldRequestLayout */);
}
}
private void setTextSizeInternal(int unit, float size, boolean shouldRequestLayout) {
Context c = getContext();
Resources r;
if (c == null) {
r = Resources.getSystem();
} else {
r = c.getResources();
}
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
shouldRequestLayout);
}
private void setRawTextSize(float size, boolean shouldRequestLayout) {
if (size != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(size);
if (shouldRequestLayout && mLayout != null) {
// Do not auto-size right after setting the text size.
mNeedsAutoSizeText = false;
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Gets the extent by which text should be stretched horizontally.
* This will usually be 1.0.
* @return The horizontal scale factor.
*/
public float getTextScaleX() {
return mTextPaint.getTextScaleX();
}
/**
* Sets the horizontal scale factor for text. The default value
* is 1.0. Values greater than 1.0 stretch the text wider.
* Values less than 1.0 make the text narrower. By default, this value is 1.0.
* @param size The horizontal scale factor.
* @attr ref android.R.styleable#TextView_textScaleX
*/
@android.view.RemotableViewMethod
public void setTextScaleX(float size) {
if (size != mTextPaint.getTextScaleX()) {
mUserSetTextScaleX = true;
mTextPaint.setTextScaleX(size);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Sets the typeface and style in which the text should be displayed.
* Note that not all Typeface families actually have bold and italic
* variants, so you may need to use
* {@link #setTypeface(Typeface, int)} to get the appearance
* that you actually want.
*
* @see #getTypeface()
*
* @attr ref android.R.styleable#TextView_fontFamily
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
public void setTypeface(Typeface tf) {
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Gets the current {@link Typeface} that is used to style the text.
* @return The current Typeface.
*
* @see #setTypeface(Typeface)
*
* @attr ref android.R.styleable#TextView_fontFamily
* @attr ref android.R.styleable#TextView_typeface
* @attr ref android.R.styleable#TextView_textStyle
*/
public Typeface getTypeface() {
return mTextPaint.getTypeface();
}
/**
* Set the TextView's elegant height metrics flag. This setting selects font
* variants that have not been compacted to fit Latin-based vertical
* metrics, and also increases top and bottom bounds to provide more space.
*
* @param elegant set the paint's elegant metrics flag.
*
* @attr ref android.R.styleable#TextView_elegantTextHeight
*/
public void setElegantTextHeight(boolean elegant) {
if (elegant != mTextPaint.isElegantTextHeight()) {
mTextPaint.setElegantTextHeight(elegant);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Gets the text letter-space value, which determines the spacing between characters.
* The value returned is in ems. Normally, this value is 0.0.
* @return The text letter-space value in ems.
*
* @see #setLetterSpacing(float)
* @see Paint#setLetterSpacing
*/
public float getLetterSpacing() {
return mTextPaint.getLetterSpacing();
}
/**
* Sets text letter-spacing in em units. Typical values
* for slight expansion will be around 0.05. Negative values tighten text.
*
* @see #getLetterSpacing()
* @see Paint#getLetterSpacing
*
* @param letterSpacing A text letter-space value in ems.
* @attr ref android.R.styleable#TextView_letterSpacing
*/
@android.view.RemotableViewMethod
public void setLetterSpacing(float letterSpacing) {
if (letterSpacing != mTextPaint.getLetterSpacing()) {
mTextPaint.setLetterSpacing(letterSpacing);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Returns the font feature settings. The format is the same as the CSS
* font-feature-settings attribute:
* <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
* https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
*
* @return the currently set font feature settings. Default is null.
*
* @see #setFontFeatureSettings(String)
* @see Paint#setFontFeatureSettings(String) Paint.setFontFeatureSettings(String)
*/
@Nullable
public String getFontFeatureSettings() {
return mTextPaint.getFontFeatureSettings();
}
/**
* Returns the font variation settings.
*
* @return the currently set font variation settings. Returns null if no variation is
* specified.
*
* @see #setFontVariationSettings(String)
* @see Paint#setFontVariationSettings(String) Paint.setFontVariationSettings(String)
*/
@Nullable
public String getFontVariationSettings() {
return mTextPaint.getFontVariationSettings();
}
/**
* Sets the break strategy for breaking paragraphs into lines. The default value for
* TextView is {@link Layout#BREAK_STRATEGY_HIGH_QUALITY}, and the default value for
* EditText is {@link Layout#BREAK_STRATEGY_SIMPLE}, the latter to avoid the
* text "dancing" when being edited.
*
* @attr ref android.R.styleable#TextView_breakStrategy
* @see #getBreakStrategy()
*/
public void setBreakStrategy(@Layout.BreakStrategy int breakStrategy) {
mBreakStrategy = breakStrategy;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* Gets the current strategy for breaking paragraphs into lines.
* @return the current strategy for breaking paragraphs into lines.
*
* @attr ref android.R.styleable#TextView_breakStrategy
* @see #setBreakStrategy(int)
*/
@Layout.BreakStrategy
public int getBreakStrategy() {
return mBreakStrategy;
}
/**
* Sets the frequency of automatic hyphenation to use when determining word breaks.
* The default value for both TextView and {@link EditText} is
* {@link Layout#HYPHENATION_FREQUENCY_NORMAL}.
* Note that the default hyphenation frequency value is set from the theme.
*
* @param hyphenationFrequency The hyphenation frequency to use.
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @see #getHyphenationFrequency()
*/
public void setHyphenationFrequency(@Layout.HyphenationFrequency int hyphenationFrequency) {
mHyphenationFrequency = hyphenationFrequency;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* Gets the current frequency of automatic hyphenation to be used when determining word breaks.
* @return the current frequency of automatic hyphenation to be used when determining word
* breaks.
*
* @attr ref android.R.styleable#TextView_hyphenationFrequency
* @see #setHyphenationFrequency(int)
*/
@Layout.HyphenationFrequency
public int getHyphenationFrequency() {
return mHyphenationFrequency;
}
/**
* Set justification mode. The default value is {@link Layout#JUSTIFICATION_MODE_NONE}. If the
* last line is too short for justification, the last line will be displayed with the
* alignment set by {@link android.view.View#setTextAlignment}.
*
* @see #getJustificationMode()
*/
@Layout.JustificationMode
public void setJustificationMode(@Layout.JustificationMode int justificationMode) {
mJustificationMode = justificationMode;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
/**
* @return true if currently paragraph justification mode.
*
* @see #setJustificationMode(int)
*/
public @Layout.JustificationMode int getJustificationMode() {
return mJustificationMode;
}
/**
* Sets font feature settings. The format is the same as the CSS
* font-feature-settings attribute:
* <a href="https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop">
* https://www.w3.org/TR/css-fonts-3/#font-feature-settings-prop</a>
*
* @param fontFeatureSettings font feature settings represented as CSS compatible string
*
* @see #getFontFeatureSettings()
* @see Paint#getFontFeatureSettings() Paint.getFontFeatureSettings()
*
* @attr ref android.R.styleable#TextView_fontFeatureSettings
*/
@android.view.RemotableViewMethod
public void setFontFeatureSettings(@Nullable String fontFeatureSettings) {
if (fontFeatureSettings != mTextPaint.getFontFeatureSettings()) {
mTextPaint.setFontFeatureSettings(fontFeatureSettings);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Sets TrueType or OpenType font variation settings. The settings string is constructed from
* multiple pairs of axis tag and style values. The axis tag must contain four ASCII characters
* and must be wrapped with single quotes (U+0027) or double quotes (U+0022). Axis strings that
* are longer or shorter than four characters, or contain characters outside of U+0020..U+007E
* are invalid. If a specified axis name is not defined in the font, the settings will be
* ignored.
*
* <p>
* Examples,
* <ul>
* <li>Set font width to 150.
* <pre>
* <code>
* TextView textView = (TextView) findViewById(R.id.textView);
* textView.setFontVariationSettings("'wdth' 150");
* </code>
* </pre>
* </li>
*
* <li>Set the font slant to 20 degrees and ask for italic style.
* <pre>
* <code>
* TextView textView = (TextView) findViewById(R.id.textView);
* textView.setFontVariationSettings("'slnt' 20, 'ital' 1");
* </code>
* </pre>
* </p>
* </li>
* </ul>
*
* @param fontVariationSettings font variation settings. You can pass null or empty string as
* no variation settings.
* @return true if the given settings is effective to at least one font file underlying this
* TextView. This function also returns true for empty settings string. Otherwise
* returns false.
*
* @throws IllegalArgumentException If given string is not a valid font variation settings
* format.
*
* @see #getFontVariationSettings()
* @see FontVariationAxis
*/
public boolean setFontVariationSettings(@Nullable String fontVariationSettings) {
final String existingSettings = mTextPaint.getFontVariationSettings();
if (fontVariationSettings == existingSettings
|| (fontVariationSettings != null
&& fontVariationSettings.equals(existingSettings))) {
return true;
}
boolean effective = mTextPaint.setFontVariationSettings(fontVariationSettings);
if (effective && mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
return effective;
}
/**
重载setTextColor方法
设置文本颜色
* Sets the text color for all the states (normal, selected,
* focused) to be this color.
*
* @param color A color value in the form 0xAARRGGBB.
* Do not pass a resource ID. To get a color value from a resource ID, call
* {@link android.support.v4.content.ContextCompat#getColor(Context, int) getColor}.
*
* @see #setTextColor(ColorStateList)
* @see #getTextColors()
*
* @attr ref android.R.styleable#TextView_textColor
*/
@android.view.RemotableViewMethod
public void setTextColor(@ColorInt int color) {
mTextColor = ColorStateList.valueOf(color);
updateTextColors();
}
/**
重载setTextColor方法
* Sets the text color.设置文本颜色
*
* @see #setTextColor(int)
* @see #getTextColors()
* @see #setHintTextColor(ColorStateList)
* @see #setLinkTextColor(ColorStateList)
*
* @attr ref android.R.styleable#TextView_textColor
*/
@android.view.RemotableViewMethod
public void setTextColor(ColorStateList colors) {
if (colors == null) {
throw new NullPointerException();
}
mTextColor = colors;
updateTextColors();
}
/**
获取文本的颜色,呃,不是一个是返回一堆(列表)颜色值
* Gets the text colors for the different states (normal, selected, focused) of the TextView.
*
* @see #setTextColor(ColorStateList)
* @see #setTextColor(int)
*
* @attr ref android.R.styleable#TextView_textColor
*/
public final ColorStateList getTextColors() {
return mTextColor;
}
/**
返回当前文本的颜色
* Return the current color selected for normal text.
*
* @return Returns the current text color.
*/
@ColorInt
public final int getCurrentTextColor() {
return mCurTextColor;
}
/**
* Sets the color used to display the selection highlight.
*
* @attr ref android.R.styleable#TextView_textColorHighlight
*/
@android.view.RemotableViewMethod
public void setHighlightColor(@ColorInt int color) {
if (mHighlightColor != color) {
mHighlightColor = color;
invalidate();
}
}
/**
* @return the color used to display the selection highlight
*
* @see #setHighlightColor(int)
*
* @attr ref android.R.styleable#TextView_textColorHighlight
*/
@ColorInt
public int getHighlightColor() {
return mHighlightColor;
}
/**
* Sets whether the soft input method will be made visible when this
* TextView gets focused. The default is true.
*/
@android.view.RemotableViewMethod
public final void setShowSoftInputOnFocus(boolean show) {
createEditorIfNeeded();
mEditor.mShowSoftInputOnFocus = show;
}
/**
* Returns whether the soft input method will be made visible when this
* TextView gets focused. The default is true.
*/
public final boolean getShowSoftInputOnFocus() {
// When there is no Editor, return default true value
return mEditor == null || mEditor.mShowSoftInputOnFocus;
}
/**
* Gives the text a shadow of the specified blur radius and color, the specified
* distance from its drawn position.
* <p>
* The text shadow produced does not interact with the properties on view
* that are responsible for real time shadows,
* {@link View#getElevation() elevation} and
* {@link View#getTranslationZ() translationZ}.
*
* @see Paint#setShadowLayer(float, float, float, int)
*
* @attr ref android.R.styleable#TextView_shadowColor
* @attr ref android.R.styleable#TextView_shadowDx
* @attr ref android.R.styleable#TextView_shadowDy
* @attr ref android.R.styleable#TextView_shadowRadius
*/
public void setShadowLayer(float radius, float dx, float dy, int color) {
mTextPaint.setShadowLayer(radius, dx, dy, color);
mShadowRadius = radius;
mShadowDx = dx;
mShadowDy = dy;
mShadowColor = color;
// Will change text clip region
if (mEditor != null) {
mEditor.invalidateTextDisplayList();
mEditor.invalidateHandlesAndActionMode();
}
invalidate();
}
/**
* Gets the radius of the shadow layer.
*
* @return the radius of the shadow layer. If 0, the shadow layer is not visible
*
* @see #setShadowLayer(float, float, float, int)
*
* @attr ref android.R.styleable#TextView_shadowRadius
*/
public float getShadowRadius() {
return mShadowRadius;
}
/**
* @return the horizontal offset of the shadow layer
*
* @see #setShadowLayer(float, float, float, int)
*
* @attr ref android.R.styleable#TextView_shadowDx
*/
public float getShadowDx() {
return mShadowDx;
}
/**
* Gets the vertical offset of the shadow layer.
* @return The vertical offset of the shadow layer.
*
* @see #setShadowLayer(float, float, float, int)
*
* @attr ref android.R.styleable#TextView_shadowDy
*/
public float getShadowDy() {
return mShadowDy;
}
/**
* Gets the color of the shadow layer.
* @return the color of the shadow layer
*
* @see #setShadowLayer(float, float, float, int)
*
* @attr ref android.R.styleable#TextView_shadowColor
*/
@ColorInt
public int getShadowColor() {
return mShadowColor;
}
/**
* Gets the {@link TextPaint} used for the text.
* Use this only to consult the Paint's properties and not to change them.
* @return The base paint used for the text.
*/
public TextPaint getPaint() {
return mTextPaint;
}
/**
* Sets the autolink mask of the text. See {@link
* android.text.util.Linkify#ALL Linkify.ALL} and peers for
* possible values.
*
* @attr ref android.R.styleable#TextView_autoLink
*/
@android.view.RemotableViewMethod
public final void setAutoLinkMask(int mask) {
mAutoLinkMask = mask;
}
/**
* Sets whether the movement method will automatically be set to
* {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
* set to nonzero and links are detected in {@link #setText}.
* The default is true.
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
@android.view.RemotableViewMethod
public final void setLinksClickable(boolean whether) {
mLinksClickable = whether;
}
/**
* Returns whether the movement method will automatically be set to
* {@link LinkMovementMethod} if {@link #setAutoLinkMask} has been
* set to nonzero and links are detected in {@link #setText}.
* The default is true.
*
* @attr ref android.R.styleable#TextView_linksClickable
*/
public final boolean getLinksClickable() {
return mLinksClickable;
}
/**
* Returns the list of {@link android.text.style.URLSpan URLSpans} attached to the text
* (by {@link Linkify} or otherwise) if any. You can call
* {@link URLSpan#getURL} on them to find where they link to
* or use {@link Spanned#getSpanStart} and {@link Spanned#getSpanEnd}
* to find the region of the text they are attached to.
*/
public URLSpan[] getUrls() {
if (mText instanceof Spanned) {
return ((Spanned) mText).getSpans(0, mText.length(), URLSpan.class);
} else {
return new URLSpan[0];
}
}
/**
* Sets the color of the hint text for all the states (disabled, focussed, selected...) of this
* TextView.
*
* @see #setHintTextColor(ColorStateList)
* @see #getHintTextColors()
* @see #setTextColor(int)
*
* @attr ref android.R.styleable#TextView_textColorHint
*/
@android.view.RemotableViewMethod
public final void setHintTextColor(@ColorInt int color) {
mHintTextColor = ColorStateList.valueOf(color);
updateTextColors();
}
/**
* Sets the color of the hint text.
*
* @see #getHintTextColors()
* @see #setHintTextColor(int)
* @see #setTextColor(ColorStateList)
* @see #setLinkTextColor(ColorStateList)
*
* @attr ref android.R.styleable#TextView_textColorHint
*/
public final void setHintTextColor(ColorStateList colors) {
mHintTextColor = colors;
updateTextColors();
}
/**
* @return the color of the hint text, for the different states of this TextView.
*
* @see #setHintTextColor(ColorStateList)
* @see #setHintTextColor(int)
* @see #setTextColor(ColorStateList)
* @see #setLinkTextColor(ColorStateList)
*
* @attr ref android.R.styleable#TextView_textColorHint
*/
public final ColorStateList getHintTextColors() {
return mHintTextColor;
}
/**
* <p>Return the current color selected to paint the hint text.</p>
*
* @return Returns the current hint text color.
*/
@ColorInt
public final int getCurrentHintTextColor() {
return mHintTextColor != null ? mCurHintTextColor : mCurTextColor;
}
/**
* Sets the color of links in the text.
*
* @see #setLinkTextColor(ColorStateList)
* @see #getLinkTextColors()
*
* @attr ref android.R.styleable#TextView_textColorLink
*/
@android.view.RemotableViewMethod
public final void setLinkTextColor(@ColorInt int color) {
mLinkTextColor = ColorStateList.valueOf(color);
updateTextColors();
}
/**
* Sets the color of links in the text.
*
* @see #setLinkTextColor(int)
* @see #getLinkTextColors()
* @see #setTextColor(ColorStateList)
* @see #setHintTextColor(ColorStateList)
*
* @attr ref android.R.styleable#TextView_textColorLink
*/
public final void setLinkTextColor(ColorStateList colors) {
mLinkTextColor = colors;
updateTextColors();
}
/**
* @return the list of colors used to paint the links in the text, for the different states of
* this TextView
*
* @see #setLinkTextColor(ColorStateList)
* @see #setLinkTextColor(int)
*
* @attr ref android.R.styleable#TextView_textColorLink
*/
public final ColorStateList getLinkTextColors() {
return mLinkTextColor;
}
/**
* Sets the horizontal alignment of the text and the
* vertical gravity that will be used when there is extra space
* in the TextView beyond what is required for the text itself.
*
* @see android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
public void setGravity(int gravity) {
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.START;
}
if ((gravity & Gravity.VERTICAL_GRAVITY_MASK) == 0) {
gravity |= Gravity.TOP;
}
boolean newLayout = false;
if ((gravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)
!= (mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK)) {
newLayout = true;
}
if (gravity != mGravity) {
invalidate();
}
mGravity = gravity;
if (mLayout != null && newLayout) {
// XXX this is heavy-handed because no actual content changes.
int want = mLayout.getWidth();
int hintWant = mHintLayout == null ? 0 : mHintLayout.getWidth();
makeNewLayout(want, hintWant, UNKNOWN_BORING, UNKNOWN_BORING,
mRight - mLeft - getCompoundPaddingLeft() - getCompoundPaddingRight(), true);
}
}
/**
* Returns the horizontal and vertical alignment of this TextView.
*
* @see android.view.Gravity
* @attr ref android.R.styleable#TextView_gravity
*/
public int getGravity() {
return mGravity;
}
/**
* Gets the flags on the Paint being used to display the text.
* @return The flags on the Paint being used to display the text.
* @see Paint#getFlags
*/
public int getPaintFlags() {
return mTextPaint.getFlags();
}
/**
* Sets flags on the Paint being used to display the text and
* reflows the text if they are different from the old flags.
* @see Paint#setFlags
*/
@android.view.RemotableViewMethod
public void setPaintFlags(int flags) {
if (mTextPaint.getFlags() != flags) {
mTextPaint.setFlags(flags);
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Sets whether the text should be allowed to be wider than the
* View is. If false, it will be wrapped to the width of the View.
*
* @attr ref android.R.styleable#TextView_scrollHorizontally
*/
public void setHorizontallyScrolling(boolean whether) {
if (mHorizontallyScrolling != whether) {
mHorizontallyScrolling = whether;
if (mLayout != null) {
nullLayouts();
requestLayout();
invalidate();
}
}
}
/**
* Returns whether the text is allowed to be wider than the View is.
* If false, the text will be wrapped to the width of the View.
*
* @attr ref android.R.styleable#TextView_scrollHorizontally
* @hide
*/
public boolean getHorizontallyScrolling() {
return mHorizontallyScrolling;
}
/**
* Sets the height of the TextView to be at least {@code minLines} tall.
* <p>
* This value is used for height calculation if LayoutParams does not force TextView to have an
* exact height. Setting this value overrides other previous minimum height configurations such
* as {@link #setMinHeight(int)} or {@link #setHeight(int)}. {@link #setSingleLine()} will set
* this value to 1.
*
* @param minLines the minimum height of TextView in terms of number of lines
*
* @see #getMinLines()
* @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_minLines
*/
@android.view.RemotableViewMethod
public void setMinLines(int minLines) {
mMinimum = minLines;
mMinMode = LINES;
requestLayout();
invalidate();
}
/**
* Returns the minimum height of TextView in terms of number of lines or -1 if the minimum
* height was set using {@link #setMinHeight(int)} or {@link #setHeight(int)}.
*
* @return the minimum height of TextView in terms of number of lines or -1 if the minimum
* height is not defined in lines
*
* @see #setMinLines(int)
* @see #setLines(int)
*
* @attr ref android.R.styleable#TextView_minLines
*/
public int getMinLines() {
return mMinMode == LINES ? mMinimum : -1;
}
/**
* Sets the height of the TextView to be at least {@code minPixels} tall.
* <p>
* This value is used for height calculation if LayoutParams does not force TextView to have an
* exact height. Setting this value overrides previous minimum height configurations such as
* {@link #setMinLines(int)} or {@link #setLines(int)}.
* <p>
* The value given here is different than {@link #setMinimumHeight(int)}. Between
* {@code minHeight} and the value set in {@link #setMinimumHeight(int)}, the greater one is
* used to decide the final height.
*
* @param minPixels the minimum height of TextView in terms of pixels
*
* @see #getMinHeight()
* @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_minHeight
*/
@android.view.RemotableViewMethod
public void setMinHeight(int minPixels) {
mMinimum = minPixels;
mMinMode = PIXELS;
requestLayout();
invalidate();
}
/**
* Returns the minimum height of TextView in terms of pixels or -1 if the minimum height was
* set using {@link #setMinLines(int)} or {@link #setLines(int)}.
*
* @return the minimum height of TextView in terms of pixels or -1 if the minimum height is not
* defined in pixels
*
* @see #setMinHeight(int)
* @see #setHeight(int)
*
* @attr ref android.R.styleable#TextView_minHeight
*/
public int getMinHeight() {
return mMinMode == PIXELS ? mMinimum : -1;
}