1. 自定义简易FrameLayout 分别左上,右上,左下,右下4个子View
2. 自定义简易LinearLayout,实现横向和纵向布局
3. 自定义简易RelativeLayout,实现layout_alignParentXXX方法
1. 自定义ViewGroup主要是复写onMeasure测量每一个子View的宽高,复写onLayout计算每一个子View的上下左右间距
2. 自定义一些属性值
3. 复写generateLayoutParams,计算margin值
1. onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//分别获取宽高的测量模式
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//记录如果是wrap_content时设置的宽和高
int width, height;
//左右的高度
int lHeight = 0,rHeight = 0;
//上下的宽度
int tWidth = 0, bWidth = 0;
//测量所有子孩子的宽高
measureChildren(widthMeasureSpec,heightMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
MarginLayoutParams mParams = (MarginLayoutParams) childView.getLayoutParams();
if(i ==0 || i == 1){
tWidth += childWidth + mParams.leftMargin + mParams.rightMargin;
}
if(i == 2 || i == 3){
bWidth += childWidth + mParams.leftMargin + mParams.rightMargin;
}
if(i == 0 || i== 2){
lHeight += childHeight + mParams.topMargin + mParams.bottomMargin;
}
if(i == 1 || i == 3){
rHeight += childHeight + mParams.topMargin + mParams.bottomMargin;
}
}
//wrap_content 时取宽高的最大值
width = Math.max(tWidth,bWidth);
height = Math.max(lHeight,rHeight);
int measureWidth = widthMode == MeasureSpec.EXACTLY ? widthSize : width;
int measureHeight = heightMode == MeasureSpec.EXACTLY ? heightSize : height;
setMeasuredDimension(measureWidth,measureHeight);
}
2. onLayout 需要注意的一个地方是最好计算一下父View的pading值
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
MarginLayoutParams cParams = (MarginLayoutParams) childView.getLayoutParams();
int childLeft = 0,childTop = 0,childRight = 0,childBottom = 0;
switch (i){
case 0:
childLeft = cParams.leftMargin;
childTop = cParams.topMargin+ getPaddingTop();
break;
case 1:
childLeft = getMeasuredWidth() - childWidth - cParams.rightMargin;
childTop = cParams.topMargin + getPaddingTop();
break;
case 2:
childLeft = cParams.leftMargin;
childTop = getMeasuredHeight() - childHeight - cParams.bottomMargin
- getPaddingBottom() - getPaddingTop();
break;
case 3:
childLeft = getMeasuredWidth() - childWidth - cParams.rightMargin;
childTop = getMeasuredHeight() - childHeight - cParams.bottomMargin
-getPaddingTop() - getPaddingBottom();
break;
default:
}
childRight = childLeft + childWidth;
childBottom = childTop + childHeight;
childView.layout(childLeft,childTop,childRight,childBottom);
}
}
分横向和竖向
横向的时候当宽高为wrap_content的时候,宽度累加+左右padding值
高度取子View的最大高度
竖向的时候当宽高为wrap_content的时候,宽度取子View中的最大宽度,高度累加+上下padding值
onLayout主要是计算子View的left,top,right,bottom的距离,细心一点就好
1. onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//记录如果是wrap_content时设置的宽和高
int width, height;
int totalWidth = 0;
int totalHeight = 0;
//测量所有子孩子的宽高
measureChildren(widthMeasureSpec,heightMeasureSpec);
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
MarginLayoutParams mParams = (MarginLayoutParams) childView.getLayoutParams();
if("horizontal".equals(mOrientation)){
//横向高度取子孩子中高度最大值 带上margin和父View的padding值
int cHeight = childHeight + mParams.topMargin + mParams.bottomMargin + this.getPaddingBottom() + this.getPaddingTop();
if(cHeight > totalHeight){
totalHeight = cHeight;
}
//横向第一个子孩子和最后一个子孩子需要带上父View的padding值
if(i == 0){
totalWidth+= getPaddingLeft();
}else if(i == getChildCount() - 1){
totalWidth += getPaddingRight();
}
totalWidth += childWidth + mParams.leftMargin + mParams.rightMargin;
}else {
//竖向宽度取其中一个子孩子的最大高度
int cWidth = childWidth + mParams.leftMargin + mParams.rightMargin + this.getPaddingLeft() + this.getPaddingRight();
if (cWidth > totalWidth) {
totalWidth = cWidth;
}
//竖向的第一子孩子和最后一个子孩子需要带上父View的padding值
if(i == 0){
totalHeight += getPaddingTop();
}else if(i == getChildCount() - 1){
totalHeight += getPaddingBottom();
}
totalHeight += childHeight + mParams.topMargin + mParams.bottomMargin;
}
}
width = totalWidth;
height = totalHeight;
int measureWidth = widthMode == MeasureSpec.EXACTLY ? widthSize : width;
int measureHeight = heightMode == MeasureSpec.EXACTLY ? heightSize : height;
setMeasuredDimension(measureWidth,measureHeight);
}
2. onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childLeft ;
int childTop ;
int childRight ;
int childBottom ;
int lastTotalHeight = 0;
int lastTotalWidth = 0;
for (int i = 0; i < getChildCount(); i++) {
View childView = getChildAt(i);
int childWidth = childView.getMeasuredWidth();
int childHeight = childView.getMeasuredHeight();
MarginLayoutParams cParams = (MarginLayoutParams) childView.getLayoutParams();
if("horizontal".equals(mOrientation)) {
//横向第一个带上父View的左边padding值
if(i == 0){
childLeft = cParams.leftMargin + getPaddingLeft();
}else {
childLeft = lastTotalWidth + cParams.leftMargin;
}
//横向最后一个带上父View的右边padding值
if(i == getChildCount() - 1) {
childRight = childLeft + childWidth+ getPaddingRight();
}else{
childRight = childLeft + childWidth;
}
lastTotalWidth = childRight;
//横向上下带上上下padding值
childTop = cParams.topMargin + getPaddingTop();
childBottom = childTop + childHeight + getPaddingBottom();
}else{
//竖向左右带上左右padding值
childLeft = cParams.leftMargin + getPaddingLeft();
childRight = childLeft + childWidth + getPaddingRight();
//竖向第一个带上父View的上边padding值
if(i == 0){
childTop = cParams.topMargin + getPaddingTop();
}else {
childTop = lastTotalHeight + cParams.topMargin;
}
////竖向最后个带上父View的下边padding值
if(i == getChildCount() - 1){
childBottom = childTop + childHeight+ getPaddingBottom();
}else {
childBottom = childTop + childHeight;
}
lastTotalHeight = childBottom;
}
childView.layout(childLeft,childTop,childRight,childBottom);
}
}
onMeasure
当宽高为wrap_content的时候,宽度取子View的最大宽度
高度取子View的最大高度,记得带上左右padding值
onLayout
根据自定义的子View的不同位置layout_position,计算子View的left,top,right,bottom的距离。
1. onMeasure
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
//用于计算wrap_content时候的宽高
int width = 0;
int height = 0;
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
//测量每一个子孩子
measureChild(child,widthMeasureSpec,heightMeasureSpec);
CustomLayoutParam lp = (CustomLayoutParam) child.getLayoutParams();
int lrMargin = lp.leftMargin + lp.rightMargin;
int tbMargin = lp.topMargin + lp.bottomMargin;
int childWidth = child.getMeasuredWidth() + lrMargin;
int childHeight = child.getMeasuredHeight() + tbMargin;
//获取子孩子中最大的子孩子宽度
if(width < childWidth){
width = childWidth;
}
//获取子孩子总最大的子孩子高度
if(height < childHeight){
height = childHeight;
}
}
//带上父View的上下左右的pading
width += getPaddingLeft() + getPaddingRight();
height += getPaddingTop() + getPaddingBottom();
int measureWidth = widthMode == MeasureSpec.AT_MOST ? width : widthSize;
int measureHeight = heightMode == MeasureSpec.AT_MOST ? height : heightSize;
setMeasuredDimension(measureWidth,measureHeight);
}
2. onLayout
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int pWidth = getMeasuredWidth();
int pHeight = getMeasuredHeight();
for (int i = 0; i < getChildCount(); i++) {
View child = getChildAt(i);
CustomLayoutParam lp = (CustomLayoutParam) child.getLayoutParams();
int lrMargin = lp.leftMargin + lp.rightMargin;
int tbMargin = lp.topMargin + lp.bottomMargin;
int cWidthAndMargin = child.getMeasuredWidth() + lrMargin;
int cHeightAndMargin = child.getMeasuredHeight() + tbMargin;
int cWidth = child.getMeasuredWidth();
int cHeight = child.getMeasuredHeight();
int left = 0;
int top = 0;
int right = 0;
int bottom = 0;
switch (lp.getPosition()){
case "leftTop":
left = lp.leftMargin + getPaddingLeft();
top = lp.topMargin + getPaddingTop();
break;
case "rightTop":
left = pWidth - cWidthAndMargin- getPaddingRight();
top = lp.topMargin + getPaddingTop();
right += getPaddingRight();
break;
case "leftBottom":
left = lp.leftMargin + getPaddingLeft();
top = pHeight - cHeightAndMargin;
bottom += getPaddingBottom();
break;
case "rightBottom":
left = pWidth - cWidthAndMargin- getPaddingLeft();
top = pHeight - cHeightAndMargin;
bottom += getPaddingBottom();
break;
case "horizontalCenter":
left = (pWidth - cWidth) / 2;
top = lp.topMargin + getPaddingTop();
break;
case "verticalCenter":
left = lp.leftMargin + getPaddingLeft();
top = (pHeight - cHeight) / 2;
break;
case "rightVerticalCenter":
left = pWidth - cWidthAndMargin- getPaddingLeft();
top = (pHeight - cHeight) / 2;
break;
case "bottomHorizontalCenter":
left = (pWidth - cWidth) / 2;
top = pHeight - cHeightAndMargin;
bottom += getPaddingBottom();
break;
case "center":
left = (pWidth - cWidth) / 2;
top = (pHeight - cHeight) / 2;
break;
}
right += left + child.getMeasuredWidth();
bottom += top + child.getMeasuredHeight();
child.layout(left,top,right,bottom);
}
}
@Override
public LayoutParams generateLayoutParams(AttributeSet attrs) {
return new CustomLayoutParam(getContext(),attrs);
}
public class CustomLayoutParam extends ViewGroup.MarginLayoutParams{
/**
* leftTop
* rightTop
* horizontalCenter
* verticalCenter
* rightVerticalCenter
* bottomHorizontalCenter
* center
* leftBottom
* rightBottom
*/
private String mPosition;
public CustomLayoutParam(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.MyRelativeLayout);
mPosition = typedArray.getString(R.styleable.MyRelativeLayout_layout_position);
if(TextUtils.isEmpty(mPosition)){
mPosition = "leftTop";
}
typedArray.recycle();
}
public String getPosition() {
return mPosition;
}
}
后面统一提供下载地址
QQ:1509815887