自定义view/viewgroup/流布局(极简版)

来源:互联网 发布:尼古拉斯凯奇现状 知乎 编辑:程序博客网 时间:2024/04/30 01:38

1自定义view
1)构造器

private String text;private int mTitleTextSize;private Rect mBound;private Paint mPaint;public MyCuntomView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {     super(context, attrs, defStyleAttr, defStyleRes);     TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyCuntomView, defStyleAttr, defStyleRes);//自定义属性     int n = a.getIndexCount();     for (int i = 0; i < n; i++) {         int attr = a.getIndex(i);         switch (attr) {             case R.styleable.CustomTitleView_titleText :                 text = a.getString(attr);//获得属性app:titleText=""                 break;             case R.styleable.CustomTitleView_titleTextSize:                 // 默认设置为16sp,TypeValue也可以把sp转化为px                 //getDimensionPixelSize方法返回的是像素数值                 //applyDimension转变为标准尺寸的一个函数                 //getDisplayMetrics//1.0或者1.5                 mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));                 Log.e("CustomTitleView: ", mTitleTextSize+"");                 break;         }     }     a.recycle();//回收TypedArray     //单击事件     this.setOnClickListener(new OnClickListener() {         @Override         public void onClick(View v) {             mTitleText = "OnClick";             postInvalidate();//线程中刷新页面         }     });}public MyCuntomView(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr, 0);}public MyCuntomView(Context context, AttributeSet attrs) {    this(context, attrs, 0, 0);}public MyCuntomView(Context context) {    this(context, null, 0, 0);}

2)画图

@Overrideprotected void onDraw(Canvas canvas) {    mPaint.setColor(Color.YELLOW);    canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);//画背景    mPaint.setColor(Color.BLACK);    canvas.drawText(text, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);//写字}

3)测量

@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){    int widthMode = MeasureSpec.getMode(widthMeasureSpec);//android:layout_width    int widthSize = MeasureSpec.getSize(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);//android:layout_height    int heightSize = MeasureSpec.getSize(heightMeasureSpec);    int width;    int height;    if (widthMode == MeasureSpec.EXACTLY) {        //明确的值或者MATCH_PARENT        width = widthSize;    } else {        //WARP_CONTENT        mPaint.setTextSize(mTitleTextSize);        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);        width = mBound.width();    }    if (heightMode == MeasureSpec.EXACTLY) {        //明确的值或者MATCH_PARENT        height = heightSize;    } else {        //WARP_CONTENT        mPaint.setTextSize(mTitleTextSize);        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);        height = mBound.height();    }    setMeasuredDimension(width, height);//设置测量结果}

2自定义viewgroup
1)generateLayoutParams

//ViewGroup能够支持margin即可,那么我们直接使用系统的MarginLayoutParams    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(), attrs);    }

2)构造器

public MyViewGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {    super(context, attrs, defStyleAttr, defStyleRes);}public MyViewGroup(Context context, AttributeSet attrs, int defStyle){    super(context, attrs, defStyle);}public MyViewGroup(Context context){    super(context);}public MyViewGroup(Context context, AttributeSet attrs){    super(context, attrs);}

3)onLayout子控件定位

//onLayout对其所有childView进行定位(设置childView的绘制区域)@Overrideprotected void onLayout(boolean changed, int l, int t, int r, int b) {    int cCount = getChildCount();    int cWidth = 0;    int cHeight = 0;    MarginLayoutParams cParams = null;    /**     * 遍历所有childView根据其宽和高,以及margin进行布局     */    for (int i = 0; i < cCount; i++)    {        View childView = getChildAt(i);//获得子控件        cWidth = childView.getMeasuredWidth();        cHeight = childView.getMeasuredHeight();        cParams = (MarginLayoutParams) childView.getLayoutParams();        int cl = 0, ct = 0, cr = 0, cb = 0;        switch (i)        {            case 0://左上                cl = cParams.leftMargin;                ct = cParams.topMargin;                break;            case 1://右上                cl = getWidth() - cWidth - cParams.leftMargin - cParams.rightMargin;                ct = cParams.topMargin;                break;        }        cr = cl + cWidth;        cb = cHeight + ct;        childView.layout(cl, ct, cr, cb);//确定位置    }}

4)onMeasure测量groupview宽高

