2019-10-20 [M3.什么时候重写viewGroup的generateLayoutParam]

[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>&lt;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"&gt;
*
*   &lt;!-- Include other widget or layout tags here. These are considered
*           "child views" or "children" of the linear layout --&gt;
*
* &lt;/LinearLayout&gt;</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();
       }

3.3 结论:

3.31viewGroup的generateLayoutParam方法是 LayoutInflate填充布局时调用

3.32 自定义ViewGroup可能需要重写generateLayoutparams

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271

推荐阅读更多精彩内容