自定义viewgroup实践之仿写LinearLayout


首先声明:本文只是仿写LinearLayout的一个小部分----线性横向,用来说明自定义viewgroup的步骤。想更深入LinearLayout的同学请查看android的LinearLayout源码。

一般来说,自定义一个viewgroup至少需重写onmeasure()、onLayout()方法,如果对于布局有特定的需要,还需重写LayoutParam。

好了,现在来说具体的步骤:

一般而言有自定义属性,所以需得写构造方法。不是本文的重点,略过。

下面将一次详细的说明onMeasure()、onLayout()的实现。

onMeasure()具体实现

横向依次排列,首先定义一个宽度,初始值为0,每排一个view,就加入view的宽度和view的左右margin值,最后加上自身的左右padding值,就是viewgroup的宽度。

对于高度而言,子view中height加上其上下margin最大值就是其高度。然后再调用setMeasuredDimension();

在onMeasure()中最重要的是 要measure每个子view。

下面看具体代码:

 
 @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        //第一步--获取宽高模型
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);

        int usedWidth = 0;
        int usedHeight = 0;
        int usedMaxHeight = 0;
        Log.i("tag","kuan"+MeasureSpec.getSize(widthMeasureSpec)+" gao"+ MeasureSpec.getSize(heightMeasureSpec));

        //第二步--测量每个子view,并计算出所有子view的总长
        for(int i = 0; i < getChildCount(); i++){
            View child = getChildAt(i);
            if(child == null)
                continue;
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            measureChildWithMargins(child,widthMeasureSpec,usedWidth,heightMeasureSpec,usedHeight);

//            if(widthMode == MeasureSpec.EXACTLY){
//                usedWidth += lp.width + lp.layout_left_space + lp.leftMargin + lp.rightMargin;
//            }else{
                usedWidth += child.getMeasuredWidth() + lp.layout_left_space + lp.leftMargin + lp.rightMargin;
//            }
            if(heightMode == MeasureSpec.EXACTLY){
                usedMaxHeight = Math.max(usedMaxHeight,lp.height + lp.topMargin + lp.bottomMargin);
            }else{
                usedMaxHeight = Math.max(usedMaxHeight,child.getMeasuredHeight() + lp.topMargin + lp.bottomMargin);
            }
        }

        //第三步--计算出已知所用的长度,然后生成相关的宽高值
        int widthSize = usedWidth + getPaddingLeft() + getPaddingRight();
        int heightSize = usedMaxHeight + getPaddingTop() + getPaddingBottom();
        int widthSpec = 0;
        int heightSpec = 0;
        // Check against our minimum width
        widthSize = Math.max(widthSize, getSuggestedMinimumWidth());

        // Reconcile our calculated size with the widthMeasureSpec
        if(Build.VERSION.SDK_INT < Build.VERSION_CODES.HONEYCOMB){
            widthSpec = resolveSize(widthSize,widthMeasureSpec);
            heightSpec = resolveSize(heightSize,heightMeasureSpec);
        }else{
            widthSpec = resolveSizeAndState(widthSize, widthMeasureSpec, 0);
            heightSpec = resolveSizeAndState(heightSize,heightMeasureSpec,0);
        }
        widthSize = widthSpec & MEASURED_SIZE_MASK;
        heightSize = heightSpec & MEASURED_SIZE_MASK;
        //第四步--设置测量值
        setMeasuredDimension(widthSize,heightSize);
    }



onLayout()具体实现

在这里面也要layout每个子view。在这里就可以用view的get宽高方法了,看代码:

@Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        Log.i("tag_layout","left" + l);
        int startLeft = getPaddingLeft();
        final int startTop = getPaddingTop();
        for(int i = 0; i < getChildCount(); i++){
            View child = getChildAt(i);
            LayoutParams lp = (LayoutParams) child.getLayoutParams();
            startLeft += lp.leftMargin + lp.layout_left_space;

            int viewRight = startLeft + child.getMeasuredWidth();
            int viewBottom = startTop + child.getMeasuredHeight() + lp.topMargin;
            child.layout(startLeft,startTop + lp.topMargin,viewRight,viewBottom);
            startLeft = viewRight + lp.rightMargin;
        }
    }



对于LayoutParam有特殊需求的,重写LayoutParam

我这里定义了每个子view的横向间隔,看代码:

public LayoutParams(Context c, AttributeSet attrs) {
            super(c, attrs);
            TypedArray a = c.obtainStyledAttributes(attrs,R.styleable.Custom_Layout);
            layout_left_space = a.getDimensionPixelSize(R.styleable.Custom_Layout_layout_left_space,0);
            a.recycle();
        }

另外,对于自定义ViewGroup,还需重写以下三个方法:
 @Override
    public LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new LayoutParams(getContext(),attrs);
    }

    @Override
    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
        return new LayoutParams(p);
    }

    @Override
    protected LayoutParams generateDefaultLayoutParams() {
        return new LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
    }



如果要做ondraw(),有两种方法:
第一种 在构造方法中 setWillNotDraw(false);
第二种    在构造方法中        setBackgroundResource(R.drawable.ic_launcher);


ok,本文完毕,觉得还可以的哥们顶下。









  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android自定义ViewGroup是指在Android开发中,通过继承ViewGroup类来创建自定义的布局容器。自定义ViewGroup可以用于实现一些特殊的布局效果,比如侧滑菜单、滑动卡片等等。通过自定义ViewGroup,我们可以更灵活地控制子视图的布局和交互行为,以满足特定的需求。自定义ViewGroup的实现主要包括重onMeasure()方法和onLayout()方法,来测量和布局子视图。同时,我们还可以通过重onInterceptTouchEvent()方法和onTouchEvent()方法来处理触摸事件,实现自定义的交互效果。如果你对自定义ViewGroup还不是很了解,或者正想学习如何自定义,可以参考相关的教程和文档,如引用\[1\]和引用\[2\]所提到的博客和官方文档。 #### 引用[.reference_title] - *1* [Android 手把手教您自定义ViewGroup(一)](https://blog.csdn.net/iteye_563/article/details/82601716)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [使用LayoutParams自定义安卓ViewGroup](https://blog.csdn.net/lfq88/article/details/127268493)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Android自定义ViewGroup](https://blog.csdn.net/farsight2009/article/details/62046643)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值