简单研究Android View绘制二 LayoutParams

2015-07-28 17:23:20

本篇是关于LayoutParams相关

ViewGroup.LayoutParams文档解释如下:

LayoutParams are used by views to tell their parents how they want to be laid out. See ViewGroup Layout Attributes for a list of all child view attributes that this class supports.

The base LayoutParams class just describes how big the view wants to be for both width and height. For each dimension, it can specify one of:

  • FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which means that the view wants to be as big as its parent (minus padding)
  • WRAP_CONTENT, which means that the view wants to be just big enough to enclose its content (plus padding)
  • an exact number

There are subclasses of LayoutParams for different subclasses of ViewGroup. For example, AbsoluteLayout has its own subclass of LayoutParams which adds an X and Y value.

翻译过来,大意是:LayoutParams经常用于view告诉它的父控件如何摆放它。此父类只申明了view想要多大的宽和高,值为FILL_PARENT、WRAP_CONTENT或者一个精确值。ViewGroup的子类大都有自己的、继承自该类的子类,比如AbsoluteLayout有自己的LayoutParams子类,并且多了x和y两个属性。

OK,我们至少明白一点,那就是LayoutParams是view用来告诉他的父控件如何摆放他的属性集合,比如宽、高、是否居中等。所以LayoutParams被设计为ViewGroup的内部类,而且每一个继承自ViewGroup的子类,都会继承并重写ViewGroup的LayoutParams类,毕竟每个布局类实现的效果不一样,所使用的参数、属性等也就不一样,所以需要重写。比如LineatLayout中可以使用weight,但是RelativeLayout中并无此属性。

现在来看看ViewGroup中LayoutParams的源码:

  1 /**
  2      * LayoutParams are used by views to tell their parents how they want to be
  3      * laid out. See
  4      * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
  5      * for a list of all child view attributes that this class supports.
  6      *
  7      * <p>
  8      * The base LayoutParams class just describes how big the view wants to be
  9      * for both width and height. For each dimension, it can specify one of:
 10      * <ul>
 11      * <li>FILL_PARENT (renamed MATCH_PARENT in API Level 8 and higher), which
 12      * means that the view wants to be as big as its parent (minus padding)
 13      * <li> WRAP_CONTENT, which means that the view wants to be just big enough
 14      * to enclose its content (plus padding)
 15      * <li> an exact number
 16      * </ul>
 17      * There are subclasses of LayoutParams for different subclasses of
 18      * ViewGroup. For example, AbsoluteLayout has its own subclass of
 19      * LayoutParams which adds an X and Y value.</p>
 20      *
 21      * <div class="special reference">
 22      * <h3>Developer Guides</h3>
 23      * <p>For more information about creating user interface layouts, read the
 24      * <a href="{@docRoot}guide/topics/ui/declaring-layout.html">XML Layouts</a> developer
 25      * guide.</p></div>
 26      *
 27      * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
 28      * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
 29      */
 30     public static class LayoutParams {
 31         /**
 32          * Special value for the height or width requested by a View.
 33          * FILL_PARENT means that the view wants to be as big as its parent,
 34          * minus the parent‘s padding, if any. This value is deprecated
 35          * starting in API Level 8 and replaced by {@link #MATCH_PARENT}.
 36          */
 37         @SuppressWarnings({"UnusedDeclaration"})
 38         @Deprecated
 39         public static final int FILL_PARENT = -1;
 40
 41         /**
 42          * Special value for the height or width requested by a View.
 43          * MATCH_PARENT means that the view wants to be as big as its parent,
 44          * minus the parent‘s padding, if any. Introduced in API Level 8.
 45          */
 46         public static final int MATCH_PARENT = -1;
 47
 48         /**
 49          * Special value for the height or width requested by a View.
 50          * WRAP_CONTENT means that the view wants to be just large enough to fit
 51          * its own internal content, taking its own padding into account.
 52          */
 53         public static final int WRAP_CONTENT = -2;
 54
 55         /**
 56          * Information about how wide the view wants to be. Can be one of the
 57          * constants FILL_PARENT (replaced by MATCH_PARENT ,
 58          * in API Level 8) or WRAP_CONTENT. or an exact size.
 59          */
 60         @ViewDebug.ExportedProperty(category = "layout", mapping = {
 61             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
 62             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
 63         })
 64         public int width;
 65
 66         /**
 67          * Information about how tall the view wants to be. Can be one of the
 68          * constants FILL_PARENT (replaced by MATCH_PARENT ,
 69          * in API Level 8) or WRAP_CONTENT. or an exact size.
 70          */
 71         @ViewDebug.ExportedProperty(category = "layout", mapping = {
 72             @ViewDebug.IntToString(from = MATCH_PARENT, to = "MATCH_PARENT"),
 73             @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
 74         })
 75         public int height;
 76
 77         /**
 78          * Used to animate layouts.
 79          */
 80         public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
 81
 82         /**
 83          * Creates a new set of layout parameters. The values are extracted from
 84          * the supplied attributes set and context. The XML attributes mapped
 85          * to this set of layout parameters are:
 86          *
 87          * <ul>
 88          *   <li><code>layout_width</code>: the width, either an exact value,
 89          *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
 90          *   {@link #MATCH_PARENT} in API Level 8)</li>
 91          *   <li><code>layout_height</code>: the height, either an exact value,
 92          *   {@link #WRAP_CONTENT}, or {@link #FILL_PARENT} (replaced by
 93          *   {@link #MATCH_PARENT} in API Level 8)</li>
 94          * </ul>
 95          *
 96          * @param c the application environment
 97          * @param attrs the set of attributes from which to extract the layout
 98          *              parameters‘ values
 99          */
100         public LayoutParams(Context c, AttributeSet attrs) {
101             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
102             setBaseAttributes(a,
103                     R.styleable.ViewGroup_Layout_layout_width,
104                     R.styleable.ViewGroup_Layout_layout_height);
105             a.recycle();
106         }
107
108         /**
109          * Creates a new set of layout parameters with the specified width
110          * and height.
111          *
112          * @param width the width, either {@link #WRAP_CONTENT},
113          *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
114          *        API Level 8), or a fixed size in pixels
115          * @param height the height, either {@link #WRAP_CONTENT},
116          *        {@link #FILL_PARENT} (replaced by {@link #MATCH_PARENT} in
117          *        API Level 8), or a fixed size in pixels
118          */
119         public LayoutParams(int width, int height) {
120             this.width = width;
121             this.height = height;
122         }
123
124         /**
125          * Copy constructor. Clones the width and height values of the source.
126          *
127          * @param source The layout params to copy from.
128          */
129         public LayoutParams(LayoutParams source) {
130             this.width = source.width;
131             this.height = source.height;
132         }
133
134         /**
135          * Used internally by MarginLayoutParams.
136          * @hide
137          */
138         LayoutParams() {
139         }
140
141         /**
142          * Extracts the layout parameters from the supplied attributes.
143          *
144          * @param a the style attributes to extract the parameters from
145          * @param widthAttr the identifier of the width attribute
146          * @param heightAttr the identifier of the height attribute
147          */
148         protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
149             width = a.getLayoutDimension(widthAttr, "layout_width");
150             height = a.getLayoutDimension(heightAttr, "layout_height");
151         }
152
153         /**
154          * Resolve layout parameters depending on the layout direction. Subclasses that care about
155          * layoutDirection changes should override this method. The default implementation does
156          * nothing.
157          *
158          * @param layoutDirection the direction of the layout
159          *
160          * {@link View#LAYOUT_DIRECTION_LTR}
161          * {@link View#LAYOUT_DIRECTION_RTL}
162          */
163         public void resolveLayoutDirection(int layoutDirection) {
164         }
165
166         /**
167          * Returns a String representation of this set of layout parameters.
168          *
169          * @param output the String to prepend to the internal representation
170          * @return a String with the following format: output +
171          *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
172          *
173          * @hide
174          */
175         public String debug(String output) {
176             return output + "ViewGroup.LayoutParams={ width="
177                     + sizeToString(width) + ", height=" + sizeToString(height) + " }";
178         }
179
180         /**
181          * Use {@code canvas} to draw suitable debugging annotations for these LayoutParameters.
182          *
183          * @param view the view that contains these layout parameters
184          * @param canvas the canvas on which to draw
185          *
186          * @hide
187          */
188         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
189         }
190
191         /**
192          * Converts the specified size to a readable String.
193          *
194          * @param size the size to convert
195          * @return a String instance representing the supplied size
196          *
197          * @hide
198          */
199         protected static String sizeToString(int size) {
200             if (size == WRAP_CONTENT) {
201                 return "wrap-content";
202             }
203             if (size == MATCH_PARENT) {
204                 return "match-parent";
205             }
206             return String.valueOf(size);
207         }
208     }

