1、前言
在👴经历了结肠炎导致的发烧超39度、恶寒等等debuff之后,打赢复活赛重新归来,终于有时间来写第二篇的内容了捏😅。
2、概述
本篇将说明第二个步骤——重写View的onDraw方法。(上篇: 从0开始学会自定义View(一))。
3、重写onDraw
用途:概括一下,onDraw方法目的就是绘制你想要的内容。
但是,在正式了解onDraw方法之前我们还需要了解两个非常重要的类:
-
Paint(画笔🖌️
很好理解,就是我在画画时手中的画笔,可以决定绘图的颜色(包括背景、文字颜色等)、文字的大小、文字的边距、边框大小等等。
-
Canvas(画布
这个也很容易懂,就是画笔的颜色确定了之后,我们拿着“这支画笔”来画画,我们可以画一个矩形、圆形、不规则图形、文字等等。
Ok,介绍完这些之后我们正式开始学习onDraw方法。
首先先把方法重写:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}
这里的参数canvas就是我们的画布,我们需要什么就用canvas画什么就行了,里面的内容还是用我们上一期在onDraw里面用到的代码(其中的mSolidColor有过调整,换成其他颜色了):
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//第一部分
mPaint.setColor(mSolidColor);//背景颜色
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);//绘制背景
//第二部分
mPaint.setColor(mTextColor);//文字颜色
canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2, getHeight() / 2 + mTextBound.height() / 2, mPaint);//绘制文字
}
将这里面的代码拆成两个部分来看:
第一部分:
这部分是在绘制背景图形。
矩形
mPaint.setColor(mSolidColor);//背景颜色
//绘制一个矩形
canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);//绘制背景
步骤就是确定笔刷颜色,使用canvas绘制图形。
效果图:
其中drawRect方法中用到的参数分别是 左侧坐标、顶部坐标、右侧坐标、底部坐标、笔刷对象。 也可以通过创建一个Rect对象来存储矩形边界,调用时直接将该对象作为参数传入也可以达到一样的效果:
Rect rect = new Rect(0,0,getWidth(),getHeight()); //方法外部初始化
canvas.drawRect(rect,mPaint);//方法内部调用
也可以画个带圆角的矩形:
mPaint.setColor(mSolidColor);
canvas.drawRoundRect(0, 0, getWidth(), getHeight(), dp2px(15), dp2px(15), mPaint);
前4个参数不多说了,不同的在后两个参数,表示圆角的椭圆的 x 半径和圆角的椭圆的 y 半径,其中dp2px是将dp值转为px值的方法,我们这里的圆角设为15dp。
protected int dp2px(float dp) {
//mContext在MyCustomView构造方法中初始化。
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, metrics);
}
效果如下:
- 圆形
mPaint.setColor(mSolidColor);
canvas.drawArc(0,0,getWidth(),getHeight(),0,360,false,mPaint);
第三和第四个参数表示开始的角度和扫过的角度,第五个参数表示设置我们的圆弧在绘画的时候,是否经过圆形。
mPaint.setColor(mSolidColor);
//这里第五个参数useCenter
canvas.drawArc(0,0,getWidth(),getHeight(),0,270,useCenter,mPaint);
useCenter为true | useCenter为false |
---|---|
第二部分: 这一部分是在背景之上绘制文字
mPaint.setColor(mTextColor);//文字颜色
canvas.drawText(mText, getWidth() / 2 - mTextBound.width() / 2, getHeight() / 2 + mTextBound.height() / 2, mPaint);//绘制文字
同样的,绘制文字也需要知道文字的坐标,这里的坐标为文字水平方向上的x坐标点,垂直方向上基线的坐标点。
代码中getWidth和getHeight是获取View的宽高,而mTextBound为包含文字边界的Rect对象,如何测量文字并将结果放进这个对象呢?使用Paint对象的getTextBound方法,该方法接收一个字符串类型参数(代表你需要测量的文字)、一个其实位置、和一个结束位置以及一个接收边界的Rect类:
//不为空则测量
if (!TextUtils.isEmpty(mText)) {
//从0开始,到文字长度结束
mPaint.getTextBounds(mText, 0, mText.length(), mTextBound);
}
这里要注意的是,View绘制是有层级的,如果我们将两步对调会发现效果和第一张的效果是一样的,因为文字是先绘制的,而后绘制的背景会盖在文字上方。
4、更多
padding值
这个时候我们如果在xml文件上给我们的加上任意padding值会发现根本不会生效,因为在代码中我们的背景大小就是根据我们给的宽高来的,没有将padding算进去,因此我们可以来加入考虑padding值的情况再来看看:
mPaint.setColor(mSolidColor);
canvas.drawRect(getPaddingLeft(), getPaddingTop(), getWidth() - getPaddingRight(), getHeight() - getPaddingBottom(), mPaint);
xml:
<com.my.mycustomview.MyCustomView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
app:solid_color="@color/teal_200"
app:text_size="25sp"
app:text="AAA"
android:padding="10dp"
app:background_drawable="@drawable/custom_view"/>
效果:
使用Drawable设置背景
如果我们想加入渐变色的背景,分别控制上、下、左、右的圆角角度可以考虑使用Drawable对象,设置改对象并调用setBackGround方法。 attrs:
<!--背景颜色-->
<attr name="solid_color" format="color" />
<!--文字-->
<attr name="text" format="string" />
<!--文字颜色-->
<attr name="text_color" format="color" />
<!--文字大小-->
<attr name="text_size" format="dimension" />
<!--开始颜色-->
<attr name="start_color" format="color" />
<!--中间色-->
<attr name="center_color" format="color" />
<!--结束颜色-->
<attr name="end_color" format="color" />
<!--整体圆角-->
<attr name="corners_radius" format="dimension"/>
<!--左上圆角-->
<attr name="top_left_radius" format="dimension"/>
<!--右上圆角-->
<attr name="top_right_radius" format="dimension"/>
<!--左下圆角-->
<attr name="bottom_left_radius" format="dimension"/>
<!--右下圆角-->
<attr name="bottom_right_radius" format="dimension"/>
<declare-styleable name="MyCustomView">
<attr name="solid_color" />
<attr name="text" />
<attr name="text_color" />
<attr name="text_size" />
<attr name="start_color" />
<attr name="center_color" />
<attr name="end_color" />
<attr name="corners_radius"/>
<attr name="top_left_radius"/>
<attr name="top_right_radius"/>
<attr name="bottom_left_radius"/>
<attr name="bottom_right_radius"/>
<attr name="background_drawable" format="reference"/>
</declare-styleable>
MyCustomView.java:
mStartColor = a.getColor(R.styleable.MyCustomView_start_color, Color.TRANSPARENT);
mCenterColor = a.getColor(R.styleable.MyCustomView_start_color, Color.TRANSPARENT);
mEndColor = a.getColor(R.styleable.MyCustomView_end_color, Color.TRANSPARENT);
cornersRadius = a.getDimension(R.styleable.MyCustomView_corners_radius, 0.0F);
topLeftRadius = a.getDimension(R.styleable.MyCustomView_top_left_radius, 0.0F);
topRightRadius = a.getDimension(R.styleable.MyCustomView_top_right_radius, 0.0F);
bottomLeftRadius = a.getDimension(R.styleable.MyCustomView_bottom_left_radius, 0.0F);
bottomRightRadius = a.getDimension(R.styleable.MyCustomView_bottom_right_radius, 0.0F);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//背景
setBackGroundColor();
//文字
mPaint.setColor(mTextColor);
if (!TextUtils.isEmpty(mText)) {
canvas.drawText(mText, (float) getWidth() / 2 - (float) mTextBound.width() / 2, (float) getHeight() / 2 + (float) mTextBound.height() / 2, mPaint);
}
}
protected void setBackGroundColor() {
//创建GradientDrawable对象
GradientDrawable drawable = new GradientDrawable();
//背景颜色
if (mSolidColor != 0) {
drawable.setColor(mSolidColor);
} else if (mStartColor != 0 && mEndColor != 0) {
drawable.setColors(new int[]{mStartColor, mEndColor});
}
//圆角
if (topLeftRadius != 0 || topRightRadius != 0 || bottomLeftRadius != 0 || bottomRightRadius != 0) {//设置某个角弧度
drawable.setCornerRadii(new float[]{topLeftRadius, topLeftRadius, topRightRadius, topRightRadius, bottomRightRadius, bottomRightRadius, bottomLeftRadius, bottomLeftRadius});
} else if (cornersRadius > 0) {
drawable.setCornerRadius(cornersRadius);//设置弧度
}
setBackground(drawable);
}
xml:
<com.my.mycustomview.MyCustomView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
app:start_color="#FF4C81"
app:end_color="#FF8E61"
app:text_size="25sp"
app:text="AAA"
app:text_color="@color/white"
app:corners_radius="20dp"
app:background_drawable="@drawable/custom_view"/>
最终效果:
属性加入Drawable
另一种途径是我们在attrs中加入一个属性来指定背景的drawable。 下面我在MyCustomView的申明中加入background_drawable这个属性,format为reference。 attrs:
<attr name="background_drawable" format="reference"/>
MyCustomView.java:
mDrawable = a.getDrawable(R.styleable.MyCustomView_background_drawable);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//背景
if (mDrawable != null) {
setBackGroundDrawable(mDrawable, canvas);
}
//文字
mPaint.setColor(mTextColor);
if (!TextUtils.isEmpty(mText)) {
canvas.drawText(mText, (float) getWidth() / 2 - (float) mTextBound.width() / 2, (float) getHeight() / 2 + (float) mTextBound.height() / 2, mPaint);
}
}
protected void setBackGroundDrawable(@NonNull Drawable drawable, Canvas canvas) {
//这里确定引入的drawable对象的边界大小,传入一个Rect对象,大小和View一致
drawable.setBounds(new Rect(0, 0, getWidth(), getHeight()));
drawable.draw(canvas);
}
xml:
<com.my.mycustomview.MyCustomView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
app:text_size="25sp"
app:text="AAA"
app:text_color="@color/white"
app:background_drawable="@drawable/custom_view"/>
@drawable/custom_view:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="#FF4C81"
android:endColor="#FF8E61"
android:angle="270"/>
<corners android:radius="20dp"/>
</shape>
效果:
5、结尾
那么到这里这篇内容就结束了,在这篇文章中讲述了如何重写onDraw方法,以及加入padding值、在其中使用Drawable对象和属性加入Drawable对象和运用,下一篇将会是我们今天这篇的实战运用,更新时间不定,作者懒狗一条🐶捏,拜拜👋