\
\
\
LayoutParams是什么?
LayoutParams相当于一个Layout的信息包,它封装了Layout的位置、高、宽等信息。假设在屏幕上一块区域是由一个Layout占领的,如果将一个View添加到一个Layout中,最好告诉Layout用户期望的布局方式,也就是将一个认可的layoutParams传递进去。
可以这样去形容LayoutParams,在象棋的棋盘上,每个棋子都占据一个位置,也就是每个棋子都有一个位置的信息,如这个棋子在4行4列,这里的“4行4列”就是棋子的LayoutParams。
但LayoutParams类也只是简单的描述了宽高,宽和高都可以设置成三种值:
**** 1,一个确定的值;
2,FILL_PARENT,即填满(和父容器一样大小);
3,WRAP_CONTENT,即包裹住组件就好。
决定ViewGroup的LayoutParams,在本例中能够支持margin
使用系统的MarginLayoutParams
<span style="font-size:18px;"> @Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}</span>
onMeasure() 测量:
在ViewGroup中, 是根据测量childview的高度和宽度,以及margin
通过measureChildren( 父容器建议的宽度,父容器建议的高度)测量childView
的高和宽
/**
* 计算所有的chlseView的高度和宽度
* ,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,
* 此行执行完成后,childView的宽和高都已经正确的计算过了
*/
measureChildren(widthMeasureSpec, heightMeasureSpec);
根据ViewGroup的模式,以及childView的高度和宽度,
以及margin 来设置自己的高度和宽度
主要考虑的是mode为wrap_content 也就是MeasureSpec.At_most
根据childview来设置自己的高度和宽度, 也就是width, height.
否则直接是自己测量的父容器建议的值
<span style="white-space:pre"> </span><span style="font-family:Times New Roman;">/**
* 设置父容器的宽度和高度
* 如果是wrap_content设置为我们的值 EXCATLY
* 否则: 直接设置为父容器计算的值 At_MOST
*/
<span style="font-size:18px;"> setMeasuredDimension(
(widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,
(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
);</span></span>
onLayout设置chidView的位置,(设置childView绘制的区域)\
通过 测量的高度和宽度,以及margin,来确定childeview的位置。
\
\
\
\
\
自定义ViewGroup:
package com.example.viewgroupdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
this(context, null);
}
public MyViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
try {
/**
* TODO: 对其所有的 childView进行定位(设置childView的绘制区域)
*
*/
int cCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams marginLayoutParams = null;
/***
* 遍历所有的childView 根据其高宽,以及margin进行布局
*
*/
for (int i = 0; i < cCount; i++) {
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
//childView的四个边
int cl = 0, cr = 0, ct = 0, cb = 0;
switch (i) {
case 0:
cl = marginLayoutParams.leftMargin;
ct = marginLayoutParams.topMargin;
break;
case 1:
cl = getWidth() - cWidth - marginLayoutParams.leftMargin - marginLayoutParams.rightMargin;
ct = marginLayoutParams.topMargin;
break;
case 2:
cl = marginLayoutParams.leftMargin;
ct = getHeight() - marginLayoutParams.bottomMargin - cHeight;
break;
case 3:
cl = getWidth() - cWidth - marginLayoutParams.rightMargin- marginLayoutParams.rightMargin;
ct = getHeight() - cHeight - marginLayoutParams.bottomMargin;
break;
default:
break;
}
cr = cl + cWidth;
cb = ct + cHeight;
childView.layout(cl, ct, cr, cb);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
/**
* TODO:计算所有的childView的测量,获取高度和宽度,
* 并根据childView的计算结果, 设置自己的 宽度和高度
*/
//获取ViewGroup上级容器为其推荐的高度和宽度, 以及设计模式
int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpec = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
/**
* 计算所有的chlseView的高度和宽度
* ,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,
* 此行执行完成后,childView的宽和高都已经正确的计算过了
*/
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
//子view的个数,
int mCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams marginLayoutParams = null;
//计算左边两个childView的高度
int lHeight = 0;
//计算右边两个childView的高度, 最终高度是去lHeight和 rHeight最大值决定;
int rHeight = 0;
//计算上边两个childView的宽度
int tWidth = 0;
//计算下边两个childView的宽度, 最终宽度由tWidth和bWidth最大值决定
int bWidth = 0;
/**
* 根据childView 计算出的宽和高,以及设置的margin计算容器的宽度和高度,
* 主要用于容器是wrap_content
*/
for (int i = 0; i < mCount; i++) {
//获取子view
View childView = getChildAt(i);
//获取childView测量的宽度和高度
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
//通过子view 获取MarginlayoutParams
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
//遍历四个childView
//上边两个宽度
if(i==0 || i==1){
tWidth += cWidth +
marginLayoutParams.leftMargin +
marginLayoutParams.rightMargin;
}
//下边两个宽度
if(i==2 || i==3){
bWidth += cWidth +
marginLayoutParams.leftMargin +
marginLayoutParams.rightMargin;
}
//左边两个的高度
if(i == 0 || i == 2){
lHeight += cHeight +
marginLayoutParams.topMargin
+marginLayoutParams.bottomMargin;
}
//右边两个的高度
if(i == 1 || i == 3){
rHeight += cHeight +
marginLayoutParams.topMargin
+marginLayoutParams.bottomMargin;
}
//计算ViewGroup的高度和宽度
width = Math.max(tWidth, bWidth);
height = Math.max(lHeight, rHeight);
/**
* 设置父容器的宽度和高度
* 如果是wrap_content设置为我们的值 EXCATLY
* 否则: 直接设置为父容器计算的值 At_MOST
*/
setMeasuredDimension(
(widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,
(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
布局:
<com.example.viewgroupdemo.MyViewGroup
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#AA333333" >
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#FF4444"
android:gravity="center"
android:text="0"
android:textColor="#FFFFFF"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#00ff00"
android:gravity="center"
android:text="1"
android:textColor="#FFFFFF"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#ff0000"
android:gravity="center"
android:text="2"
android:textColor="#FFFFFF"
android:textSize="22sp"
android:textStyle="bold" />
<TextView
android:layout_width="50dp"
android:layout_height="50dp"
android:background="#0000ff"
android:gravity="center"
android:text="3"
android:textColor="#FFFFFF"
android:textSize="22sp"
android:textStyle="bold" />
</com.example.viewgroupdemo.MyViewGroup>
\
package com.example.viewgroupdemo;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.ViewGroup.MarginLayoutParams;
import android.widget.LinearLayout;
/**
* 自定义ViewGroup , 添加四个TextView(childview)
* 四个角
*
* onMeasure 测量,
* 首先获取viewgroup 父容器建议的高度和宽度, 以及模式
*
* 通过此方法, 为childView设置高度和宽度,
* 此方法执行完成后, childview的高度和宽度已经计算好了,
* measureChildren(widthMeasureSpec, heightMeasureSpec);
* 遍历childview ,计算高度和宽度, 通过 MarginLayoutParams 获取margin
*
* //通过子view 获取MarginlayoutParams
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
*
* //获取了childview的高度和宽度以及margin完成
* 根据viewgroup的mode 设置viewgroup的高度和宽度
* setMeasuredDemension(
* (widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,
(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
*
* )
*
*/
public class MyViewGroup extends ViewGroup {
public MyViewGroup(Context context) {
this(context, null);
}
public MyViewGroup(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyViewGroup(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
try {
/**
* TODO: 对其所有的 childView进行定位(设置childView的绘制区域)
*
*/
int cCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams marginLayoutParams = null;
/***
* 遍历所有的childView 根据其高宽,以及margin进行布局
*
*/
for (int i = 0; i < cCount; i++) {
View childView = getChildAt(i);
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
//childView的四个边
int cl = 0, cr = 0, ct = 0, cb = 0;
switch (i) {
case 0:
cl = marginLayoutParams.leftMargin;
ct = marginLayoutParams.topMargin;
break;
case 1:
cl = getWidth() - cWidth - marginLayoutParams.leftMargin - marginLayoutParams.rightMargin;
ct = marginLayoutParams.topMargin;
break;
case 2:
cl = marginLayoutParams.leftMargin;
ct = getHeight() - marginLayoutParams.bottomMargin - cHeight;
break;
case 3:
cl = getWidth() - cWidth - marginLayoutParams.rightMargin- marginLayoutParams.rightMargin;
ct = getHeight() - cHeight - marginLayoutParams.bottomMargin;
break;
default:
break;
}
cr = cl + cWidth;
cb = ct + cHeight;
childView.layout(cl, ct, cr, cb);
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs)
{
return new MarginLayoutParams(getContext(), attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
try {
/**
* TODO:计算所有的childView的测量,获取高度和宽度,
* 并根据childView的计算结果, 设置自己的 宽度和高度
*/
//获取ViewGroup上级容器为其推荐的高度和宽度, 以及设计模式
int widthSpec = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpec = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
/**
* 计算所有的chlseView的高度和宽度
* ,通过ViewGroup的measureChildren方法为其所有的孩子设置宽和高,
* 此行执行完成后,childView的宽和高都已经正确的计算过了
*/
measureChildren(widthMeasureSpec, heightMeasureSpec);
int width = 0;
int height = 0;
//子view的个数,
int mCount = getChildCount();
int cWidth = 0;
int cHeight = 0;
MarginLayoutParams marginLayoutParams = null;
//计算左边两个childView的高度
int lHeight = 0;
//计算右边两个childView的高度, 最终高度是去lHeight和 rHeight最大值决定;
int rHeight = 0;
//计算上边两个childView的宽度
int tWidth = 0;
//计算下边两个childView的宽度, 最终宽度由tWidth和bWidth最大值决定
int bWidth = 0;
/**
* 根据childView 计算出的宽和高,以及设置的margin计算容器的宽度和高度,
* 主要用于容器是wrap_content
*/
for (int i = 0; i < mCount; i++) {
//获取子view
View childView = getChildAt(i);
//获取childView测量的宽度和高度
cWidth = childView.getMeasuredWidth();
cHeight = childView.getMeasuredHeight();
//通过子view 获取MarginlayoutParams
marginLayoutParams = (MarginLayoutParams) childView.getLayoutParams();
//遍历四个childView
//上边两个宽度
if(i==0 || i==1){
tWidth += cWidth +
marginLayoutParams.leftMargin +
marginLayoutParams.rightMargin;
}
//下边两个宽度
if(i==2 || i==3){
bWidth += cWidth +
marginLayoutParams.leftMargin +
marginLayoutParams.rightMargin;
}
//左边两个的高度
if(i == 0 || i == 2){
lHeight += cHeight +
marginLayoutParams.topMargin
+marginLayoutParams.bottomMargin;
}
//右边两个的高度
if(i == 1 || i == 3){
rHeight += cHeight +
marginLayoutParams.topMargin
+marginLayoutParams.bottomMargin;
}
//计算ViewGroup的高度和宽度
width = Math.max(tWidth, bWidth);
height = Math.max(lHeight, rHeight);
/**
* 设置父容器的宽度和高度
* 如果是wrap_content设置为我们的值 EXCATLY
* 否则: 直接设置为父容器计算的值 At_MOST
*/
setMeasuredDimension(
(widthMode == MeasureSpec.EXACTLY) ? widthSpec: width,
(heightMode == MeasureSpec.EXACTLY) ? heightSpec : height
);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
\
\
\
\
\
\