可以看到,在所有LayoutParams的祖宗类ViewGroup.LayoutParams中只定义了两个最最基本的属性:width和height,这两个属性是在哪定义的呢?R.styleable.ViewGroup_Layout_layout_width和R.styleable.ViewGroup_Layout_layout_height,它们是在frameworks/base/core/res/res/values/attrs.xml中定义的,这和我们自定义View时添加自己属性的方式是一样的。

其实ViewGroup中并不只提供了这一种LayoutParams,还有一个叫MarginLayoutParams,源码如下:

  1 /**
  2      * Per-child layout information for layouts that support margins.
  3      * See
  4      * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
  5      * for a list of all child view attributes that this class supports.
  6      */
  7     public static class MarginLayoutParams extends ViewGroup.LayoutParams {
  8         /**
  9          * The left margin in pixels of the child.
 10          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 11          * to this field.
 12          */
 13         @ViewDebug.ExportedProperty(category = "layout")
 14         public int leftMargin;
 15
 16         /**
 17          * The top margin in pixels of the child.
 18          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 19          * to this field.
 20          */
 21         @ViewDebug.ExportedProperty(category = "layout")
 22         public int topMargin;
 23
 24         /**
 25          * The right margin in pixels of the child.
 26          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 27          * to this field.
 28          */
 29         @ViewDebug.ExportedProperty(category = "layout")
 30         public int rightMargin;
 31
 32         /**
 33          * The bottom margin in pixels of the child.
 34          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 35          * to this field.
 36          */
 37         @ViewDebug.ExportedProperty(category = "layout")
 38         public int bottomMargin;
 39
 40         /**
 41          * The start margin in pixels of the child.
 42          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 43          * to this field.
 44          */
 45         @ViewDebug.ExportedProperty(category = "layout")
 46         private int startMargin = DEFAULT_MARGIN_RELATIVE;
 47
 48         /**
 49          * The end margin in pixels of the child.
 50          * Call {@link ViewGroup#setLayoutParams(LayoutParams)} after reassigning a new value
 51          * to this field.
 52          */
 53         @ViewDebug.ExportedProperty(category = "layout")
 54         private int endMargin = DEFAULT_MARGIN_RELATIVE;
 55
 56         /**
 57          * The default start and end margin.
 58          * @hide
 59          */
 60         public static final int DEFAULT_MARGIN_RELATIVE = Integer.MIN_VALUE;
 61
 62         /**
 63          * Bit  0: layout direction
 64          * Bit  1: layout direction
 65          * Bit  2: left margin undefined
 66          * Bit  3: right margin undefined
 67          * Bit  4: is RTL compatibility mode
 68          * Bit  5: need resolution
 69          *
 70          * Bit 6 to 7 not used
 71          *
 72          * @hide
 73          */
 74         @ViewDebug.ExportedProperty(category = "layout", flagMapping = {
 75                 @ViewDebug.FlagToString(mask = LAYOUT_DIRECTION_MASK,
 76                         equals = LAYOUT_DIRECTION_MASK, name = "LAYOUT_DIRECTION"),
 77                 @ViewDebug.FlagToString(mask = LEFT_MARGIN_UNDEFINED_MASK,
 78                         equals = LEFT_MARGIN_UNDEFINED_MASK, name = "LEFT_MARGIN_UNDEFINED_MASK"),
 79                 @ViewDebug.FlagToString(mask = RIGHT_MARGIN_UNDEFINED_MASK,
 80                         equals = RIGHT_MARGIN_UNDEFINED_MASK, name = "RIGHT_MARGIN_UNDEFINED_MASK"),
 81                 @ViewDebug.FlagToString(mask = RTL_COMPATIBILITY_MODE_MASK,
 82                         equals = RTL_COMPATIBILITY_MODE_MASK, name = "RTL_COMPATIBILITY_MODE_MASK"),
 83                 @ViewDebug.FlagToString(mask = NEED_RESOLUTION_MASK,
 84                         equals = NEED_RESOLUTION_MASK, name = "NEED_RESOLUTION_MASK")
 85         })
 86         byte mMarginFlags;
 87
 88         private static final int LAYOUT_DIRECTION_MASK = 0x00000003;
 89         private static final int LEFT_MARGIN_UNDEFINED_MASK = 0x00000004;
 90         private static final int RIGHT_MARGIN_UNDEFINED_MASK = 0x00000008;
 91         private static final int RTL_COMPATIBILITY_MODE_MASK = 0x00000010;
 92         private static final int NEED_RESOLUTION_MASK = 0x00000020;
 93
 94         private static final int DEFAULT_MARGIN_RESOLVED = 0;
 95         private static final int UNDEFINED_MARGIN = DEFAULT_MARGIN_RELATIVE;
 96
 97         /**
 98          * Creates a new set of layout parameters. The values are extracted from
 99          * the supplied attributes set and context.
100          *
101          * @param c the application environment
102          * @param attrs the set of attributes from which to extract the layout
103          *              parameters‘ values
104          */
105         public MarginLayoutParams(Context c, AttributeSet attrs) {
106             super();
107
108             TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
109             setBaseAttributes(a,
110                     R.styleable.ViewGroup_MarginLayout_layout_width,
111                     R.styleable.ViewGroup_MarginLayout_layout_height);
112
113             int margin = a.getDimensionPixelSize(
114                     com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
115             if (margin >= 0) {
116                 leftMargin = margin;
117                 topMargin = margin;
118                 rightMargin= margin;
119                 bottomMargin = margin;
120             } else {
121                 leftMargin = a.getDimensionPixelSize(
122                         R.styleable.ViewGroup_MarginLayout_layout_marginLeft,
123                         UNDEFINED_MARGIN);
124                 if (leftMargin == UNDEFINED_MARGIN) {
125                     mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
126                     leftMargin = DEFAULT_MARGIN_RESOLVED;
127                 }
128                 rightMargin = a.getDimensionPixelSize(
129                         R.styleable.ViewGroup_MarginLayout_layout_marginRight,
130                         UNDEFINED_MARGIN);
131                 if (rightMargin == UNDEFINED_MARGIN) {
132                     mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
133                     rightMargin = DEFAULT_MARGIN_RESOLVED;
134                 }
135
136                 topMargin = a.getDimensionPixelSize(
137                         R.styleable.ViewGroup_MarginLayout_layout_marginTop,
138                         DEFAULT_MARGIN_RESOLVED);
139                 bottomMargin = a.getDimensionPixelSize(
140                         R.styleable.ViewGroup_MarginLayout_layout_marginBottom,
141                         DEFAULT_MARGIN_RESOLVED);
142
143                 startMargin = a.getDimensionPixelSize(
144                         R.styleable.ViewGroup_MarginLayout_layout_marginStart,
145                         DEFAULT_MARGIN_RELATIVE);
146                 endMargin = a.getDimensionPixelSize(
147                         R.styleable.ViewGroup_MarginLayout_layout_marginEnd,
148                         DEFAULT_MARGIN_RELATIVE);
149
150                 if (isMarginRelative()) {
151                    mMarginFlags |= NEED_RESOLUTION_MASK;
152                 }
153             }
154
155             final boolean hasRtlSupport = c.getApplicationInfo().hasRtlSupport();
156             final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
157             if (targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport) {
158                 mMarginFlags |= RTL_COMPATIBILITY_MODE_MASK;
159             }
160
161             // Layout direction is LTR by default
162             mMarginFlags |= LAYOUT_DIRECTION_LTR;
163
164             a.recycle();
165         }
166
167         /**
168          * {@inheritDoc}
169          */
170         public MarginLayoutParams(int width, int height) {
171             super(width, height);
172
173             mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
174             mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
175
176             mMarginFlags &= ~NEED_RESOLUTION_MASK;
177             mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
178         }
179
180         /**
181          * Copy constructor. Clones the width, height and margin values of the source.
182          *
183          * @param source The layout params to copy from.
184          */
185         public MarginLayoutParams(MarginLayoutParams source) {
186             this.width = source.width;
187             this.height = source.height;
188
189             this.leftMargin = source.leftMargin;
190             this.topMargin = source.topMargin;
191             this.rightMargin = source.rightMargin;
192             this.bottomMargin = source.bottomMargin;
193             this.startMargin = source.startMargin;
194             this.endMargin = source.endMargin;
195
196             this.mMarginFlags = source.mMarginFlags;
197         }
198
199         /**
200          * {@inheritDoc}
201          */
202         public MarginLayoutParams(LayoutParams source) {
203             super(source);
204
205             mMarginFlags |= LEFT_MARGIN_UNDEFINED_MASK;
206             mMarginFlags |= RIGHT_MARGIN_UNDEFINED_MASK;
207
208             mMarginFlags &= ~NEED_RESOLUTION_MASK;
209             mMarginFlags &= ~RTL_COMPATIBILITY_MODE_MASK;
210         }
211
212         /**
213          * Sets the margins, in pixels. A call to {@link android.view.View#requestLayout()} needs
214          * to be done so that the new margins are taken into account. Left and right margins may be
215          * overriden by {@link android.view.View#requestLayout()} depending on layout direction.
216          *
217          * @param left the left margin size
218          * @param top the top margin size
219          * @param right the right margin size
220          * @param bottom the bottom margin size
221          *
222          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
223          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
224          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
225          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
226          */
227         public void setMargins(int left, int top, int right, int bottom) {
228             leftMargin = left;
229             topMargin = top;
230             rightMargin = right;
231             bottomMargin = bottom;
232             mMarginFlags &= ~LEFT_MARGIN_UNDEFINED_MASK;
233             mMarginFlags &= ~RIGHT_MARGIN_UNDEFINED_MASK;
234             if (isMarginRelative()) {
235                 mMarginFlags |= NEED_RESOLUTION_MASK;
236             } else {
237                 mMarginFlags &= ~NEED_RESOLUTION_MASK;
238             }
239         }
240
241         /**
242          * Sets the relative margins, in pixels. A call to {@link android.view.View#requestLayout()}
243          * needs to be done so that the new relative margins are taken into account. Left and right
244          * margins may be overriden by {@link android.view.View#requestLayout()} depending on layout
245          * direction.
246          *
247          * @param start the start margin size
248          * @param top the top margin size
249          * @param end the right margin size
250          * @param bottom the bottom margin size
251          *
252          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
253          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
254          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
255          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
256          *
257          * @hide
258          */
259         public void setMarginsRelative(int start, int top, int end, int bottom) {
260             startMargin = start;
261             topMargin = top;
262             endMargin = end;
263             bottomMargin = bottom;
264             mMarginFlags |= NEED_RESOLUTION_MASK;
265         }
266
267         /**
268          * Sets the relative start margin.
269          *
270          * @param start the start margin size
271          *
272          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
273          */
274         public void setMarginStart(int start) {
275             startMargin = start;
276             mMarginFlags |= NEED_RESOLUTION_MASK;
277         }
278
279         /**
280          * Returns the start margin in pixels.
281          *
282          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
283          *
284          * @return the start margin in pixels.
285          */
286         public int getMarginStart() {
287             if (startMargin != DEFAULT_MARGIN_RELATIVE) return startMargin;
288             if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
289                 doResolveMargins();
290             }
291             switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
292                 case View.LAYOUT_DIRECTION_RTL:
293                     return rightMargin;
294                 case View.LAYOUT_DIRECTION_LTR:
295                 default:
296                     return leftMargin;
297             }
298         }
299
300         /**
301          * Sets the relative end margin.
302          *
303          * @param end the end margin size
304          *
305          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
306          */
307         public void setMarginEnd(int end) {
308             endMargin = end;
309             mMarginFlags |= NEED_RESOLUTION_MASK;
310         }
311
312         /**
313          * Returns the end margin in pixels.
314          *
315          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
316          *
317          * @return the end margin in pixels.
318          */
319         public int getMarginEnd() {
320             if (endMargin != DEFAULT_MARGIN_RELATIVE) return endMargin;
321             if ((mMarginFlags & NEED_RESOLUTION_MASK) == NEED_RESOLUTION_MASK) {
322                 doResolveMargins();
323             }
324             switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
325                 case View.LAYOUT_DIRECTION_RTL:
326                     return leftMargin;
327                 case View.LAYOUT_DIRECTION_LTR:
328                 default:
329                     return rightMargin;
330             }
331         }
332
333         /**
334          * Check if margins are relative.
335          *
336          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginStart
337          * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginEnd
338          *
339          * @return true if either marginStart or marginEnd has been set.
340          */
341         public boolean isMarginRelative() {
342             return (startMargin != DEFAULT_MARGIN_RELATIVE || endMargin != DEFAULT_MARGIN_RELATIVE);
343         }
344
345         /**
346          * Set the layout direction
347          * @param layoutDirection the layout direction.
348          *        Should be either {@link View#LAYOUT_DIRECTION_LTR}
349          *                     or {@link View#LAYOUT_DIRECTION_RTL}.
350          */
351         public void setLayoutDirection(int layoutDirection) {
352             if (layoutDirection != View.LAYOUT_DIRECTION_LTR &&
353                     layoutDirection != View.LAYOUT_DIRECTION_RTL) return;
354             if (layoutDirection != (mMarginFlags & LAYOUT_DIRECTION_MASK)) {
355                 mMarginFlags &= ~LAYOUT_DIRECTION_MASK;
356                 mMarginFlags |= (layoutDirection & LAYOUT_DIRECTION_MASK);
357                 if (isMarginRelative()) {
358                     mMarginFlags |= NEED_RESOLUTION_MASK;
359                 } else {
360                     mMarginFlags &= ~NEED_RESOLUTION_MASK;
361                 }
362             }
363         }
364
365         /**
366          * Retuns the layout direction. Can be either {@link View#LAYOUT_DIRECTION_LTR} or
367          * {@link View#LAYOUT_DIRECTION_RTL}.
368          *
369          * @return the layout direction.
370          */
371         public int getLayoutDirection() {
372             return (mMarginFlags & LAYOUT_DIRECTION_MASK);
373         }
374
375         /**
376          * This will be called by {@link android.view.View#requestLayout()}. Left and Right margins
377          * may be overridden depending on layout direction.
378          */
379         @Override
380         public void resolveLayoutDirection(int layoutDirection) {
381             setLayoutDirection(layoutDirection);
382
383             // No relative margin or pre JB-MR1 case or no need to resolve, just dont do anything
384             // Will use the left and right margins if no relative margin is defined.
385             if (!isMarginRelative() ||
386                     (mMarginFlags & NEED_RESOLUTION_MASK) != NEED_RESOLUTION_MASK) return;
387
388             // Proceed with resolution
389             doResolveMargins();
390         }
391
392         private void doResolveMargins() {
393             if ((mMarginFlags & RTL_COMPATIBILITY_MODE_MASK) == RTL_COMPATIBILITY_MODE_MASK) {
394                 // if left or right margins are not defined and if we have some start or end margin
395                 // defined then use those start and end margins.
396                 if ((mMarginFlags & LEFT_MARGIN_UNDEFINED_MASK) == LEFT_MARGIN_UNDEFINED_MASK
397                         && startMargin > DEFAULT_MARGIN_RELATIVE) {
398                     leftMargin = startMargin;
399                 }
400                 if ((mMarginFlags & RIGHT_MARGIN_UNDEFINED_MASK) == RIGHT_MARGIN_UNDEFINED_MASK
401                         && endMargin > DEFAULT_MARGIN_RELATIVE) {
402                     rightMargin = endMargin;
403                 }
404             } else {
405                 // We have some relative margins (either the start one or the end one or both). So use
406                 // them and override what has been defined for left and right margins. If either start
407                 // or end margin is not defined, just set it to default "0".
408                 switch(mMarginFlags & LAYOUT_DIRECTION_MASK) {
409                     case View.LAYOUT_DIRECTION_RTL:
410                         leftMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
411                                 endMargin : DEFAULT_MARGIN_RESOLVED;
412                         rightMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
413                                 startMargin : DEFAULT_MARGIN_RESOLVED;
414                         break;
415                     case View.LAYOUT_DIRECTION_LTR:
416                     default:
417                         leftMargin = (startMargin > DEFAULT_MARGIN_RELATIVE) ?
418                                 startMargin : DEFAULT_MARGIN_RESOLVED;
419                         rightMargin = (endMargin > DEFAULT_MARGIN_RELATIVE) ?
420                                 endMargin : DEFAULT_MARGIN_RESOLVED;
421                         break;
422                 }
423             }
424             mMarginFlags &= ~NEED_RESOLUTION_MASK;
425         }
426
427         /**
428          * @hide
429          */
430         public boolean isLayoutRtl() {
431             return ((mMarginFlags & LAYOUT_DIRECTION_MASK) == View.LAYOUT_DIRECTION_RTL);
432         }
433
434         /**
435          * @hide
436          */
437         @Override
438         public void onDebugDraw(View view, Canvas canvas, Paint paint) {
439             Insets oi = isLayoutModeOptical(view.mParent) ? view.getOpticalInsets() : Insets.NONE;
440
441             fillDifference(canvas,
442                     view.getLeft()   + oi.left,
443                     view.getTop()    + oi.top,
444                     view.getRight()  - oi.right,
445                     view.getBottom() - oi.bottom,
446                     leftMargin,
447                     topMargin,
448                     rightMargin,
449                     bottomMargin,
450                     paint);
451         }
452     }

