一些琐碎的随手笔记
- canvas.drawBitmap()直接画图片,可以用matrix
- canvas.drawPath()直接按路径画
- canvas.drawXXX()直接画图形,可以用Shader加渲染效果,如渐变,和附加图片渲染BitmapShader
- canvas.clipPath()按路径裁剪画布
- new一个canvas时注意传入一个bimap实例,但是这个bitmap应该是未绘制的
关于自定义View的四个构造函数
1.view(context) new一个view时调用
/**
* Simple constructor to use when creating a view from code.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
*/
2.view(context,attributeset) 从xml加载时调用
/**
* Constructor that is called when inflating a view *from XML. This is called
* when a view is being constructed from an XML file, *supplying attributes
* that were specified in the XML file. This version *uses a default style of
* 0, so the only attribute values applied are those *in the Context's Theme
* and the given AttributeSet.
*
* <p>
* The method onFinishInflate() will be called after *all children have been
* added.
*
* @param context The Context the view is running in, *through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is *inflating the view.
* @see #View(Context, AttributeSet, int)
*/
3. view(context,attributeset,defStyle),不会被系统第一个调用
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute. This constructor of View allows subclasses to use their
* own base style when they are inflating. For example, a Button class's
* constructor would call this version of the super class constructor and
* supply <code>R.attr.buttonStyle</code> for <var>defStyleAttr</var>; this
* allows the theme's button style to modify all of the base view attributes
* (in particular its background) as well as the Button class's attributes.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @see #View(Context, AttributeSet)
*/
4.view(context,attributeset,defstyle,defStyleRes)不会被第一个调用
/**
* Perform inflation from XML and apply a class-specific base style from a
* theme attribute or style resource. This constructor of View allows
* subclasses to use their own base style when they are inflating.
* <p>
* When determining the final value of a particular attribute, there are
* four inputs that come into play:
* <ol>
* <li>Any attribute values in the given AttributeSet.
* <li>The style resource specified in the AttributeSet (named "style").
* <li>The default style specified by <var>defStyleAttr</var>.
* <li>The default style specified by <var>defStyleRes</var>.
* <li>The base values in this theme.
* </ol>
* <p>
* Each of these inputs is considered in-order, with the first listed taking
* precedence over the following ones. In other words, if in the
* AttributeSet you have supplied <code><Button * textColor="#ff000000"></code>
* , then the button's text will <em>always</em> be black, regardless of
* what is specified in any of the styles.
*
* @param context The Context the view is running in, through which it can
* access the current theme, resources, etc.
* @param attrs The attributes of the XML tag that is inflating the view.
* @param defStyleAttr An attribute in the current theme that contains a
* reference to a style resource that supplies default values for
* the view. Can be 0 to not look for defaults.
* @param defStyleRes A resource identifier of a style resource that
* supplies default values for the view, used only if
* defStyleAttr is 0 or can not be found in the theme. Can be 0
* to not look for defaults.
* @see #View(Context, AttributeSet, int)
*/
super(context, attrs, defStyleAttr);
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView); 从属性集查询属性
{
this(context);
final TypedArray a = context.obtainStyledAttributes(
attrs, com.android.internal.R.styleable.View, defStyleAttr, defStyleRes);
if (mDebugViewAttributes) {
saveAttributeData(attrs, a);
}
Drawable background = null;
int leftPadding = -1;
int topPadding = -1;
int rightPadding = -1;
int bottomPadding = -1;
int startPadding = UNDEFINED_PADDING;
int endPadding = UNDEFINED_PADDING;
int padding = -1;
int paddingHorizontal = -1;
int paddingVertical = -1;
int viewFlagValues = 0;
int viewFlagMasks = 0;
boolean setScrollContainer = false;
int x = 0;
int y = 0;
float tx = 0;
float ty = 0;
float tz = 0;
float elevation = 0;
float rotation = 0;
float rotationX = 0;
float rotationY = 0;
float sx = 1f;
float sy = 1f;
boolean transformSet = false;
int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
int overScrollMode = mOverScrollMode;
boolean initializeScrollbars = false;
boolean initializeScrollIndicators = false;
boolean startPaddingDefined = false;
boolean endPaddingDefined = false;
boolean leftPaddingDefined = false;
boolean rightPaddingDefined = false;
final int targetSdkVersion = context.getApplicationInfo().targetSdkVersion;
// Set default values.
viewFlagValues |= FOCUSABLE_AUTO;
viewFlagMasks |= FOCUSABLE_AUTO;
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case com.android.internal.R.styleable.View_background:
background = a.getDrawable(attr);
break;
case com.android.internal.R.styleable.View_padding:
padding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
leftPaddingDefined = true;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingHorizontal:
paddingHorizontal = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = paddingHorizontal;
mUserPaddingRightInitial = paddingHorizontal;
leftPaddingDefined = true;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingVertical:
paddingVertical = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingLeft:
leftPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingLeftInitial = leftPadding;
leftPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingTop:
topPadding = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingRight:
rightPadding = a.getDimensionPixelSize(attr, -1);
mUserPaddingRightInitial = rightPadding;
rightPaddingDefined = true;
break;
case com.android.internal.R.styleable.View_paddingBottom:
bottomPadding = a.getDimensionPixelSize(attr, -1);
break;
case com.android.internal.R.styleable.View_paddingStart:
startPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING);
startPaddingDefined = (startPadding != UNDEFINED_PADDING);
break;
case com.android.internal.R.styleable.View_paddingEnd:
endPadding = a.getDimensionPixelSize(attr, UNDEFINED_PADDING);
endPaddingDefined = (endPadding != UNDEFINED_PADDING);
break;
case com.android.internal.R.styleable.View_scrollX:
x = a.getDimensionPixelOffset(attr, 0);
break;
case com.android.internal.R.styleable.View_scrollY:
y = a.getDimensionPixelOffset(attr, 0);
break;
case com.android.internal.R.styleable.View_alpha:
setAlpha(a.getFloat(attr, 1f));
break;
case com.android.internal.R.styleable.View_transformPivotX:
setPivotX(a.getDimension(attr, 0));
break;
case com.android.internal.R.styleable.View_transformPivotY:
setPivotY(a.getDimension(attr, 0));
break;
case com.android.internal.R.styleable.View_translationX:
tx = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_translationY:
ty = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_translationZ:
tz = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_elevation:
elevation = a.getDimension(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotation:
rotation = a.getFloat(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotationX:
rotationX = a.getFloat(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_rotationY:
rotationY = a.getFloat(attr, 0);
transformSet = true;
break;
case com.android.internal.R.styleable.View_scaleX:
sx = a.getFloat(attr, 1f);
transformSet = true;
break;
case com.android.internal.R.styleable.View_scaleY:
sy = a.getFloat(attr, 1f);
transformSet = true;
break;
case com.android.internal.R.styleable.View_id:
mID = a.getResourceId(attr, NO_ID);
break;
case com.android.internal.R.styleable.View_tag:
mTag = a.getText(attr);
break;
case com.android.internal.R.styleable.View_fitsSystemWindows:
if (a.getBoolean(attr, false)) {
viewFlagValues |= FITS_SYSTEM_WINDOWS;
viewFlagMasks |= FITS_SYSTEM_WINDOWS;
}
break;
case com.android.internal.R.styleable.View_focusable:
viewFlagValues = (viewFlagValues & ~FOCUSABLE_MASK) | getFocusableAttribute(a);
if ((viewFlagValues & FOCUSABLE_AUTO) == 0) {
viewFlagMasks |= FOCUSABLE_MASK;
}
break;
case com.android.internal.R.styleable.View_focusableInTouchMode:
if (a.getBoolean(attr, false)) {
// unset auto focus since focusableInTouchMode implies explicit focusable
viewFlagValues &= ~FOCUSABLE_AUTO;
viewFlagValues |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE;
viewFlagMasks |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE_MASK;
}
break;
case com.android.internal.R.styleable.View_clickable:
if (a.getBoolean(attr, false)) {
viewFlagValues |= CLICKABLE;
viewFlagMasks |= CLICKABLE;
}
break;
case com.android.internal.R.styleable.View_longClickable:
if (a.getBoolean(attr, false)) {
viewFlagValues |= LONG_CLICKABLE;
viewFlagMasks |= LONG_CLICKABLE;
}
break;
case com.android.internal.R.styleable.View_contextClickable:
if (a.getBoolean(attr, false)) {
viewFlagValues |= CONTEXT_CLICKABLE;
viewFlagMasks |= CONTEXT_CLICKABLE;
}
break;
case com.android.internal.R.styleable.View_saveEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues |= SAVE_DISABLED;
viewFlagMasks |= SAVE_DISABLED_MASK;
}
break;
case com.android.internal.R.styleable.View_duplicateParentState:
if (a.getBoolean(attr, false)) {
viewFlagValues |= DUPLICATE_PARENT_STATE;
viewFlagMasks |= DUPLICATE_PARENT_STATE;
}
break;
case com.android.internal.R.styleable.View_visibility:
final int visibility = a.getInt(attr, 0);
if (visibility != 0) {
viewFlagValues |= VISIBILITY_FLAGS[visibility];
viewFlagMasks |= VISIBILITY_MASK;
}
break;
case com.android.internal.R.styleable.View_layoutDirection:
// Clear any layout direction flags (included resolved bits) already set
mPrivateFlags2 &=
~(PFLAG2_LAYOUT_DIRECTION_MASK | PFLAG2_LAYOUT_DIRECTION_RESOLVED_MASK);
// Set the layout direction flags depending on the value of the attribute
final int layoutDirection = a.getInt(attr, -1);
final int value = (layoutDirection != -1) ?
LAYOUT_DIRECTION_FLAGS[layoutDirection] : LAYOUT_DIRECTION_DEFAULT;
mPrivateFlags2 |= (value << PFLAG2_LAYOUT_DIRECTION_MASK_SHIFT);
break;
case com.android.internal.R.styleable.View_drawingCacheQuality:
final int cacheQuality = a.getInt(attr, 0);
if (cacheQuality != 0) {
viewFlagValues |= DRAWING_CACHE_QUALITY_FLAGS[cacheQuality];
viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK;
}
break;
case com.android.internal.R.styleable.View_contentDescription:
setContentDescription(a.getString(attr));
break;
case com.android.internal.R.styleable.View_accessibilityTraversalBefore:
setAccessibilityTraversalBefore(a.getResourceId(attr, NO_ID));
break;
case com.android.internal.R.styleable.View_accessibilityTraversalAfter:
setAccessibilityTraversalAfter(a.getResourceId(attr, NO_ID));
break;
case com.android.internal.R.styleable.View_labelFor:
setLabelFor(a.getResourceId(attr, NO_ID));
break;
case com.android.internal.R.styleable.View_soundEffectsEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
viewFlagMasks |= SOUND_EFFECTS_ENABLED;
}
break;
case com.android.internal.R.styleable.View_hapticFeedbackEnabled:
if (!a.getBoolean(attr, true)) {
viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED;
viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED;
}
break;
case R.styleable.View_scrollbars:
final int scrollbars = a.getInt(attr, SCROLLBARS_NONE);
if (scrollbars != SCROLLBARS_NONE) {
viewFlagValues |= scrollbars;
viewFlagMasks |= SCROLLBARS_MASK;
initializeScrollbars = true;
}
break;
//noinspection deprecation
case R.styleable.View_fadingEdge:
if (targetSdkVersion >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// Ignore the attribute starting with ICS
break;
}
// With builds < ICS, fall through and apply fading edges
case R.styleable.View_requiresFadingEdge:
final int fadingEdge = a.getInt(attr, FADING_EDGE_NONE);
if (fadingEdge != FADING_EDGE_NONE) {
viewFlagValues |= fadingEdge;
viewFlagMasks |= FADING_EDGE_MASK;
initializeFadingEdgeInternal(a);
}
break;
case R.styleable.View_scrollbarStyle:
scrollbarStyle = a.getInt(attr, SCROLLBARS_INSIDE_OVERLAY);
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
viewFlagValues |= scrollbarStyle & SCROLLBARS_STYLE_MASK;
viewFlagMasks |= SCROLLBARS_STYLE_MASK;
}
break;
case R.styleable.View_isScrollContainer:
setScrollContainer = true;
if (a.getBoolean(attr, false)) {
setScrollContainer(true);
}
break;
case com.android.internal.R.styleable.View_keepScreenOn:
if (a.getBoolean(attr, false)) {
viewFlagValues |= KEEP_SCREEN_ON;
viewFlagMasks |= KEEP_SCREEN_ON;
}
break;
case R.styleable.View_filterTouchesWhenObscured:
if (a.getBoolean(attr, false)) {
viewFlagValues |= FILTER_TOUCHES_WHEN_OBSCURED;
viewFlagMasks |= FILTER_TOUCHES_WHEN_OBSCURED;
}
break;
case R.styleable.View_nextFocusLeft:
mNextFocusLeftId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusRight:
mNextFocusRightId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusUp:
mNextFocusUpId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusDown:
mNextFocusDownId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextFocusForward:
mNextFocusForwardId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_nextClusterForward:
mNextClusterForwardId = a.getResourceId(attr, View.NO_ID);
break;
case R.styleable.View_minWidth:
mMinWidth = a.getDimensionPixelSize(attr, 0);
break;
case R.styleable.View_minHeight:
mMinHeight = a.getDimensionPixelSize(attr, 0);
break;
case R.styleable.View_onClick:
if (context.isRestricted()) {
throw new IllegalStateException("The android:onClick attribute cannot "
+ "be used within a restricted context");
}
final String handlerName = a.getString(attr);
if (handlerName != null) {
setOnClickListener(new DeclaredOnClickListener(this, handlerName));
}
break;
case R.styleable.View_overScrollMode:
overScrollMode = a.getInt(attr, OVER_SCROLL_IF_CONTENT_SCROLLS);
break;
case R.styleable.View_verticalScrollbarPosition:
mVerticalScrollbarPosition = a.getInt(attr, SCROLLBAR_POSITION_DEFAULT);
break;
case R.styleable.View_layerType:
setLayerType(a.getInt(attr, LAYER_TYPE_NONE), null);
break;
case R.styleable.View_textDirection:
// Clear any text direction flag already set
mPrivateFlags2 &= ~PFLAG2_TEXT_DIRECTION_MASK;
// Set the text direction flags depending on the value of the attribute
final int textDirection = a.getInt(attr, -1);
if (textDirection != -1) {
mPrivateFlags2 |= PFLAG2_TEXT_DIRECTION_FLAGS[textDirection];
}
break;
case R.styleable.View_textAlignment:
// Clear any text alignment flag already set
mPrivateFlags2 &= ~PFLAG2_TEXT_ALIGNMENT_MASK;
// Set the text alignment flag depending on the value of the attribute
final int textAlignment = a.getInt(attr, TEXT_ALIGNMENT_DEFAULT);
mPrivateFlags2 |= PFLAG2_TEXT_ALIGNMENT_FLAGS[textAlignment];
break;
case R.styleable.View_importantForAccessibility:
setImportantForAccessibility(a.getInt(attr,
IMPORTANT_FOR_ACCESSIBILITY_DEFAULT));
break;
case R.styleable.View_accessibilityLiveRegion:
setAccessibilityLiveRegion(a.getInt(attr, ACCESSIBILITY_LIVE_REGION_DEFAULT));
break;
case R.styleable.View_transitionName:
setTransitionName(a.getString(attr));
break;
case R.styleable.View_nestedScrollingEnabled:
setNestedScrollingEnabled(a.getBoolean(attr, false));
break;
case R.styleable.View_stateListAnimator:
setStateListAnimator(AnimatorInflater.loadStateListAnimator(context,
a.getResourceId(attr, 0)));
break;
case R.styleable.View_backgroundTint:
// This will get applied later during setBackground().
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintList = a.getColorStateList(
R.styleable.View_backgroundTint);
mBackgroundTint.mHasTintList = true;
break;
case R.styleable.View_backgroundTintMode:
// This will get applied later during setBackground().
if (mBackgroundTint == null) {
mBackgroundTint = new TintInfo();
}
mBackgroundTint.mTintMode = Drawable.parseTintMode(a.getInt(
R.styleable.View_backgroundTintMode, -1), null);
mBackgroundTint.mHasTintMode = true;
break;
case R.styleable.View_outlineProvider:
setOutlineProviderFromAttribute(a.getInt(R.styleable.View_outlineProvider,
PROVIDER_BACKGROUND));
break;
case R.styleable.View_foreground:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForeground(a.getDrawable(attr));
}
break;
case R.styleable.View_foregroundGravity:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForegroundGravity(a.getInt(attr, Gravity.NO_GRAVITY));
}
break;
case R.styleable.View_foregroundTintMode:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForegroundTintMode(Drawable.parseTintMode(a.getInt(attr, -1), null));
}
break;
case R.styleable.View_foregroundTint:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
setForegroundTintList(a.getColorStateList(attr));
}
break;
case R.styleable.View_foregroundInsidePadding:
if (targetSdkVersion >= Build.VERSION_CODES.M || this instanceof FrameLayout) {
if (mForegroundInfo == null) {
mForegroundInfo = new ForegroundInfo();
}
mForegroundInfo.mInsidePadding = a.getBoolean(attr,
mForegroundInfo.mInsidePadding);
}
break;
case R.styleable.View_scrollIndicators:
final int scrollIndicators =
(a.getInt(attr, 0) << SCROLL_INDICATORS_TO_PFLAGS3_LSHIFT)
& SCROLL_INDICATORS_PFLAG3_MASK;
if (scrollIndicators != 0) {
mPrivateFlags3 |= scrollIndicators;
initializeScrollIndicators = true;
}
break;
case R.styleable.View_pointerIcon:
final int resourceId = a.getResourceId(attr, 0);
if (resourceId != 0) {
setPointerIcon(PointerIcon.load(
context.getResources(), resourceId));
} else {
final int pointerType = a.getInt(attr, PointerIcon.TYPE_NOT_SPECIFIED);
if (pointerType != PointerIcon.TYPE_NOT_SPECIFIED) {
setPointerIcon(PointerIcon.getSystemIcon(context, pointerType));
}
}
break;
case R.styleable.View_forceHasOverlappingRendering:
if (a.peekValue(attr) != null) {
forceHasOverlappingRendering(a.getBoolean(attr, true));
}
break;
case R.styleable.View_tooltipText:
setTooltipText(a.getText(attr));
break;
case R.styleable.View_keyboardNavigationCluster:
if (a.peekValue(attr) != null) {
setKeyboardNavigationCluster(a.getBoolean(attr, true));
}
break;
case R.styleable.View_focusedByDefault:
if (a.peekValue(attr) != null) {
setFocusedByDefault(a.getBoolean(attr, true));
}
break;
case R.styleable.View_autofillHints:
if (a.peekValue(attr) != null) {
CharSequence[] rawHints = null;
String rawString = null;
if (a.getType(attr) == TypedValue.TYPE_REFERENCE) {
int resId = a.getResourceId(attr, 0);
try {
rawHints = a.getTextArray(attr);
} catch (Resources.NotFoundException e) {
rawString = getResources().getString(resId);
}
} else {
rawString = a.getString(attr);
}
if (rawHints == null) {
if (rawString == null) {
throw new IllegalArgumentException(
"Could not resolve autofillHints");
} else {
rawHints = rawString.split(",");
}
}
String[] hints = new String[rawHints.length];
int numHints = rawHints.length;
for (int rawHintNum = 0; rawHintNum < numHints; rawHintNum++) {
hints[rawHintNum] = rawHints[rawHintNum].toString().trim();
}
setAutofillHints(hints);
}
break;
case R.styleable.View_importantForAutofill:
if (a.peekValue(attr) != null) {
setImportantForAutofill(a.getInt(attr, IMPORTANT_FOR_AUTOFILL_AUTO));
}
break;
case R.styleable.View_defaultFocusHighlightEnabled:
if (a.peekValue(attr) != null) {
setDefaultFocusHighlightEnabled(a.getBoolean(attr, true));
}
break;
}
}
setOverScrollMode(overScrollMode);
// Cache start/end user padding as we cannot fully resolve padding here (we dont have yet
// the resolved layout direction). Those cached values will be used later during padding
// resolution.
mUserPaddingStart = startPadding;
mUserPaddingEnd = endPadding;
if (background != null) {
setBackground(background);
}
// setBackground above will record that padding is currently provided by the background.
// If we have padding specified via xml, record that here instead and use it.
mLeftPaddingDefined = leftPaddingDefined;
mRightPaddingDefined = rightPaddingDefined;
if (padding >= 0) {
leftPadding = padding;
topPadding = padding;
rightPadding = padding;
bottomPadding = padding;
mUserPaddingLeftInitial = padding;
mUserPaddingRightInitial = padding;
} else {
if (paddingHorizontal >= 0) {
leftPadding = paddingHorizontal;
rightPadding = paddingHorizontal;
mUserPaddingLeftInitial = paddingHorizontal;
mUserPaddingRightInitial = paddingHorizontal;
}
if (paddingVertical >= 0) {
topPadding = paddingVertical;
bottomPadding = paddingVertical;
}
}
if (isRtlCompatibilityMode()) {
// RTL compatibility mode: pre Jelly Bean MR1 case OR no RTL support case.
// left / right padding are used if defined (meaning here nothing to do). If they are not
// defined and start / end padding are defined (e.g. in Frameworks resources), then we use
// start / end and resolve them as left / right (layout direction is not taken into account).
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
if (!mLeftPaddingDefined && startPaddingDefined) {
leftPadding = startPadding;
}
mUserPaddingLeftInitial = (leftPadding >= 0) ? leftPadding : mUserPaddingLeftInitial;
if (!mRightPaddingDefined && endPaddingDefined) {
rightPadding = endPadding;
}
mUserPaddingRightInitial = (rightPadding >= 0) ? rightPadding : mUserPaddingRightInitial;
} else {
// Jelly Bean MR1 and after case: if start/end defined, they will override any left/right
// values defined. Otherwise, left /right values are used.
// Padding from the background drawable is stored at this point in mUserPaddingLeftInitial
// and mUserPaddingRightInitial) so drawable padding will be used as ultimate default if
// defined.
final boolean hasRelativePadding = startPaddingDefined || endPaddingDefined;
if (mLeftPaddingDefined && !hasRelativePadding) {
mUserPaddingLeftInitial = leftPadding;
}
if (mRightPaddingDefined && !hasRelativePadding) {
mUserPaddingRightInitial = rightPadding;
}
}
internalSetPadding(
mUserPaddingLeftInitial,
topPadding >= 0 ? topPadding : mPaddingTop,
mUserPaddingRightInitial,
bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
if (viewFlagMasks != 0) {
setFlags(viewFlagValues, viewFlagMasks);
}
if (initializeScrollbars) {
initializeScrollbarsInternal(a);
}
if (initializeScrollIndicators) {
initializeScrollIndicatorsInternal();
}
a.recycle();
// Needs to be called after mViewFlags is set
if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
recomputePadding();
}
if (x != 0 || y != 0) {
scrollTo(x, y);
}
if (transformSet) {
setTranslationX(tx);
setTranslationY(ty);
setTranslationZ(tz);
setElevation(elevation);
setRotation(rotation);
setRotationX(rotationX);
setRotationY(rotationY);
setScaleX(sx);
setScaleY(sy);
}
if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) {
setScrollContainer(true);
}
computeOpaqueFlags();
}
总结,View的实例化有两套机制4个方法。当new一个View时,调用第一个构造函数,完成View的基本配置,当然,属性的具体赋值需要代码动态指定。当xml加载View,时,调用第二构造函数,第二构造函数依次逐级调用到第四个构造函数,顶层View的,后面两个参数都是0,代表不查询Them中的属性值和没有指定style布局。当自定义View时,最终调用第四个构造函数,里面先调用第一个View(context),然后theme.obtainStyleAttribute(4p)依次查询属性集里面所有属性在xml,xml指定style,theme指定style和构造函数指定style中的值,然后重新设置,将无法识别的自定义属性的值留给用户处理。用户在自定义View时调用顶View构造函数后便可再次theme.obtainStyleAttribute(2p)查询属性值,自行配置。自定义一般只要写至少两个构造函数,第一个可为XXView(context){super(context)}或者XXView(context){
super(context,null)}最终殊途同归,其他构造方法可以按需要的查询属性值使用
。