自定义viewgroup实践之仿写LinearLayout

来源:互联网 发布:泽野弘之 知乎 编辑:程序博客网 时间:2024/05/15 05:38


首先声明:本文只是仿写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);
具体请参考ViewGroup为什么不会调用onDraw


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

项目源码








1 0
原创粉丝点击