android是如何自定义view

如今需求的多种多样,app要求各种绚丽的效果,使得andriod开发的我们无法避免一个问题,最终逼得我们不得不去自定义一些view来实现需求。

阅读了下 Android 我眼中的自定义View(2) 这篇文章,给自己做一个笔记。总结了下:

通常自定义view有一下几种:
1、直接继承View,或者ViewGroup去重写一个自定义view。
2、继承某个已有的控件,比如TextView去重写一个自定义view。
3、继承某个已有的ViewGroup,比如LinearLayout去重写一个自定义view。

对于第一种方式,需要注意的是,在内容区域未超过屏幕尺寸的情况下,我们一般需要在onMeasure()中重新测量ViewGroup尺寸来对wrap_content属性进行支持,如果内容区域的大小超过屏幕尺寸,我们就必须在onMeasure()中重新测量ViewGroup的尺寸,否则ViewGroup的最大尺寸为屏幕尺寸,导致ViewGroup中的内容显示不全。因为,此时wrap_content和match_parent的效果是一样的,同时各种padding也是无效的,因此是需要你自己onMeasure的。

直接继承View的话,通常模板应该是这样的:

public class CircleView extends View {
    .......省略若干代码........    
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(500, 500);
        } else if (widthSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(500, heightSpecSize);
        } else if (heightSpecMode == MeasureSpec.AT_MOST) {
            setMeasuredDimension(widthSpecSize, 500);
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int width = getWidth();
        int height = getHeight();
//需要在这里设置padding哦
        int radius = Math.min((width - getPaddingLeft() - getPaddingRight()) / 2,
                (height - getPaddingTop() - getPaddingBottom()) / 2);
        canvas.drawColor(Color.GRAY);
        canvas.drawCircle(width / 2, height / 2, radius, mPaint);
    }
}

直接继承viewGroup的话,模板大概是这样的:

public class TestViewGroup extends ViewGroup {
    //使ViewGroup支持margin属性
    @Override
    public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
        return new MarginLayoutParams(getContext(), attrs);
    }
    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        measureChildren(widthMeasureSpec, heightMeasureSpec);

        int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
        int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);

        int width = 0;
        int height = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View childView = getChildAt(i);
            MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
            width += childView.getMeasuredWidth() + params.rightMargin + params.leftMargin;

            if (i == 0) {
                height += childView.getMeasuredHeight() + params.topMargin + params.bottomMargin;
            }
        }
        if (width > getScreenWidth()) {
            setMeasuredDimension(width, height);
        } else {
            setMeasuredDimension((widthSpecMode == MeasureSpec.AT_MOST) ? width : widthSpecSize,
                    (heightSpecMode == MeasureSpec.AT_MOST) ? height : heightSpecSize);
        }
    }
//需要layout了,因为他要指定他的子view的拜访位置
    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        int left = 0;
        View lastChildView = null;
        for (int i = 0; i < getChildCount(); i++) {
            View childView = getChildAt(i);
            MarginLayoutParams params = (MarginLayoutParams) childView.getLayoutParams();
            left += params.leftMargin;
            if (lastChildView != null) {
                left += lastChildView.getMeasuredWidth() + ((MarginLayoutParams) lastChildView.getLayoutParams()).rightMargin;
            }
            int right = left + childView.getMeasuredWidth();
            int top = params.topMargin;
            int bottom = childView.getMeasuredHeight() + top;
            childView.layout(left, top, right, bottom);
            lastChildView = childView;
        }
}

然后对于第二大类,通常继承一个特定的view组件,是不需要写onMeasureonLayout的,除非有必要,因为google的android工程师做的太牛逼了。

参考文章:

Android 我眼中的自定义View(2)

你可能感兴趣的:(android是如何自定义view)