当然,他必须是继承自LayoutParams祖宗类的~MarginLayoutParams添加了好多属性,主要是用来设置margin值的。下面我们以LinearLayout和RelativeLayout为例,来看看它们是怎么定义自己的LayoutParams的。

LinearLayout.java

  1 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
  2         /**
  3          * Indicates how much of the extra space in the LinearLayout will be
  4          * allocated to the view associated with these LayoutParams. Specify
  5          * 0 if the view should not be stretched. Otherwise the extra pixels
  6          * will be pro-rated among all views whose weight is greater than 0.
  7          */
  8         @ViewDebug.ExportedProperty(category = "layout")
  9         public float weight;
 10
 11         /**
 12          * Gravity for the view associated with these LayoutParams.
 13          *
 14          * @see android.view.Gravity
 15          */
 16         @ViewDebug.ExportedProperty(category = "layout", mapping = {
 17             @ViewDebug.IntToString(from =  -1,                       to = "NONE"),
 18             @ViewDebug.IntToString(from = Gravity.NO_GRAVITY,        to = "NONE"),
 19             @ViewDebug.IntToString(from = Gravity.TOP,               to = "TOP"),
 20             @ViewDebug.IntToString(from = Gravity.BOTTOM,            to = "BOTTOM"),
 21             @ViewDebug.IntToString(from = Gravity.LEFT,              to = "LEFT"),
 22             @ViewDebug.IntToString(from = Gravity.RIGHT,             to = "RIGHT"),
 23             @ViewDebug.IntToString(from = Gravity.START,            to = "START"),
 24             @ViewDebug.IntToString(from = Gravity.END,             to = "END"),
 25             @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL,   to = "CENTER_VERTICAL"),
 26             @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL,     to = "FILL_VERTICAL"),
 27             @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"),
 28             @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL,   to = "FILL_HORIZONTAL"),
 29             @ViewDebug.IntToString(from = Gravity.CENTER,            to = "CENTER"),
 30             @ViewDebug.IntToString(from = Gravity.FILL,              to = "FILL")
 31         })
 32         public int gravity = -1;
 33
 34         /**
 35          * {@inheritDoc}
 36          */
 37         public LayoutParams(Context c, AttributeSet attrs) {
 38             super(c, attrs);
 39             TypedArray a =
 40                     c.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LinearLayout_Layout);
 41
 42             weight = a.getFloat(com.android.internal.R.styleable.LinearLayout_Layout_layout_weight, 0);
 43             gravity = a.getInt(com.android.internal.R.styleable.LinearLayout_Layout_layout_gravity, -1);
 44
 45             a.recycle();
 46         }
 47
 48         /**
 49          * {@inheritDoc}
 50          */
 51         public LayoutParams(int width, int height) {
 52             super(width, height);
 53             weight = 0;
 54         }
 55
 56         /**
 57          * Creates a new set of layout parameters with the specified width, height
 58          * and weight.
 59          *
 60          * @param width the width, either {@link #MATCH_PARENT},
 61          *        {@link #WRAP_CONTENT} or a fixed size in pixels
 62          * @param height the height, either {@link #MATCH_PARENT},
 63          *        {@link #WRAP_CONTENT} or a fixed size in pixels
 64          * @param weight the weight
 65          */
 66         public LayoutParams(int width, int height, float weight) {
 67             super(width, height);
 68             this.weight = weight;
 69         }
 70
 71         /**
 72          * {@inheritDoc}
 73          */
 74         public LayoutParams(ViewGroup.LayoutParams p) {
 75             super(p);
 76         }
 77
 78         /**
 79          * {@inheritDoc}
 80          */
 81         public LayoutParams(ViewGroup.MarginLayoutParams source) {
 82             super(source);
 83         }
 84
 85         /**
 86          * Copy constructor. Clones the width, height, margin values, weight,
 87          * and gravity of the source.
 88          *
 89          * @param source The layout params to copy from.
 90          */
 91         public LayoutParams(LayoutParams source) {
 92             super(source);
 93
 94             this.weight = source.weight;
 95             this.gravity = source.gravity;
 96         }
 97
 98         @Override
 99         public String debug(String output) {
100             return output + "LinearLayout.LayoutParams={width=" + sizeToString(width) +
101                     ", height=" + sizeToString(height) + " weight=" + weight +  "}";
102         }
103     }

