美文网首页
2019-10-20 [M3.什么时候重写viewGroup的

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

作者: 妖怪青同学 | 来源:发表于2019-10-20 16:35 被阅读0次

    [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

    相关文章

      网友评论

          本文标题:2019-10-20 [M3.什么时候重写viewGroup的

          本文链接:https://www.haomeiwen.com/subject/vslamctx.html