自定义 CircleView - 继承 View 重写 onDraw
一、画一个圆形的 View
如图,画一个简单的圆形控件,该控件的宽为 match_parent,高给出固定值 150dp,为了看到控件的整体宽高效果,为控件加了背景色即浅绿色:#3300aa00
-
该页面的布局
<?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>
-
初始化工作
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); } ...... }
-
绘制
@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值
-
控件布局添加 margin
android:layout_margin="20dp"
-
分析
margin 即外边距。看到 layout,可以知道这是父容器来控制子控件的
若设置了 margin,即为该控件设置了距离其父控件或其兄弟控件的边距
-
效果
三、将圆形控件的宽设置为 wrap_content
-
控件布局添加 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 一样
-
分析
按下表很容易得出原因:父容器的 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的设置值交给了系统
-
效果
四、为圆形控件添加 padding值
-
控件布局添加 padding
android:padding="20dp"
-
分析
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值,即为圆心坐标
-
效果
上一篇: 360推首款AI音箱:售价定位399元预约价199元
下一篇: 自定义Background
推荐阅读
-
前奏:自定义View(一)onDraw()中一些常用的属性和方法总结
-
Android 自定义View之onMeasure、onLayout、onDraw
-
自定义view哪个构造方法是不能不重写的
-
自定义View---继承ViewGroup动效
-
自定义 CircleView - 继承 View 重写 onDraw
-
自定义View之ondraw绘制
-
自定义 View - onDraw 过程详解
-
解决RecyclerView Item自定义View onDraw卡顿的方法
-
前奏:自定义View(一)onDraw()中一些常用的属性和方法总结
-
Android 自定义View之onMeasure、onLayout、onDraw