[3] 什么时候重写viewGroup的generateLayoutParam
3.1 源码
@UiThread
public abstract class ViewGroup extends View implements ViewParent, ViewManager {
....
....
/**
* Returns a new set of layout parameters based on the supplied attributes set.
*
* @param attrs the attributes to build the layout parameters from
*
* @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
* of its descendants
*/
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LayoutParams(getContext(), attrs);
}
/**
* Returns a safe set of layout parameters based on the supplied layout params.
* When a ViewGroup is passed a View whose layout params do not pass the test of
* {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
* is invoked. This method should return a new set of layout params suitable for
* this ViewGroup, possibly by copying the appropriate attributes from the
* specified set of layout params.
*
* @param p The layout parameters to convert into a suitable set of layout parameters
* for this ViewGroup.
*
* @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
* of its descendants
*/
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
return p;
}
/**
* Returns a set of default layout parameters. These parameters are requested
* when the View passed to {@link #addView(View)} has no layout parameters
* already set. If null is returned, an exception is thrown from addView.
*
* @return a set of default layout parameters or null
*/
protected LayoutParams generateDefaultLayoutParams() {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
}
....
3.2 generateDefaultLayoutParams 调用位置
3.2.1 viewGroupHelp
/**
* Helper class for connecting the public API to an updatable implementation.
*
* @see ViewGroupProvider
*
* @hide
*/
public abstract class ViewGroupHelper<T extends ViewGroupProvider> extends ViewGroup {
/** @hide */
final public T mProvider;
/** @hide */
public ViewGroupHelper(ProviderCreator<T> creator,
Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
mProvider = creator.createProvider(this, new SuperProvider(),
new PrivateProvider());
}
...
...
@Override
public LayoutParams generateDefaultLayoutParams_impl() {
return ViewGroupHelper.super.generateDefaultLayoutParams();
}
@Override
public LayoutParams generateLayoutParams_impl(AttributeSet attrs) {
return ViewGroupHelper.super.generateLayoutParams(attrs);
}
@Override
public LayoutParams generateLayoutParams_impl(LayoutParams lp) {
return ViewGroupHelper.super.generateLayoutParams(lp);
}
...
...
3.2.1 LayoutInflater
/**
* Instantiates a layout XML file into its corresponding {@link android.view.View}
* objects. It is never used directly. Instead, use
* {@link android.app.Activity#getLayoutInflater()} or
* {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
* that is already hooked up to the current context and correctly configured
* for the device you are running on.
*
* <p>
* To create a new LayoutInflater with an additional {@link Factory} for your
* own views, you can use {@link #cloneInContext} to clone an existing
* ViewFactory, and then call {@link #setFactory} on it to include your
* Factory.
*
* <p>
* For performance reasons, view inflation relies heavily on pre-processing of
* XML files that is done at build time. Therefore, it is not currently possible
* to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
* it only works with an XmlPullParser returned from a compiled resource
* (R.<em>something</em> file.)
*/
@SystemService(Context.LAYOUT_INFLATER_SERVICE)
public abstract class LayoutInflater {
private static final String TAG = LayoutInflater.class.getSimpleName();
private static final boolean DEBUG = false;
/** Empty stack trace used to avoid log spam in re-throw exceptions. */
private static final StackTraceElement[] EMPTY_STACK_TRACE = new StackTraceElement[0];
/**
* This field should be made private, so it is hidden from the SDK.
* {@hide}
*/
protected final Context mContext;
// these are optional, set by the caller
private boolean mFactorySet;
private Factory mFactory;
private Factory2 mFactory2;
private Factory2 mPrivateFactory;
private Filter mFilter;
final Object[] mConstructorArgs = new Object[2];
...
...
/**
* Recursive method used to descend down the xml hierarchy and instantiate
* views, instantiate their children, and then call onFinishInflate().
* <p>
* <strong>Note:</strong> Default visibility so the BridgeInflater can
* override it.
*/
void rInflate(XmlPullParser parser, View parent, Context context,
AttributeSet attrs, boolean finishInflate) throws XmlPullParserException, IOException {
final int depth = parser.getDepth();
int type;
boolean pendingRequestFocus = false;
while (((type = parser.next()) != XmlPullParser.END_TAG ||
parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
if (type != XmlPullParser.START_TAG) {
continue;
}
final String name = parser.getName();
if (TAG_REQUEST_FOCUS.equals(name)) {
pendingRequestFocus = true;
consumeChildElements(parser);
} else if (TAG_TAG.equals(name)) {
parseViewTag(parser, parent, attrs);
} else if (TAG_INCLUDE.equals(name)) {
if (parser.getDepth() == 0) {
throw new InflateException("<include /> cannot be the root element");
}
parseInclude(parser, context, parent, attrs); /// 看这里 跳转下一个方法 (下一个方法又会调用generateLayoutParams)
} else if (TAG_MERGE.equals(name)) {
throw new InflateException("<merge /> must be the root element");
} else {
final View view = createViewFromTag(parent, name, context, attrs);
final ViewGroup viewGroup = (ViewGroup) parent;
final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs); ///看这里啊
rInflateChildren(parser, view, attrs, true);
viewGroup.addView(view, params);
}
}
if (pendingRequestFocus) {
parent.restoreDefaultFocus();
}
if (finishInflate) {
parent.onFinishInflate();
}
}
...
...
private void parseInclude(XmlPullParser parser, Context context, View parent,
AttributeSet attrs) throws XmlPullParserException, IOException {
int type;
if (parent instanceof ViewGroup) {
// Apply a theme wrapper, if requested. This is sort of a weird
// edge case, since developers think the <include> overwrites
// values in the AttributeSet of the included View. So, if the
// included View has a theme attribute, we'll need to ignore it.
final TypedArray ta = context.obtainStyledAttributes(attrs, ATTRS_THEME);
final int themeResId = ta.getResourceId(0, 0);
final boolean hasThemeOverride = themeResId != 0;
if (hasThemeOverride) {
context = new ContextThemeWrapper(context, themeResId);
}
ta.recycle();
// If the layout is pointing to a theme attribute, we have to
// massage the value to get a resource identifier out of it.
int layout = attrs.getAttributeResourceValue(null, ATTR_LAYOUT, 0);
if (layout == 0) {
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
if (value == null || value.length() <= 0) {
throw new InflateException("You must specify a layout in the"
+ " include tag: <include layout=\"@layout/layoutID\" />");
}
// Attempt to resolve the "?attr/name" string to an attribute
// within the default (e.g. application) package.
layout = context.getResources().getIdentifier(
value.substring(1), "attr", context.getPackageName());
}
// The layout might be referencing a theme attribute.
if (mTempValue == null) {
mTempValue = new TypedValue();
}
if (layout != 0 && context.getTheme().resolveAttribute(layout, mTempValue, true)) {
layout = mTempValue.resourceId;
}
if (layout == 0) {
final String value = attrs.getAttributeValue(null, ATTR_LAYOUT);
throw new InflateException("You must specify a valid layout "
+ "reference. The layout ID " + value + " is not valid.");
} else {
final XmlResourceParser childParser = context.getResources().getLayout(layout);
try {
final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
while ((type = childParser.next()) != XmlPullParser.START_TAG &&
type != XmlPullParser.END_DOCUMENT) {
// Empty.
}
if (type != XmlPullParser.START_TAG) {
throw new InflateException(childParser.getPositionDescription() +
": No start tag found!");
}
final String childName = childParser.getName();
if (TAG_MERGE.equals(childName)) {
// The <merge> tag doesn't support android:theme, so
// nothing special to do here.
rInflate(childParser, parent, context, childAttrs, false);
} else {
final View view = createViewFromTag(parent, childName,
context, childAttrs, hasThemeOverride);
final ViewGroup group = (ViewGroup) parent;
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.Include);
final int id = a.getResourceId(R.styleable.Include_id, View.NO_ID);
final int visibility = a.getInt(R.styleable.Include_visibility, -1);
a.recycle();
// We try to load the layout params set in the <include /> tag.
// If the parent can't generate layout params (ex. missing width
// or height for the framework ViewGroups, though this is not
// necessarily true of all ViewGroups) then we expect it to throw
// a runtime exception.
// We catch this exception and set localParams accordingly: true
// means we successfully loaded layout params from the <include>
// tag, false means we need to rely on the included layout params.
ViewGroup.LayoutParams params = null;
try {
params = group.generateLayoutParams(attrs);///看这里啊
} catch (RuntimeException e) {
// Ignore, just fail over to child attrs.
}
if (params == null) {
params = group.generateLayoutParams(childAttrs);//看这里啊
}
view.setLayoutParams(params);
// Inflate all children.
rInflateChildren(childParser, view, childAttrs, true);
if (id != View.NO_ID) {
view.setId(id);
}
switch (visibility) {
case 0:
view.setVisibility(View.VISIBLE);
break;
case 1:
view.setVisibility(View.INVISIBLE);
break;
case 2:
view.setVisibility(View.GONE);
break;
}
group.addView(view);
}
} finally {
childParser.close();
}
}
} else {
throw new InflateException("<include /> can only be used inside of a ViewGroup");
}
LayoutInflater.consumeChildElements(parser);
}
...
...
3.3 看下generateLayoutParam的具体实现
/**
* A layout that arranges other views either horizontally in a single column
* or vertically in a single row.
*
* <p>The following snippet shows how to include a linear layout in your layout XML file:</p>
*
* <pre><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
* android:layout_width="match_parent"
* android:layout_height="match_parent"
* android:paddingLeft="16dp"
* android:paddingRight="16dp"
* android:orientation="horizontal"
* android:gravity="center">
*
* <!-- Include other widget or layout tags here. These are considered
* "child views" or "children" of the linear layout -->
*
* </LinearLayout></pre>
*
* <p>Set {@link android.R.styleable#LinearLayout_orientation android:orientation} to specify
* whether child views are displayed in a row or column.</p>
*
* <p>To control how linear layout aligns all the views it contains, set a value for
* {@link android.R.styleable#LinearLayout_gravity android:gravity}. For example, the
* snippet above sets android:gravity to "center". The value you set affects
* both horizontal and vertical alignment of all child views within the single row or column.</p>
*
* <p>You can set
* {@link android.R.styleable#LinearLayout_Layout_layout_weight android:layout_weight}
* on individual child views to specify how linear layout divides remaining space amongst
* the views it contains. See the
* <a href="https://developer.android.com/guide/topics/ui/layout/linear.html">Linear Layout</a>
* guide for an example.</p>
*
* <p>See
* {@link android.widget.LinearLayout.LayoutParams LinearLayout.LayoutParams}
* to learn about other attributes you can set on a child view to affect its
* position and size in the containing linear layout.</p>
*
* @attr ref android.R.styleable#LinearLayout_baselineAligned
* @attr ref android.R.styleable#LinearLayout_baselineAlignedChildIndex
* @attr ref android.R.styleable#LinearLayout_gravity
* @attr ref android.R.styleable#LinearLayout_measureWithLargestChild
* @attr ref android.R.styleable#LinearLayout_orientation
* @attr ref android.R.styleable#LinearLayout_weightSum
*/
@RemoteView
public class LinearLayout extends ViewGroup {
...
...
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new LinearLayout.LayoutParams(getContext(), attrs);
}
/**
* Returns a set of layout parameters with a width of
* {@link android.view.ViewGroup.LayoutParams#MATCH_PARENT}
* and a height of {@link android.view.ViewGroup.LayoutParams#WRAP_CONTENT}
* when the layout's orientation is {@link #VERTICAL}. When the orientation is
* {@link #HORIZONTAL}, the width is set to {@link LayoutParams#WRAP_CONTENT}
* and the height to {@link LayoutParams#WRAP_CONTENT}.
*/
@Override
protected LayoutParams generateDefaultLayoutParams() {
if (mOrientation == HORIZONTAL) {
return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
} else if (mOrientation == VERTICAL) {
return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
}
return null;
}
@Override
protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams lp) {
if (sPreserveMarginParamsInLayoutParamConversion) {
if (lp instanceof LayoutParams) {
return new LayoutParams((LayoutParams) lp);
} else if (lp instanceof MarginLayoutParams) {
return new LayoutParams((MarginLayoutParams) lp);
}
}
return new LayoutParams(lp);
}
...
...
再看下
/**
* {@inheritDoc}
*/
public LayoutParams(Context c, AttributeSet attrs) {
super(c, attrs);
TypedArray a =
c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
a.recycle();
}
网友评论