自定义view绘制基础
自定义绘制概述
- 自定义绘制的方式是重写绘制方法,其中最常用的是 onDraw()
- 绘制的关键是 Canvas 的使用
Canvas 的绘制类方法: drawXXX() (关键参数:Paint)
Canvas 的辅助类方法:范围裁切和几何变换 - 可以使用不同的绘制方法来控制遮盖关系
自定义绘制四个级别
1.Canvas 的 drawXXX() 系列方法及 Paint 最常见的使用
2.Paint 的完全攻略
3.文字的绘制
4.Canvas 对绘制的辅助——范围裁切和几何变换。
5.使用不同的绘制方法来控制绘制顺序
学完上面得课程链接,我们来个实战运用。
模仿淘宝得banner轮播图效果
淘宝现在搞活动banner图下掉了,我们把最后实现得效果呈现上来
看图,四个角是需要圆角得,然后中间内容是banner轮播。
1.首先我们要画一个圆角矩形来实现这个圆角效果,但是圆角矩形填充没办法满足我们得需求。我们需要取矩形和圆角矩形不相交得部分,这里就要用到path和path的FillType。
我们来温习一下前面学习的知识点:
WINDING 非零环绕数原则
EVEN_ODD 奇偶原则
如果你看到上面的三张图还是不能理解的画,建议看一下文章顶上的链接内容第一条
我们来看一下代码:
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();//得到测量的高度
int height = getMeasuredHeight();//得到测量的宽度
//保存画布
canvas.save();
//创建绘制矩形
RectF rectF=new RectF(0,0,width,height);
//绘制矩形,顺时针
path1.addRect(rectF, Path.Direction.CW);
//绘制圆角矩形,顺时针
path1.addRoundRect(rectF,50,50, Path.Direction.CW);
//设置填充模式,
path1.setFillType(Path.FillType.EVEN_ODD);
//设置填充颜色
paint1.setColor(Color.RED);
//在canvas上绘制填充对象
canvas.drawPath(path1,paint1);
//还原画布
canvas.restore();
//这个很重要,没次画完之后要重置以下path,paint不用重置。这里可以试一下
//如果不添加这个的话,从后台返回前台的时候,绘制会有问题。有知道的小伙伴可以在评论区回复一下
paint1.reset();
path1.reset();
}
最后这里还有一个非常重要的方法调用
避免性能优化,导致绘制不上去
setWillNotDraw(false);
完整代码
public class RadiusLayout extends LinearLayout {
public RadiusLayout(@NonNull Context context) {
super(context);
initView();
}
public RadiusLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
public RadiusLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
public RadiusLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
private void initView() {
setWillNotDraw(false);
}
Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
Path path1 = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();//得到测量的高度
int height = getMeasuredHeight();//得到测量的宽度
//保存画布
canvas.save();
//创建绘制矩形
RectF rectF=new RectF(0,0,width,height);
//绘制矩形,顺时针
path1.addRect(rectF, Path.Direction.CW);
//绘制圆角矩形,顺时针
path1.addRoundRect(rectF,50,50, Path.Direction.CW);
//设置填充模式,
path1.setFillType(Path.FillType.EVEN_ODD);
paint1.setColor(Color.RED);
canvas.drawPath(path1,paint1);
//还原画布
canvas.restore();
paint1.reset();
path1.reset();
}
}
public class RadiusLayout extends LinearLayout {
public RadiusLayout(@NonNull Context context) {
super(context);
initView();
}
public RadiusLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
public RadiusLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
public RadiusLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
private void initView() {
setWillNotDraw(false);
}
Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
Path path1 = new Path();
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int width = getMeasuredWidth();//得到测量的高度
int height = getMeasuredHeight();//得到测量的宽度
//保存画布
canvas.save();
//创建绘制矩形
RectF rectF=new RectF(0,0,width,height);
//绘制矩形,顺时针
path1.addRect(rectF, Path.Direction.CW);
//绘制圆角矩形,顺时针
path1.addRoundRect(rectF,50,50, Path.Direction.CW);
//设置填充模式,
path1.setFillType(Path.FillType.EVEN_ODD);
paint1.setColor(Color.RED);
canvas.drawPath(path1,paint1);
//还原画布
canvas.restore();
paint1.reset();
path1.reset();
}
}
这种方式需要我们把效果添加到顶层进行遮罩,那有没有更好的办法呢?
这里就要用到第五节课的内容了,绘制顺序。
这里我们拿出关键的点给大家进行讲解,然后奉上源码
于是我们就有了下面的代码:
public class RadiusLayout extends LinearLayout {
public RadiusLayout(@NonNull Context context) {
super(context);
initView();
}
public RadiusLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
initView();
}
public RadiusLayout(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView();
}
public RadiusLayout(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
initView();
}
private void initView() {
setWillNotDraw(false);
}
Paint paint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
Path path1 = new Path();
// @Override
// protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
//
// }
@Override
public void onDrawForeground(Canvas canvas) {
super.onDrawForeground(canvas);
int width = getMeasuredWidth();//得到测量的高度
int height = getMeasuredHeight();//得到测量的宽度
//保存画布
canvas.save();
//创建绘制矩形
RectF rectF=new RectF(0,0,width,height);
//绘制矩形,顺时针
path1.addRect(rectF, Path.Direction.CW);
//绘制圆角矩形,顺时针
path1.addRoundRect(rectF,50,50, Path.Direction.CW);
//设置填充模式,
path1.setFillType(Path.FillType.EVEN_ODD);
paint1.setColor(Color.RED);
canvas.drawPath(path1,paint1);
//还原画布
canvas.restore();
paint1.reset();
path1.reset();
}
}
我们把绘制这块的代码移到了onDrawForeground钩子函数中进行绘制
到这里基本上讲解完啦。
上面的两种方式,明现第二种能省掉一个view。所以效果也更加
<?xml version="1.0" encoding="utf-8"?>
<com.tudou.source.RadiusLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="200dp"
tools:context=".MainActivity">
<com.stx.xhb.androidx.XBanner
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/xbanner"
android:layout_width="match_parent"
android:background="@color/colorPrimary"
android:layout_height="match_parent"
app:AutoPlayTime="3000"
app:pointsContainerBackground="#44aaaaaa"
app:pointsPosition="RIGHT"
app:tipTextSize="12sp"
app:isShowNumberIndicator="true"
app:isShowIndicatorOnlyOne="true"
app:pageChangeDuration="800"/>
</com.tudou.source.RadiusLayout>