//在onMeasure中计算childView的测量值以及模式,以及设置自己的宽和高:@Overrideprotected 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);    // 计算出所有的childView的宽和高    measureChildren(widthMeasureSpec, heightMeasureSpec);    //记录如果是wrap_content是设置的宽和高    int width = 0;    int height = 0;    int cCount = getChildCount();    int cWidth = 0;    int cHeight = 0;    MarginLayoutParams cParams = null;    //根据childView计算的出的宽和高,以及设置的margin计算容器的宽和高,主要用于容器是    for (int i = 0; i < cCount; i++)    {        View childView = getChildAt(i);        cWidth = childView.getMeasuredWidth();        cHeight = childView.getMeasuredHeight();        cParams = (MarginLayoutParams) childView.getLayoutParams();        if (i == 0 || i == 1)        {            width += cWidth + cParams.leftMargin + cParams.rightMargin;            height = Math.max(height, cHeight);        }    }    /**     * 如果是wrap_content设置为我们计算的值     * 否则:直接设置为父容器计算的值     */    setMeasuredDimension((widthMode == MeasureSpec.EXACTLY) ? sizeWidth : width,     (heightMode == MeasureSpec.EXACTLY) ? sizeHeight : height);}

3流布局

/** * Created by user on 16-6-12.流布局viewgroup */public class FloatTag extends ViewGroup {    public FloatTag(Context context) {        super(context);    }    public FloatTag(Context context, AttributeSet attrs) {        super(context, attrs);    }    public FloatTag(Context context, AttributeSet attrs, int defStyleAttr) {        super(context, attrs, defStyleAttr);    }    public FloatTag(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {        super(context, attrs, defStyleAttr, defStyleRes);    }    //因为我们只需要支持margin    @Override    public LayoutParams generateLayoutParams(AttributeSet attrs) {        return new MarginLayoutParams(getContext(), attrs);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        super.onMeasure(widthMeasureSpec, heightMeasureSpec);        // 获得它的父容器为它设置的测量模式和大小        int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);        int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);        int modeWidth = MeasureSpec.getMode(widthMeasureSpec);        int modeHeight = MeasureSpec.getMode(heightMeasureSpec);        // 如果是warp_content情况下,记录宽和高        int width = 0;        int height = 0;        /**         * 记录每一行的宽度,width不断取最大宽度         */        int lineWidth = 0;        /**         * 每一行的高度,累加至height         */        int lineHeight = 0;        int cCount = getChildCount();        for (int i = 0; i < cCount; i++) {            View child = getChildAt(i);            if (child.getVisibility() == View.GONE)            {                continue;            }            //测量每一个child的宽和高            measureChild(child, widthMeasureSpec, heightMeasureSpec);            //得到child的布局参数            MarginLayoutParams layoutParams = (MarginLayoutParams) child.getLayoutParams();            //子控件宽高            int childWidth = child.getMeasuredWidth() + layoutParams.leftMargin + layoutParams.rightMargin;            int childHeigt = child.getMeasuredHeight() + layoutParams.topMargin + layoutParams.bottomMargin;            //换行            if (lineWidth + childWidth > sizeWidth) {                width = Math.max(lineWidth, childWidth);                lineWidth = childWidth;                height += lineHeight;                lineHeight = childHeigt;            } else {                lineWidth += childWidth;                lineHeight = Math.max(lineHeight, childHeigt);            }            if (i == cCount - 1) {                width = Math.max(lineWidth, sizeWidth);                height += lineHeight;            }        }        setMeasuredDimension((modeWidth == MeasureSpec.EXACTLY) ? sizeWidth : width,                (modeHeight == MeasureSpec.EXACTLY) ? sizeHeight : height);    }    //onLayout中完成对所有childView的位置以及大小的指定    @Override    protected void onLayout(boolean changed, int l, int t, int r, int b) {        int cCount = getChildCount();        int left = 0;        int top = 0;        int lineheight = 0;        for (int i = 0; i < cCount; i++) {            View child = getChildAt(i);            if (child.getVisibility() == View.GONE)            {                continue;            }            MarginLayoutParams lp = (MarginLayoutParams) child                    .getLayoutParams();            if (left + lp.leftMargin + child.getWidth() > getWidth()) {                left = lp.leftMargin;                lineheight = Math.max(child.getHeight(), lineheight);                top += lineheight;            } else {                left += lp.leftMargin;                lineheight = Math.max(lineheight, child.getHeight());            }            //计算childView的left,top,right,bottom            int tc = top + lp.topMargin;            int rc = left + child.getMeasuredWidth();            int bc = tc + child.getMeasuredHeight();            child.layout(left, tc, rc, bc);            left += child.getWidth();        }    }}
0 0
原创粉丝点击