安卓自定义控件(一)Canvas、Paint、Shader、Xfermode

来源:互联网 发布:柏拉图交友软件 编辑:程序博客网 时间:2024/05/29 17:26

关于自定义控件,之前就写过一篇自定义控件,上图下字的Button,图片任意指定大小,但是使用效果还是让人感觉不幸福,这次索性彻彻底底地对自定义控件做一次彻彻底底的总结。

我会花4篇博客来介绍自定义控件,由潜入深慢慢学:
工具类:ViewHelper(View处理常用方法封装)
安卓自定义控件(一)Canvas、Paint、Shader、Xfermode
安卓自定义控件(二)BitmapShader、ShapeDrawable、Shape
安卓自定义控件(三)实现自定义View
4、onLayout(实现自定义ViewGroup)。
这一篇博客就先介绍一下onDraw方法。

概述

自定义控件有三个比较重要的方法

  • onDraw 就是在绘制View的样式。
  • onMeasure 算出自己需要占用多大的面积。
  • onLayout 按照我们想要的规则自定义子View排列。

因为View设计出来肯定是给别人看的,首先我们考虑绘制View,而绘制View就必须先学会Canvas(画布)和Paint(画笔);然后就是设置View的大小,让我们的控件更加强大,更加通用,就像Button,它可以指定大小,也可以不指定;如果你设计的是ViewGroup,那可能还得考虑每一个子View的位置,那就得考虑重写onLayout方法。

最简单的自定义View

源码:

写一个最简单的自定义控件,鼓励一下自己吧。

