0
点赞
收藏
分享

微信扫一扫

自定义 CircleView - 继承 View 重写 onDraw

ZSACH 2023-02-01 阅读 73


一、画一个圆形的 View

自定义 CircleView - 继承 View 重写 onDraw_ide

如图,该圆形控件的宽为 match_parent,高 150dp,为了看到控件的整体宽高效果,为控件加了背景色即浅绿色:#3300aa00

  1. 该页面的布局

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">


<cc.catface.helloworld.view.CircleView
android:id="@+id/cv_ver01"
android:layout_width="match_parent"
android:layout_height="150dp"
android:background="#3300aa00" />

</LinearLayout>

  1. 初始化工作

public class CircleView extends View {

private Paint mPaint;

public CircleView(Context context) {
super(context);
init();
}

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

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

// 初始化画笔,并为画笔设置为蓝色
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.BLUE);
}

......
}

  1. 绘制

@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// get到控件的宽为match_parent,即根布局LinearLayout宽度即屏幕宽度
int width = getWidth();
// get到控件的高度,即150dp
int height = getHeight();

// 取控件宽高最小值作为直径,然后画圆
int radius = Math.min(width, height) / 2; // 半径
// (控件宽度/2, 控件高度/2)即为圆心坐标
canvas.drawCircle(width / 2, height / 2, radius, mPaint); // 画圆
}

二、为圆形控件添加 margin值

  1. 控件布局添加 margin

android:layout_margin="20dp"

  1. 分析
  • margin 即外边距。看到 layout,可以知道这是父容器来控制子控件的
  • 若设置了 margin,即为该控件设置了距离其父控件或其兄弟控件的边距
  1. 效果

三、将圆形控件的宽设置为 wrap_content

  1. 控件布局添加 padding

<cc.catface.helloworld.view.CircleView
android:id="@+id/cv_ver01"
android:layout_width="wrap_content"
android:layout_height="150dp"
android:layout_margin="20dp"
android:background="#3300aa00" />

运行会发现效果与设置为 match_parent 一样

  1. 分析
    按下表很容易得出原因:父容器的 layout_width 是 match_parent 即测量模式为 EXACTLY,圆形控件的宽 LayoutParams 为 wrap_content 即得出圆形控件的测量模式为 AT_MOST,其宽度即为父容器的建议宽度 parentSize,所以效果与使用 match_parent 一样。

childLayoutParams↓ &&& parentSpecMode→

EXACTLY

AT_MOST

UNSPECIFIED

dp/px

EXACTLY(childSize)

EXACTLY(childSize)

EXACTLY(childSize)

match_parent

EXACTLY(parentSize)

AT_MOST(parentSize)

UNSPECIFIED(0)

wrap_content

AT_MOST(parentSize)

AT_MOST(parentSize)

UNSPECIFIED(0)

解决办法:在 onMeasure方法 中指定 wrap_content 模式的默认宽/高,比如此处将默认宽/高分别设置为 800px和 200px

// 解决 wrap_content 无效的问题,手动设置宽/高的大小
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);

if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(800, 200);

// 由上2.分析我们得出控件的宽测量模式是AT_MOST(wrap_content),高测量模式是EXACTLY(150dp),故走此判断
} else if (widthSpecMode == MeasureSpec.AT_MOST) {
// 手动设置控件的宽为800px,高即为父容器建议的值,也就是控件设置的layout_height值150dp
setMeasuredDimension(800, heightSpecSize);
} else if (heightSpecMode == MeasureSpec.AT_MOST) {
setMeasuredDimension(widthSpecSize, 200);
}
}

分析:走完 onMeasure,最后在 setMeasuredDimension方法 中将宽 800px,高 150dp的设置值交给了系统

  1. 效果

四、为圆形控件添加 padding值

  1. 控件布局添加 padding

android:padding="20dp"

  1. 分析
  • padding 即内边距。是控件自己控制自己的属性
  • 若设置了 padding,即设置了控件距其子控件或其内部内容(如文本)的边距
  • 需要在代码中得到 padding值,并做相应的修改即可得到 padding 的效果

@Override protected void onDraw(Canvas canvas) {
super.onDraw(canvas);

// 分别获取上下左右的padding值,其实此处都相同,因为设置的padding对上下左右都有效且相同
final int paddingLeft = getPaddingLeft();
final int paddingRight = getPaddingRight();
final int paddingTop = getPaddingTop();
final int paddingBottom = getPaddingBottom();

// 控件内容的宽度即为setMeasuredDimension得到的宽度值800px-左右padding值
int width = getWidth() - paddingLeft - paddingRight;
// 控件内容的高度即为setMeasuredDimension得到的高度值150dp-上下padding值
int height = getHeight() - paddingTop - paddingBottom;

int radius = Math.min(width, height) / 2;
canvas.drawCircle(paddingLeft + width / 2, paddingTop + height / 2, radius, mPaint);
}

  • onDraw方法中的 width/height 即内容区域的宽/高,再分别加上对应 padding值,即为圆心坐标
  1. 效果


举报

相关推荐

0 条评论