很显然它继承自ViewGroup.MarginLayoutParams,而且添加了自己独有的几个 属性,比如weight、gravity,同时继承得到了width、height、margin等属性,所以,LinearLayout的子view就可以使用这些属性。

RelativeLayout.java

  1 public static class LayoutParams extends ViewGroup.MarginLayoutParams {
  2         @ViewDebug.ExportedProperty(category = "layout", resolveId = true, indexMapping = {
  3             @ViewDebug.IntToString(from = ABOVE,               to = "above"),
  4             @ViewDebug.IntToString(from = ALIGN_BASELINE,      to = "alignBaseline"),
  5             @ViewDebug.IntToString(from = ALIGN_BOTTOM,        to = "alignBottom"),
  6             @ViewDebug.IntToString(from = ALIGN_LEFT,          to = "alignLeft"),
  7             @ViewDebug.IntToString(from = ALIGN_PARENT_BOTTOM, to = "alignParentBottom"),
  8             @ViewDebug.IntToString(from = ALIGN_PARENT_LEFT,   to = "alignParentLeft"),
  9             @ViewDebug.IntToString(from = ALIGN_PARENT_RIGHT,  to = "alignParentRight"),
 10             @ViewDebug.IntToString(from = ALIGN_PARENT_TOP,    to = "alignParentTop"),
 11             @ViewDebug.IntToString(from = ALIGN_RIGHT,         to = "alignRight"),
 12             @ViewDebug.IntToString(from = ALIGN_TOP,           to = "alignTop"),
 13             @ViewDebug.IntToString(from = BELOW,               to = "below"),
 14             @ViewDebug.IntToString(from = CENTER_HORIZONTAL,   to = "centerHorizontal"),
 15             @ViewDebug.IntToString(from = CENTER_IN_PARENT,    to = "center"),
 16             @ViewDebug.IntToString(from = CENTER_VERTICAL,     to = "centerVertical"),
 17             @ViewDebug.IntToString(from = LEFT_OF,             to = "leftOf"),
 18             @ViewDebug.IntToString(from = RIGHT_OF,            to = "rightOf"),
 19             @ViewDebug.IntToString(from = ALIGN_START,         to = "alignStart"),
 20             @ViewDebug.IntToString(from = ALIGN_END,           to = "alignEnd"),
 21             @ViewDebug.IntToString(from = ALIGN_PARENT_START,  to = "alignParentStart"),
 22             @ViewDebug.IntToString(from = ALIGN_PARENT_END,    to = "alignParentEnd"),
 23             @ViewDebug.IntToString(from = START_OF,            to = "startOf"),
 24             @ViewDebug.IntToString(from = END_OF,              to = "endOf")
 25         }, mapping = {
 26             @ViewDebug.IntToString(from = TRUE, to = "true"),
 27             @ViewDebug.IntToString(from = 0,    to = "false/NO_ID")
 28         })
 29
 30         private int[] mRules = new int[VERB_COUNT];
 31         private int[] mInitialRules = new int[VERB_COUNT];
 32
 33         private int mLeft, mTop, mRight, mBottom;
 34
 35         private int mStart = DEFAULT_MARGIN_RELATIVE;
 36         private int mEnd = DEFAULT_MARGIN_RELATIVE;
 37
 38         private boolean mRulesChanged = false;
 39         private boolean mIsRtlCompatibilityMode = false;
 40
 41         /**
 42          * When true, uses the parent as the anchor if the anchor doesn‘t exist or if
 43          * the anchor‘s visibility is GONE.
 44          */
 45         @ViewDebug.ExportedProperty(category = "layout")
 46         public boolean alignWithParent;
 47
 48         public LayoutParams(Context c, AttributeSet attrs) {
 49             super(c, attrs);
 50
 51             TypedArray a = c.obtainStyledAttributes(attrs,
 52                     com.android.internal.R.styleable.RelativeLayout_Layout);
 53
 54             final int targetSdkVersion = c.getApplicationInfo().targetSdkVersion;
 55             mIsRtlCompatibilityMode = (targetSdkVersion < JELLY_BEAN_MR1 ||
 56                     !c.getApplicationInfo().hasRtlSupport());
 57
 58             final int[] rules = mRules;
 59             //noinspection MismatchedReadAndWriteOfArray
 60             final int[] initialRules = mInitialRules;
 61
 62             final int N = a.getIndexCount();
 63             for (int i = 0; i < N; i++) {
 64                 int attr = a.getIndex(i);
 65                 switch (attr) {
 66                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignWithParentIfMissing:
 67                         alignWithParent = a.getBoolean(attr, false);
 68                         break;
 69                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toLeftOf:
 70                         rules[LEFT_OF] = a.getResourceId(attr, 0);
 71                         break;
 72                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toRightOf:
 73                         rules[RIGHT_OF] = a.getResourceId(attr, 0);
 74                         break;
 75                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_above:
 76                         rules[ABOVE] = a.getResourceId(attr, 0);
 77                         break;
 78                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_below:
 79                         rules[BELOW] = a.getResourceId(attr, 0);
 80                         break;
 81                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBaseline:
 82                         rules[ALIGN_BASELINE] = a.getResourceId(attr, 0);
 83                         break;
 84                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignLeft:
 85                         rules[ALIGN_LEFT] = a.getResourceId(attr, 0);
 86                         break;
 87                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignTop:
 88                         rules[ALIGN_TOP] = a.getResourceId(attr, 0);
 89                         break;
 90                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignRight:
 91                         rules[ALIGN_RIGHT] = a.getResourceId(attr, 0);
 92                         break;
 93                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignBottom:
 94                         rules[ALIGN_BOTTOM] = a.getResourceId(attr, 0);
 95                         break;
 96                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentLeft:
 97                         rules[ALIGN_PARENT_LEFT] = a.getBoolean(attr, false) ? TRUE : 0;
 98                         break;
 99                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentTop:
100                         rules[ALIGN_PARENT_TOP] = a.getBoolean(attr, false) ? TRUE : 0;
101                         break;
102                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentRight:
103                         rules[ALIGN_PARENT_RIGHT] = a.getBoolean(attr, false) ? TRUE : 0;
104                         break;
105                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentBottom:
106                         rules[ALIGN_PARENT_BOTTOM] = a.getBoolean(attr, false) ? TRUE : 0;
107                         break;
108                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerInParent:
109                         rules[CENTER_IN_PARENT] = a.getBoolean(attr, false) ? TRUE : 0;
110                         break;
111                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerHorizontal:
112                         rules[CENTER_HORIZONTAL] = a.getBoolean(attr, false) ? TRUE : 0;
113                         break;
114                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_centerVertical:
115                         rules[CENTER_VERTICAL] = a.getBoolean(attr, false) ? TRUE : 0;
116                        break;
117                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toStartOf:
118                         rules[START_OF] = a.getResourceId(attr, 0);
119                         break;
120                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_toEndOf:
121                         rules[END_OF] = a.getResourceId(attr, 0);
122                         break;
123                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignStart:
124                         rules[ALIGN_START] = a.getResourceId(attr, 0);
125                         break;
126                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignEnd:
127                         rules[ALIGN_END] = a.getResourceId(attr, 0);
128                         break;
129                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentStart:
130                         rules[ALIGN_PARENT_START] = a.getBoolean(attr, false) ? TRUE : 0;
131                         break;
132                     case com.android.internal.R.styleable.RelativeLayout_Layout_layout_alignParentEnd:
133                         rules[ALIGN_PARENT_END] = a.getBoolean(attr, false) ? TRUE : 0;
134                         break;
135                 }
136             }
137             mRulesChanged = true;
138             System.arraycopy(rules, LEFT_OF, initialRules, LEFT_OF, VERB_COUNT);
139
140             a.recycle();
141         }

这段代码没有贴完整,但是我们可以看到,它同样继承自ViewGroup.MarginLayoutParams,而且加入了大量独有的属性。

至此,我们对LayoutParams有了一个大致的概念,至于在我们自己继承ViewGroup时如何使用,应该根据自己的需要,要么继承自ViewGroup.LayoutParams,要么继承自ViewGroup.MaginLayoutParams,我在前面的demo中是这么简单实现的:

 1 @Override
 2     public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
 3         //Log.e(TAG, "generateLayoutParams attrs");
 4         return new MarginLayoutParams(getContext(), attrs);
 5     }
 6
 7     @Override
 8     protected ViewGroup.LayoutParams generateDefaultLayoutParams() {
 9         //Log.e(TAG, "generateDefaultLayoutParams");
10         return new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT);
11     }
12
13     @Override
14     protected boolean checkLayoutParams(LayoutParams p) {
15         return super.checkLayoutParams(p);
16     }
17
18     @Override
19     protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
20         //Log.e(TAG, "generateLayoutParams p");
21         return new MarginLayoutParams(p);
22     }