/** * 矩形 * Created by ChenSS on 2016/11/24. */public class RectView extends View{    public RectView(Context context) {        super(context);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Paint paint = new Paint();        paint.setColor(Color.RED);        canvas.drawRect(60, 60, 180, 180, paint);        //或者        //canvas.drawRect(new Rect(200,200,300,300),paint);    }}

在Activity使用我们的自定义View:

public class RectActivity extends AppCompatActivity {    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(new RectView(this));    }}

这里写图片描述
这样就写完了,是不是感觉自己棒棒哒?我们实现了一个最简单的自定义View,不过它非常弱小,只是一个矩形,甚至连长宽都被我们写死了。

小结:

这个自定义控件虽然有点Low,但是我们也有一些收获:

  1. onDraw的参数是一个Canvas(画布),画布决定了绘制的是一个什么样的图形;
  2. 画布的使用需要有Paint(画笔)的配合,画笔决定了绘制的颜色;
  3. 此外,还会涉及到Color(颜色),Bitmap(位图),Shader(着色),Shape(形状)等等。

以上是一个粗略总结,查看API会发现,上面提到的类都属于 android.graphics这个包,接下去就去瞅一瞅他们到底有什么本事。

认识graphics中常用的类

1.Color(颜色)类

  Android系统中颜色的常用表示方法有以下4种:
  (1)int color = Color.BLUE;
  (2)int color = Color.argb(150,200,0,100);
  (3)layout.setBackgroundColor(R.color.white),引用颜色资源;
  (4)Color.parseColor(“#365663”)。

  在实际应用中,我们常用的颜色有以下几种,其颜色常量及其表示的颜色如下所示:
  

  • Color.BLACK 黑色
  • Color.GREEN 绿色
  • Color.BLUE 蓝色
  • Color.LTGRAY浅灰色
  • Color.CYAN 青绿色
  • Color.MAGENTA 红紫色
  • Color.DKGRAY 灰黑色
  • Color.RED 红色
  • Color.YELLOW 黄色
  • Color.TRANSPARENT 透明
  • Color.GRAY 灰色
  • Color.WHITE 白色

2.Paint(画笔)类

  要绘制图形,首先得调整画笔,按照自己的开发需要,设置画笔的相关属性。Pain类的常用属性设置方法如下:

  • setAntiAlias(); //设置画笔的锯齿效果
  • setColor(); //设置画笔的颜色
  • setARGB(); //设置画笔的A、R、G、B值
  • setAlpha(); //设置画笔的Alpha值
  • setStyle(); //设置画笔的风格(空心或实心)
  • setStrokeWidth(); //设置空心边框的宽度
  • getColor(); //获取画笔的颜色
  • setTextScaleX(float scaleX) // 设置文本缩放倍数,1.0f为原始
  • setTextSize(float textSize) // 设置字体大小
  • setUnderlineText(booleanunderlineText) // 设置下划线

3.Canvas(画布)类

  画笔属性设置好之后,还需要将图像绘制到画布上。Canvas绘制常用图形的常用方法如下:  

  • 绘制直线:canvas.drawLine(float startX, float startY, float stopX, float stopY, Paint paint);
  • 绘制矩形:canvas.drawRect(float left, float top, float right, float bottom, Paint paint);
  • 绘制圆形:canvas.drawCircle(float cx, float cy, float radius, Paint paint);
  • 绘制字符:canvas.drawText(String text, float x, float y, Paint paint);
  • 绘制图形:canvas.drawBitmap(Bitmap bitmap, float left, float top, Paint paint);
  • 绘制扇形(在一个矩形区域的两个角之间绘制一个弧。):drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
  • 绘制圆角矩形:drawRoundRect(RectF rect, float rx, float ry, Paint paint)
  • 绘制椭圆:drawOval(float left, float top, float right, float bottom, Paint paint)

补充:绘制任意多边形

其实画笔还有其它功能,这里就不再展开,这里再补充一个绘制多边形的案例。

/** * 绘制任意多边形(简单地绘制了一个三角形,我比较懒) * Created by ChenSS on 2016/11/24. */public class RectView extends View{    public RectView(Context context) {        super(context);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        Paint paint = new Paint();        //设置空心        paint.setStyle(Paint.Style.STROKE);        Path path1=new Path();        path1.moveTo(200, 800);        path1.lineTo(800, 800);        path1.lineTo(800, 200);        path1.close();//封闭        canvas.drawPath(path1, paint);    }}

duobianxing

Shader(着色器、渲染)

绘制完图形,接下来就要考虑颜色的问题了,虽然Color很强大,但是也并不能满足我们全部的问题,就比如:颜色渐变,这时候就要依靠Shader了,Shader直译是着色器,我就称呼它着色器吧。

Shader它主要实现颜色的渲染效果,此外还提供了颜色渐变、图片拉伸、平铺的功能,它包含以下5个子类:BitmapShader, ComposeShader, LinearGradient, RadialGradient, SweepGradient。

LinearGradient线性渲染

Create a shader that draws a linear gradient along a line.
创建一个着色器,颜色沿一条直线进行渐变。

        //创建线性渲染对象        int mColorLinear[] = {Color.RED, Color.GREEN, Color.BLUE, Color.WHITE};        Shader mShader = new LinearGradient(0, 0, 100, 100, mColorLinear, null, Shader.TileMode.REPEAT);

构造函数:

LinearGradient(float x0, float y0, float x1, float y1, int[] colors, float[] positions, Shader.TileMode tile)

  • float x0: 渐变起始点x坐标
  • float y0:渐变起始点y坐标
  • float x1:渐变结束点x坐标
  • float y1:渐变结束点y坐标
  • int[] colors:颜色 的int 数组
  • float[] positions: 相对位置的颜色数组,可为null, 若为null,可为null,颜色沿渐变线均匀分布
  • Shader.TileMode tile: 渲染器平铺模式

LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1, Shader.TileMode tile)

  • float x0: 渐变起始点x坐标
  • float y0:渐变起始点y坐标
  • float x1:渐变结束点x坐标
  • float y1:渐变结束点y坐标
  • int color0: 起始渐变色
  • int color1: 结束渐变色
  • Shader.TileMode tile: 渲染器平铺模式

SweepGradient梯度渲染

A subclass of Shader that draws a sweep gradient around a center point.
创建一个着色器,颜色沿着某个点的旋转方向进行渐变。

        //创建线性渲染对象        int mColorLinear[] = {Color.RED, Color.GREEN, Color.BLUE, Color.WHITE};        mSweepGradient = new SweepGradient(240, 360,mColorLinear , null);   

构造函数:

SweepGradient(float cx, float cy, int[] colors, float[] positions)

  • cx 渲染中心点x 坐标
  • cy 渲染中心y 点坐标
  • colors 围绕中心渲染的颜色数组,至少要有两种颜色值
  • positions 相对位置的颜色数组,可为null, 若为null,可为null,颜色沿渐变线均匀分布

SweepGradient(float cx, float cy, int color0, int color1)

  • cx 渲染中心点x 坐标
  • cy 渲染中心点y 坐标
  • color0 起始渲染颜色
  • color1 结束渲染颜色

RadialGradient环形渲染

Create a shader that draws a radial gradient given the center and radius.
创建一个着色器,颜色沿着半径方向进行渐变。

        //颜色渐变效果为一圈一圈的圆环        int mColorRadial[] = {Color.GREEN, Color.RED, Color.BLUE, Color.WHITE};        Shader mShader = new RadialGradient(350, 325, 75, mColorRadial, null, Shader.TileMode.REPEAT);

构造函数:

RadialGradient(float centerX, float centerY, float radius, int[] colors, float[] stops, Shader.TileMode tileMode)

  • float x: 圆心X坐标
  • float y: 圆心Y坐标
  • float radius: 半径
  • int[] colors: 渲染颜色数组
  • floate[] positions: 相对位置数组,可为null, 若为null,可为null,颜色沿渐变线均匀分布
  • Shader.TileMode tile:渲染器平铺模式

RadialGradient(float centerX, float centerY, float radius, int centerColor, int edgeColor, Shader.TileMode tileMode)

  • float x: 圆心X坐标
  • float y: 圆心Y坐标
  • float radius: 半径
  • int color0: 圆心颜色
  • int color1: 圆边缘颜色
  • Shader.TileMode tile:渲染器平铺模式

ComposeShader组合渲染

看它的构造方法,显然这个类的作用是Shader组合使用的方法,不过后面两个参数很陌生,后面我们再做介绍
ComposeShader(Shader shaderA, Shader shaderB, Xfermode mode)
ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode)

BitmapShader位图渲染

可以设置图片的CLAMP(拉伸)、REPEAT(重复)、MIRROR( 镜像),关于Shader.TileMode后面也会具体介绍
BitmapShader(Bitmap bitmap, Shader.TileMode tileX, Shader.TileMode tileY)

在自定义控件中使用着色器

/** * 颜色渲染 * Created by ChenSS on 2016/11/24. */public class RectView extends View {    public RectView(Context context) {        super(context);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        //创建线性渲染对象        int mColorLinear[] = {Color.RED, Color.GREEN, Color.BLUE, Color.WHITE};        Shader mShader = new LinearGradient(0, 0, 100, 100, mColorLinear, null, Shader.TileMode.REPEAT);        Paint paint = new Paint();        paint.setShader(mShader);        paint.setColor(Color.RED);        canvas.drawRect(60, 60, 800, 800, paint);    }}

这里写图片描述

Shader.TileMode(平铺模式)

emun Shader.TileMode这是一个枚举类,决定了图形的平铺方式,就像我们电脑上更换桌面图片,可以选择拉伸、平铺等等。

3种模式:
static final Shader.TileMode CLAMP: 边缘拉伸。
replicate the edge color if the shader draws outside of its original bounds

static final Shader.TileMode MIRROR:横向不断翻转重复,纵向不断翻转重复,不断对称变化铺满。
repeat the shader’s image horizontally and vertically, alternating mirror images so that adjacent images always seam

Static final Shader.TillMode REPETA:在水平方向和垂直方向重复摆放,两个相邻图像间有缝隙缝隙。
repeat the shader’s image horizontally and vertically

具体的效果,这里不好展开说明,具体案例我放到了下一篇博客。自定义控件(二)

Xfermode(遮罩图层)

前面介绍了ComposeShader组合渲染,不过只列出了两个构造方法,现在开始介绍这几个陌生的家伙:PorterDuff、Xfermode、PorterDuff.Mode。

查看API你会发现Xfermode有3个子类,不过,有两个类已经过时了,而且,在Android Studio上也只能使用其中一个:PorterDuffXfermode,我就只介绍PorterDuffXfermode的使用。

Xfermode能做什么?从下面的效果图,我们可以看出,Xfermode大概就是做一些运算,给他两张图片,然后做出一个组合之后的效果。

效果图

这里写图片描述

API截图:

这里写图片描述

常量介绍:

1.PorterDuff.Mode.CLEAR
所绘制不会提交到画布上。
2.PorterDuff.Mode.SRC
显示上层绘制图片
3.PorterDuff.Mode.DST
显示下层绘制图片
4.PorterDuff.Mode.SRC_OVER
正常绘制显示,上下层绘制叠盖。
5.PorterDuff.Mode.DST_OVER
上下层都显示。下层居上显示。
6.PorterDuff.Mode.SRC_IN
取两层绘制交集。显示上层。
7.PorterDuff.Mode.DST_IN
取两层绘制交集。显示下层。
8.PorterDuff.Mode.SRC_OUT
取上层绘制非交集部分。
9.PorterDuff.Mode.DST_OUT
取下层绘制非交集部分。
10.PorterDuff.Mode.SRC_ATOP
取下层非交集部分与上层交集部分
11.PorterDuff.Mode.DST_ATOP
取上层非交集部分与下层交集部分
12.PorterDuff.Mode.XOR
异或:去除两图层交集部分
13.PorterDuff.Mode.DARKEN
取两图层全部区域,交集部分颜色加深
14.PorterDuff.Mode.LIGHTEN
取两图层全部,点亮交集部分颜色
15.PorterDuff.Mode.MULTIPLY
取两图层交集部分叠加后颜色
16.PorterDuff.Mode.SCREEN
取两图层全部区域,交集部分变为透明色
17.PorterDuff.Mode.ADD Saturate(S + D)
18.PorterDuff.Mode.OVERLAY

Xfermode测试Demo

/** * Xfermode的使用 * Created by ChenSS on 2016/11/24. */public class RectView extends View {    private int screenW, screenH;    private int width = 200;    private int height = 200;    private Paint mPaint;    private Bitmap srcBitmap, dstBitmap;    private PorterDuffXfermode xfermode;    public RectView(Context context) {        this(context, null);    }    public RectView(Context context, AttributeSet attrs) {        super(context, attrs);        //ViewHelper是我写的工具类,顶部已经给出链接        screenW = ViewHelper.getScreenW(context);        screenH = ViewHelper.getScreenH(context);        srcBitmap = makeSrc(width, height);        dstBitmap = makeDst(width, height);        //修改Mode常量,查看不同的效果        xfermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);        mPaint = new Paint();        mPaint.setFilterBitmap(false);        mPaint.setStyle(Paint.Style.FILL);    }    @Override    protected void onDraw(Canvas canvas) {        //左边的图片        canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2, (screenH / 2 - height) / 2, mPaint);        //中间的图片        canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3, (screenH / 2 - height) / 2, mPaint);        //创建一个图层,关于图层的使用,结尾附上其他人的博客链接        int sc = canvas.saveLayer(0, 0, screenW, screenH, null, Canvas.MATRIX_SAVE_FLAG |                Canvas.CLIP_SAVE_FLAG |                Canvas.HAS_ALPHA_LAYER_SAVE_FLAG |                Canvas.FULL_COLOR_LAYER_SAVE_FLAG |                Canvas.CLIP_TO_LAYER_SAVE_FLAG);        //合并两个图片        canvas.drawBitmap(dstBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2, (screenH / 2 - height) / 2, mPaint);        //设置Paint的Xfermode        mPaint.setXfermode(xfermode);        canvas.drawBitmap(srcBitmap, (screenW / 3 - width) / 2 + screenW / 3 * 2, (screenH / 2 - height) / 2, mPaint);        mPaint.setXfermode(null);        // 还原画布        canvas.restoreToCount(sc);    }    /**     * 绘制一个圆形的Bitmap     */    private Bitmap makeDst(int w, int h) {        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);        Canvas c = new Canvas(bm);        Paint paint = new Paint();        paint.setAntiAlias(true);        paint.setColor(Color.RED);        c.drawOval(new RectF(0, 0, w * 3 / 4, h * 3 / 4), paint);        return bm;    }    /**     * 绘制一个矩形的Bitmap     */    private Bitmap makeSrc(int w, int h) {        Bitmap bm = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);        Canvas c = new Canvas(bm);        Paint paint = new Paint();        paint.setAntiAlias(true);        paint.setColor(Color.GREEN);        c.drawRect(w / 3, h / 3, w * 19 / 20, h * 19 / 20, paint);        return bm;    }}

这里写图片描述

代码中,我将PorterDuff.Mode设置为DST_IN,他是取第二个图层(DST)的交集部分,大家可以修改Mode值,查看每个常量的效果。

ComposeShader组合渲染Demo

ComposeShader的放到这里来介绍,因为它的构造函数我们还不理解,了解了PorterDuff.Mode每一个常量的作用之后,ComposeShader的功能也比较好理解一些。
它的作用就是根据PorterDuff.Mode模式,组合使用两个Shader。

/** * ComposeShader对象的使用 * Created by ChenSS on 2016/11/24. */public class RectView extends View {    private LinearGradient mLinearGradient;    private BitmapShader mBitmapShader;    private ComposeShader mComposeShader;    private Paint mPaint;    private Bitmap mBitmap;    private int mWidth, mHeight;    public RectView(Context context) {        super(context);        //得到图像        mBitmap = ViewHelper.findBitmapById(context, R.mipmap.view_robot);        mWidth = mBitmap.getWidth();        mHeight = mBitmap.getHeight();        //创建LinearGradient对象        int mColorLinear[] = {Color.WHITE,  Color.BLUE, Color.WHITE};        mLinearGradient = new LinearGradient(0, 0, mWidth, mHeight, mColorLinear, null, Shader.TileMode.REPEAT);        // 创建BitmapShader对象        mBitmapShader = new BitmapShader(mBitmap, Shader.TileMode.MIRROR, Shader.TileMode.MIRROR);        // 混合渲染 将两个效果叠加,Mode取值DST_ATOP        mComposeShader = new ComposeShader(mBitmapShader, mLinearGradient, PorterDuff.Mode.DST_ATOP);        mPaint = new Paint();        mPaint.setAntiAlias(true);        mPaint.setColor(Color.GRAY);    }    @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 绘制混合渲染效果        mPaint.setShader(mComposeShader);        canvas.drawRect(0, 0, mWidth, mHeight, mPaint);    }}

这里写图片描述

Xfermode实战的初步设想:

我们先画一个圆,然后再给他一张Bitmap,然后通过Xfermode,把Bitmap圆形的部分显示出来,实现一个圆形的ImageView。

安卓自定义控件(一)就到此结束,简单地介绍了一下图形绘制、颜色的使用,下一篇进一步介绍BitmapShader位图渲染和Xfermode的实际使用,然后再想办法做出各种形状的图片。

Canvas图层也是十分重要的,这里就不具体展开,这里给出其他人博客的链接。
android canvas layer (图层)详解与进阶

0 0
原创粉丝点击