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对象,这个方法会被调用,但是什么时候调用,我们在接下来的文章中接着分析。