也就是说,我根本就没重写自己的LayoutParams。所以在generateLayoutParams(AttributeSet attrs)方法中,返回了ViewGroup.MarginLayoutParams对象,这个方法会被调用,但是什么时候调用,我们在接下来的文章中接着分析。

时间: 2024-10-03 22:51:18

简单研究Android View绘制二 LayoutParams的相关文章

简单研究Android View绘制三 布局过程

2015-07-28 17:29:19 这一篇主要看看布局过程 一.布局过程肯定要不可避免的涉及到layout()和onLayout()方法,这两个方法都是定义在View.java中,源码如下: 1 /** 2 * Assign a size and position to a view and all of its 3 * descendants 4 * 5 * <p>This is the second phase of the layout mechanism. 6 * (The fir

简单研究Android View绘制一

2015-07-27 16:52:58 一.如何通过继承ViewGroup来实现自定义View?首先得搞清楚Android时如何绘制View的,参考Android官方文档:How Android Draws Views 以下翻译摘自:http://blog.csdn.net/linghu_java/article/details/23882681,这也是一片好文章,推荐大家看看- When an Activity receives focus, it will be requested to d

Android View绘制及实践

概述 整个View树的绘图流程是在ViewRoot.java类的performTraversals()函数展开的,该函数做的执行过程可简单概况为: - 判断是否需要重新计算视图大小(measure) - 判断是否重新需要安置视图的位置(layout) - 判断是否需要重绘(draw) 其整个流程图如下: 图片来自:Android 开源项目源码解析 公共技术点中的 View 绘制流程 在Android中View的整个生命周期,调用invalidate和requestLayout会触发一系列的方法,

[Android][转]Android View绘制13问13答

转自:http://www.androidchina.net/4458.html 1.view的绘制流程分几步,从哪开始?哪个过程结束以后能看到view? 答:从ViewRoot的performTraversals开始,经过measure,layout,draw 三个流程.draw流程结束以后就可以在屏幕上看到view了. 2.view的测量宽高和实际宽高有区别吗? 答:基本上百分之99的情况下都是可以认为没有区别的.有两种情况,有区别.第一种 就是有的时候会因为某些原因 view会多次测量,那

对于Android View绘制的一些思考

AT_MOST 表示最大是多大. UNSPECIFIED 不确定是多大, 你想多大就多大,我尽量满足你. EXACTLY 就这么大 已经指定了大小 MATCH_PARENT 为什么说MATCH_PARENT 因为 MATCH_PARENT 就是间接说 我要占据父控件剩下的那部分了. 这就相当于指定了确定的宽或高. 一个view 的绘制需要三个阶段 measure -> layout -> draw view的测量阶段 measure-> onMeasure() 我们就看单纯一个view的

Android View绘制机制

------------------------------------------------------------------------------ GitHub:lightSky    微博:    light_sky, 即时分享最新技术,欢迎关注 ------------------------------------------------------------------------------ 前言 该篇文章来自一个开源项目android-open-project-analy

Android View绘制流程

框架分析 在之前的下拉刷新中,小结过触屏消息先到WindowManagerService(Wms)然后顺次传递给ViewRoot(派生自Handler),经decor view到Activity再传递给指定的View,这次整理View的绘制流程,通过源码可知,这个过程应该没有涉及到IPC(或者我没有发现),需要绘制时在UI线程中通过ViewRoot发送一个异步请求消息,然后ViewRoot自己接收并不处理这个消息. 在正式进入View绘制之前,首先需要明确一下Android UI的架构组成,偷图

Android View 绘制过程

Android的View绘制是从根节点(Activity是DecorView)开始,他是一个自上而下的过程.View的绘制经历三个过程:Measure.Layout.Draw.基本流程如下图: performTraversals函数,具体的可以参考一下源代码: 1 private void performTraversals() { 2 final View host = mView; 3 ... 4 host.measure(childWidthMeasureSpec, childHeight

Android View 绘制流程(Draw) 完全解析

前言 前几篇文章,笔者分别讲述了DecorView,measure,layout流程等,接下来将详细分析三大工作流程的最后一个流程--绘制流程.测量流程决定了View的大小,布局流程决定了View的位置,那么绘制流程将决定View的样子,一个View该显示什么由绘制流程完成.以下源码均取自Android API 21. 从performDraw说起 前面几篇文章提到,三大工作流程始于ViewRootImpl#performTraversals,在这个方法内部会分别调用performMeasure