自定义View

2019-01-24  本文已影响6人  jie啊

继承已有的View,简单改写它们的尺寸:重写onMeasure()

  1. 重写onMeasure()
  2. 用getMeasuredWidth()和getMeasuredHeight()获取到测量出的尺寸
  3. 计算出最终尺寸
  4. 用setMeasuredDimension(w,h)把结果保存
 * 继承已有控件,重写onMeasure
 */
public class SquareImageView extends android.support.v7.widget.AppCompatImageView {
    public SquareImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int measuredWidth = getMeasuredWidth();
        int measuredHeight = getMeasuredHeight();
        int size = Math.max(measuredWidth, measuredHeight);
        setMeasuredDimension(size, size); // 保存测得的尺寸
    }

//
//    @Override
//    public void layout(int l, int t, int r, int b) {//直接在此指定宽高,样式虽说改变,但是没有经过测量,父view并不知晓控件宽高已经改写,
    //会导致父控件中的子view重叠
//        int w = r - l;
//        int h = b - t;
//        int size = Math.min(w, h);
//        super.layout(l, t, r + size, b + size);
//
//    }
}

对自定义View完全进行自定义尺寸计算:重写onMeasure()

  1. 重写onMeasure()
  2. 计算出自己的尺寸
  3. 用resolveSize()或者resolveSizeAndState()修正结果
public class CircleView extends View {
    private static final int RADIUS = (int) Utils.dpToPixel(80);//应该在自定义属性值可动态设置
    private static final int PADDING = (int) Utils.dpToPixel(30);
    Paint p = new Paint(Paint.ANTI_ALIAS_FLAG);

    public CircleView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.RED);//画布
        canvas.drawCircle(RADIUS+PADDING, RADIUS+PADDING, RADIUS, p);

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = (PADDING + RADIUS) * 2; //圆的半径加padding
        int height = (PADDING + RADIUS) * 2;

        width = resolveSizeAndState(width, widthMeasureSpec, 0);////resolveSize()
        height = resolveSizeAndState(height, widthMeasureSpec, 0);//resolveSizeAndState是否压缩状态
        setMeasuredDimension(width, height);

    }
}

⾃定义 Layout:重写 onMeasure() 和 onLayout():TagLayout

  1. 重写 onMeasure()
    1. 遍历每个⼦ View,⽤ measureChildWidthMargins() 测量⼦ View
      • 需要重写 generateLayoutParams() 并返回 MarginLayoutParams 才能使⽤
        measureChildWithMargins() ⽅法
    • 有些⼦ View 可能需要重新测量(⽐如换⾏处)
    • 测量完成后,得出⼦ View 的实际位置和尺⼨,并暂时保存
    • measureChildWidthMargins() 的内部实现(代码必读):
      通过 getChildMeasureSpec(int spec, int padding, int childDimension) ⽅
      法计算出⼦ View 的 widthMeasureSpec 和 heightMeasureSpec,然后调
      ⽤ child.measure() ⽅法来让⼦ View ⾃我测量;
  2. 重写 onLayout()
public class TagLayout extends ViewGroup  {
    private static final String TAG = "TagLayout";
    List<Rect> childrenBounds = new ArrayList<>();//便于存放子view尺寸,容器以情况而定

    public TagLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        for (int i = 0; i < getChildCount(); i++) {
            final View child = getChildAt(i);
            Rect childBounds = childrenBounds.get(i);
            child.layout(childBounds.left, childBounds.top, childBounds.right, childBounds.bottom);
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {//widthMeasureSpec,heightMeasureSpec是TagLayout的测量模式
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthUsed = 0;
        int heightUsed = 0;
        int lineMaxHeight = 0;
        int lineWidthUsed = 0;
        int specWidth = MeasureSpec.getSize(widthMeasureSpec);
        int specMode = MeasureSpec.getMode(widthMeasureSpec);
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed);//测量子view,0表示,最右边的view可以放下,避免被压缩
            if (specMode != MeasureSpec.UNSPECIFIED && lineWidthUsed + child.getMeasuredWidth() > specWidth) {
                lineWidthUsed = 0;
                heightUsed += lineMaxHeight;
                lineMaxHeight = 0;
                measureChildWithMargins(child, widthMeasureSpec, 0, heightMeasureSpec, heightUsed);//
            }
            Rect rect;
            if (childrenBounds.size() <= i) {
                rect = new Rect();
                childrenBounds.add(rect);
            } else {
                rect = childrenBounds.get(i);
            }
            rect.set(lineWidthUsed, heightUsed,
                    lineWidthUsed + child.getMeasuredWidth(), heightUsed + child.getMeasuredHeight());
            lineWidthUsed += child.getMeasuredWidth();
            widthUsed = Math.max(widthUsed, lineWidthUsed);
            lineMaxHeight = Math.max(lineMaxHeight, child.getMeasuredHeight());
        }
        int w = widthUsed;
        int h = heightUsed + lineMaxHeight;
        setMeasuredDimension(w, h);//保存尺寸
    }

    //重写
    @Override
    public LayoutParams generateLayoutParams(AttributeSet p) {
        return new MarginLayoutParams(getContext(), p);
    }
}

学习自hencode课程

上一篇下一篇

猜你喜欢

热点阅读