Android自定义ViewGroup( 支持layout_margin属性)

原文链接:https://blog.csdn.net/ldld1717/article/details/80458917

3. 支持layout_margin属性

如果我们自定义的布局参数类继承自MarginLayoutParams,就自动支持了layout_margin属性了,我们需要做的就是直接在布局文件中使用layout_margin属性,然后再onMeasure和onLayout中使用margin属性值测量和摆放子控件。需要注意的是我们测量子控件的时候应该调用measureChildWithMargin()方法。

布局文件:

<?xml version="1.0" encoding= "utf-8"?>
<com.openxu.costomlayout.CustomLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:openxu= "http://schemas.android.com/apk/res/com.openxu.costomlayout"
    android:background="#33000000"
    android:layout_width= "match_parent"
    android:layout_height= "match_parent" >
 
    <Button
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        openxu:layout_position= "left"
        android:layout_marginLeft = "20dip"
        android:background= "#FF8247"
        android:textColor= "#ffffff"
        android:textSize="20dip"
        android:padding= "20dip"
        android:text="按钮1" />
 
    <Button
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        android:layout_marginTop = "30dip"
        openxu:layout_position= "right"
        android:background= "#8B0A50"
        android:textColor= "#ffffff"
        android:textSize="18dip"
        android:padding= "10dip"
        android:text="按钮2222222222222" />
 
    <Button
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        android:layout_marginLeft = "30dip"
        android:layout_marginBottom = "10dip"
        openxu:layout_position= "bottom"
        android:background= "#7CFC00"
        android:textColor= "#ffffff"
        android:textSize="20dip"
        android:padding= "15dip"
        android:text="按钮333333" />
 
    <Button
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        openxu:layout_position= "rightAndBottom"
        android:layout_marginBottom = "30dip"
        android:background= "#1E90FF"
        android:textColor= "#ffffff"
        android:textSize="15dip"
        android:padding= "10dip"
        android:text="按钮4" />
 
    <Button
        android:layout_width= "wrap_content"
        android:layout_height= "wrap_content"
        openxu:layout_position= "center"
        android:layout_marginBottom = "30dip"
        android:layout_marginRight = "30dip"
        android:background= "#191970"
        android:textColor= "#ffffff"
        android:textSize="20dip"
        android:padding= "15dip"
        android:text="按钮5" />
 
</com.openxu.costomlayout.CustomLayout>

onMeasure和onLayout:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 
   // 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式   
  int widthMode = MeasureSpec. getMode(widthMeasureSpec); 
  int heightMode = MeasureSpec. getMode(heightMeasureSpec); 
  int sizeWidth = MeasureSpec. getSize(widthMeasureSpec); 
  int sizeHeight = MeasureSpec. getSize(heightMeasureSpec); 
  int layoutWidth = 0;
  int layoutHeight = 0;
       int cWidth = 0;
       int cHeight = 0;
       int count = getChildCount(); 
 
       // 计算出所有的childView的宽和高
       for( int i = 0; i < count; i++){
            View child = getChildAt(i); 
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, 0);
      }
      CustomLayoutParams params = null;
       if(widthMode == MeasureSpec. EXACTLY){
             //如果布局容器的宽度模式时确定的(具体的size或者match_parent)
            layoutWidth = sizeWidth;
      } else{
             //如果是未指定或者wrap_content,我们都按照包裹内容做,宽度方向上只需要拿到所有子控件中宽度做大的作为布局宽度
             for ( int i = 0; i < count; i++)  { 
                   View child = getChildAt(i); 
               cWidth = child.getMeasuredWidth(); 
               params = (CustomLayoutParams) child.getLayoutParams(); 
               //获取子控件宽度和左右边距之和,作为这个控件需要占据的宽度
               int marginWidth = cWidth+params.leftMargin+params.rightMargin ;
               layoutWidth = marginWidth > layoutWidth ? marginWidth : layoutWidth;
            }
      }
       //高度很宽度处理思想一样
       if(heightMode == MeasureSpec. EXACTLY){
            layoutHeight = sizeHeight;
      } else{
             for ( int i = 0; i < count; i++)  { 
                   View child = getChildAt(i); 
                   cHeight = child.getMeasuredHeight();
                   params = (CustomLayoutParams) child.getLayoutParams(); 
                   int marginHeight = cHeight+params.topMargin+params.bottomMargin ;
                   layoutHeight = marginHeight > layoutHeight ? marginHeight : layoutHeight;
            }
      }
 
       // 测量并保存layout的宽高
      setMeasuredDimension(layoutWidth, layoutHeight);
}
 
@Override
protected void onLayout( boolean changed, int left, int top, int right,
             int bottom) {
       final int count = getChildCount();
       int childMeasureWidth = 0;
       int childMeasureHeight = 0;
      CustomLayoutParams params = null;
       for ( int i = 0; i < count; i++) {
            View child = getChildAt(i);
             // 注意此处不能使用getWidth和getHeight,这两个方法必须在onLayout执行完,才能正确获取宽高
            childMeasureWidth = child.getMeasuredWidth();
            childMeasureHeight = child.getMeasuredHeight();
            params = (CustomLayoutParams) child.getLayoutParams(); 
      switch (params. position) {
             case CustomLayoutParams. POSITION_MIDDLE:    // 中间
                  left = (getWidth()-childMeasureWidth)/2 - params.rightMargin + params.leftMargin ;
                  top = (getHeight()-childMeasureHeight)/2 + params.topMargin - params.bottomMargin ;
                   break;
             case CustomLayoutParams. POSITION_LEFT:      // 左上方
                  left = 0 + params. leftMargin;
                  top = 0 + params. topMargin;
                   break;
             case CustomLayoutParams. POSITION_RIGHT:     // 右上方
                  left = getWidth()-childMeasureWidth - params.rightMargin;
                  top = 0 + params. topMargin;
                   break;
             case CustomLayoutParams. POSITION_BOTTOM:    // 左下角
                  left = 0 + params. leftMargin;
                  top = getHeight()-childMeasureHeight-params.bottomMargin ;
                   break;
             case CustomLayoutParams. POSITION_RIGHTANDBOTTOM:// 右下角
                  left = getWidth()-childMeasureWidth - params.rightMargin;
                  top = getHeight()-childMeasureHeight-params.bottomMargin ;
                   break;
             default:
                   break;
            }
					
 
             // 确定子控件的位置,四个参数分别代表(左上右下)点的坐标值
            child.layout(left, top, left+childMeasureWidth, top+childMeasureHeight);
      }
 
}

运行效果:

好了,就写到这里,如果想尝试设置其他属性,比如above、below等,感兴趣的同学可以尝试一下哦~。其实也没什么难的,无非就是如果布局属性定义的多,那么在onMeasure和onLayout中考虑的问题就更多更复杂,自定义布局容器就是根据自己的需求,让容器满足我们特殊的摆放要求。

总结一下今天学习的内容,这篇博客主要学习了两个知识点:
自定义ViewGroup的步骤:
①. 继承ViewGroup,覆盖构造方法
②. 重写onMeasure方法测量子控件和自身宽高
③. 实现onLayout方法摆放子控件
为布局容器自定义布局属性:
①. 大致明确布局容器的需求,初步定义布局属性
②. 继承LayoutParams,定义布局参数类
③. 重写获取布局参数的方法
④. 在布局文件中使用布局属性
⑤. 在onMeasure和onLayout中使用布局参数

点击下方链接免费获取Android进阶资料:
https://shimo.im/docs/tXXKHgdjPYj6WT8d/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值