Fork me on GitHub

Android绘画学习(Paint相关)

想做Android动画的时候,有的时候又需要自己画一些图形,比如自定义的进度条这样的。所以,学一些基础的绘画代码也是必要的。

我这里的Androi动画学习笔记是从CSDN的一位大佬的动画博客教程那里学来的,其实很多部分都与原博客相似,且内容不如原博客详细。这里直接贴上原博客,原博客有更丰富、更全面的教程,以方便大家学习,毕竟我自己的学习习惯有些问题,笔记并不一定全面。

原博客:Android自定义控件学习三部曲

Paint与Canvas

Paint是画笔,Canvas是画布。画笔可以负责的,比如颜色,粗细,透明度等;而画布负责的,是画笔在画布上画的位置,大小,形状等。所以,搞清楚各部分负责的职能,逻辑会更清晰些。

参数稍后再写,先写怎么能让应用运行起来,这样,在改一些参数设置的时候,就用不着只是看,可以亲自感受变化了。

基本实现方法

1、先将布局更换为FrameLayout,并添加id,用于添加视图

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_frame"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.example.administrator.animationtest.activity.MainActivity">
</FrameLayout>

2、创建一个自定义视图类,继承自View类。在这个类中,重点在于重写OnDraw()函数。

public class CustomView extends View {

    Context context;

    public CustomView(Context context) {
        super(context);
        this.context = context;
    }

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

        //示例代码
        //设置画笔基本属性
        Paint paint = new Paint();
        paint.setAntiAlias(true);           //抗锯齿
        paint.setColor(Color.RED);          //画笔颜色
        paint.setStyle(Paint.Style.FILL);   //填充样式
        paint.setStrokeWidth(5);            //画笔宽度
        //paint.setShadowLayer(1,15,15,Color.GREEN);      //设置阴影

        canvas.drawRGB(255,255,255);
        //画图
        canvas.drawCircle(200,200,150,paint);

    }
}

3、在Activity类中添加绘画显示代码。

@Bind(R.id.main_frame) FrameLayout mainFrame;

@Override
    protected void onCreate(Bundle savedInstanceState) {
        ...
        mainFrame.addView(new CustomView(MainActivity.this));

    }

运行效果如下:

我记得还有一个实现方法,是在布局文件中直接引用view类,不通过addView()方法也可以显示view。但由于忘了一些细节,没办法实现这个操作,所以在这里插个眼,等找到实现方法了TP回来。

Paint的基本参数设置

  • setAntiAlias(Boolean boolean) 设置是否开启抗锯齿

  • setColor(int color) 设置画笔颜色

  • setStrokeWidth(int width) 设置画笔宽度

  • setStyle(Paint.Style style) 设置填充样式

样式有三种:

Paint.Style.FILL 填充内部

Paint.Style.FILL_AND_STROKE 填充内部以及描边

Paint.STyle.STROKE 仅描边

其中一和二其实没有什么区别。(起初我以为画笔如果够宽,一和二就会有明显的内部、边框之分,但实验过后发现并没有区别)

  • setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置阴影(对图形无效,对字体有效)

其中radius指角度,即阴影倾斜度,dx水平位移,dy垂直位移

基本几何图形绘制

canvas本身就提供了一些基础图形的绘制方法,接下来就是各个方法的使用设置。

一条直线

方法:drawLine (float startX, float startY, float stopX, float stopY, Paint paint)

参数不用多解释,起点的横纵坐标,终点的横纵坐标,最后就是已经设置好参数的画笔。最后一项参数,在后面的绘制方法里也会有,就不再赘述了。示例如下:

canvas.drawLine(100,200,300,400,paint);

多条直线

  • 方法:drawLines (float[] pts, Paint paint)

由于每个pts的元素,都必须是以四个数,即一组点,即一条线为单位的,所以,在写多条直线的数组时,要注意数组的元素个数是否符合标准,即数的个数是否能被4整除。示例如下:

float []opts={10,10,100,100,200,200,300,300,400,400,500,500};
canvas.drawLines(opts,paint);

  • 方法:drawLines (float[] pts, int offset, int count, Paint paint)

这个方法与上个方法相比,多出来两个不同的参数。offset表示跳过的数据个数,count表示实际参与绘制的数据个数。什么意思呢?就是在绘制的时候,会先跳过已设置的offset个数的点,只将接下来的count个数的点所构成的直线绘制出来。由此可以得知,两个参数均必须是4的倍数,且两者之和不超过数据的长度。示例如下:

float []opts={10,10,100,100,200,200,300,300,400,400,500,500};
canvas.drawLines(opts,4,8,paint);
//canvas.drawLines(opts,4,4,paint);

可以看出来,第一行的运行效果,跳过了第一条直线,显示了第二、三条直线;第二行的运行效果,跳过了第一条直线,只显示了接下来四个点,也就是第二条直线;第三条直线则不再计入绘制。

单个点

方法:drawPoint (float x, float y, Paint paint)

canvas.drawPoint(100,100,paint);

多个点

  • 方法:drawPoints (float[] pts, Paint paint)

  • 方法:drawPoints (float[] pts, int offset, int count, Paint paint)

这里的方法就跟直线的很类似了,就不再赘述了。

矩形

绘制矩形有两个类:RectF类与Rect类。两个类的区别不大,目前看来的区别是构造参数类型不同。如下:

  • RectF(float left, float top, float right, float bottom)

  • Rect(int left, int top, int right, int bottom)

绘制方法:

  • 方法:drawRect (float left, float top, float right, float bottom, Paint paint)

  • 方法:drawRect (RectF rect, Paint paint)

  • 方法:drawRect (Rect r, Paint paint)

canvas.drawRect(10,10,100,100,paint);
canvas.drawRect(new Rect(120,10,210,100),paint);
canvas.drawRect(new RectF(230,10,320,100),paint);

圆角矩形

方法:drawRoundRect (RectF rect, float rx, float ry, Paint paint)

这里的rx与ry是指矩形四角生成的椭圆的x轴半径与y轴半径。

RectF rectF = new RectF(100,100,300,300);
canvas.drawRoundRect(rectF,20,20,paint);

圆形

方法:drawCircle (float cx, float cy, float radius, Paint paint)

这里的cx与cy是指圆心点的x轴与y轴坐标,radius则是圆的半径。

canvas.drawCircle(200,200,100,paint);

椭圆

椭圆是根据矩形生成的,根据矩形的左顶点与右底点坐标,决定了椭圆的x轴与y轴半径,以及中心点。

方法:drawOval (RectF oval, Paint paint)

RectF rectF = new RectF(100,100,300,200);
canvas.drawRect(rectF,paint);

paint.setColor(Color.RED);
canvas.drawOval(rectF,paint);

为了直观些观察,这里临时将填充样式改为了描边,且将绘制第二个图形的画笔颜色改变一下。

弧是椭圆的一部分,椭圆与矩形相关,所以弧也与矩形相关。

方法:drawArc (RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)

startAngle是弧开始的角度,sweepAngle是弧持续的角度,useCenter是是否需要弧的两边(详见例图)。注意,开始角度的起始位置是建立在Android的界面坐标系上的,而不是数学上的坐标系。

paint.setStyle(Paint.Style.FILL);   //填充样式
RectF rectF = new RectF(100,100,300,300);
canvas.drawArc(rectF,0,70,true,paint);

RectF rectF2 = new RectF(400,100,600,300);
canvas.drawArc(rectF2,0,70,false,paint);

paint.setStyle(Paint.Style.STROKE);     //描边样式
RectF rectF3 = new RectF(100,300,300,500);
canvas.drawArc(rectF3,0,70,true,paint);

RectF rectF4 = new RectF(400,300,600,500);
canvas.drawArc(rectF4,0,70,false,paint);

路径绘制图形

除了通过canvas直接调用绘制图形的专用方法外,还可以通过对path的参数编写并调用drawPath方法,画出想要绘制的图形。其实path有很多方法的参数跟上面的方法都很相似,这下面我就不赘述参数了。

方法:drawPath (Path path, Paint paint)

直线

  • 方法:moveTo (float x, float y)

设定直线的起始点

  • 方法:lineTo (float x, float y)

设定当前直线的终点,也是下一条直线的起始点

  • 方法:close ()

闭环,即将最后一条直线的终点与第一条的起点连接

paint.setStyle(Paint.Style.FILL);   //填充样式

Path path = new Path();
path.moveTo(10,10);
path.lineTo(10,100);
path.lineTo(300,100);
path.lineTo(500,50);
path.close();

canvas.drawPath(path,paint);

矩形

方法:addRect (RectF rect, Path.Direction dir)

矩形生成有两个方法,方法的区别主要在于矩形绘制的方向不同,一个是Path.Direction.CCW,为逆时针方向;另一个是Path.Direction.CW,为顺时针方向。为方便观察,可以通过绘制文字的生成路径来观察。

Path CCWpath = new Path();
RectF rectF1 = new RectF(50,50,240,200);
CCWpath.addRect(rectF1, Path.Direction.CCW);

Path CWpath = new Path();
RectF rectF2 = new RectF(290,50,480,200);
CWpath.addRect(rectF2, Path.Direction.CW);

canvas.drawPath(CCWpath,paint);
canvas.drawPath(CWpath,paint);

String text = "世间安得双全法,不负如来不负卿";
paint.setColor(Color.RED);
paint.setTextSize(35);
canvas.drawTextOnPath(text,CCWpath,0,18,paint);
canvas.drawTextOnPath(text,CWpath,0,18,paint);

圆角矩形

方法:addRoundRect (RectF rect, float[] radii, Path.Direction dir)

方法:addRoundRect (RectF rect, float rx, float ry, Path.Direction dir)

两个构造函数的区别主要在于第二个参数,即设定矩形的圆角大小。第一个方法可以对每个圆角进行定制,共8个数,方向则取决于绘制的方向,比如逆时针绘制圆角矩形,圆角则从左上角、左下角到右下角、右上角;而第二个方法则统一了每个圆角的大小。

Path path = new Path();
RectF rectF1 = new RectF(50,50,240,200);
path.addRoundRect(rectF1,10,15, Path.Direction.CCW);

RectF rectF2 = new RectF(290,50,480,200);
float radius[] = {10,10,20,20,30,30,40,40};
path.addRoundRect(rectF2,radius, Path.Direction.CCW);

canvas.drawPath(path,paint);

圆形路径

方法:addCircle (float x, float y, float radius, Path.Direction dir)

Path path = new Path();
path.addCircle(200,200,100, Path.Direction.CCW);
canvas.drawPath(path,paint);

椭圆路径

方法:addOval (RectF oval, Path.Direction dir)

Path path = new Path();
RectF rectF = new RectF(100,100,300,500);
path.addOval(rectF, Path.Direction.CCW);

canvas.drawPath(path,paint);

弧形路径

方法:addArc (RectF oval, float startAngle, float sweepAngle)

Path path = new Path();
RectF rectF = new RectF(100,100,300,500);
path.addArc(rectF,0,100);

canvas.drawPath(path,paint);

线段

方法:quadTo (float x1, float y1, float x2, float y2)

这个方法的特点是绘制的曲线遵循了“贝塞尔曲线”,所以在连续的绘制时看起来会很平滑,这里我也不多深究,也可能以后专门写个博客来描述一下区别吧。这里就贴一下别人的博客好了。

参考博客:quadTo()方法与lineTo()方法的区别

文字

1、Paint相关设置

paint.setColor(Color.RED);                  //画笔颜色
paint.setStrokeWidth(5);                    //画笔宽度
paint.setAntiAlias(true);                   //抗锯齿
paint.setStyle(Paint.Style.FILL);           //绘图样式
paint.setTextSize(80);                      //文字大小

paint.setFakeBoldText(true);                //是否为粗体
paint.setTextSkewX((float) -0.25);          //字体水平倾斜度

paint.setUnderlineText(true);               //下划线
paint.setStrikeThruText(true);              //删除线

paint.setTextAlign(Paint.Align.CENTER);     //对齐方式
paint.setTextScaleX(1);                     //只会将水平方向拉伸

这就是paint绘制文字时的几个主要设置选项了。画笔颜色、宽度、抗锯齿、文字大小、下划线、删除线等这些很浅显的就不具体展示了,我只对绘图样式、是否为粗体、水平倾斜度、方向拉伸做一下展示,每行字之间至少有三个设置的不同(不包括最后两行的比较,最好两行是是否为粗体和设置阴影的区别)。示例代码及效果如下:

paint.setFakeBoldText(false);
paint.setTextSkewX((float) -0.25);
paint.setTextScaleX(1);
canvas.drawText("おれはおまえが好きだ",10,100,paint);

paint.setStyle(Paint.Style.STROKE);
paint.setFakeBoldText(false);
paint.setTextSkewX((float) 0.25);
paint.setTextScaleX((float) 0.75);
canvas.drawText("わたしはあなたが好きだ",10,200,paint);

paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setFakeBoldText(false);
paint.setTextSkewX((float) -0.25);
paint.setTextScaleX((float) 1.3);
canvas.drawText("君の名は。",10,300,paint);

paint.setShadowLayer(1,15,15,Color.GRAY);
paint.setStyle(Paint.Style.FILL_AND_STROKE);
paint.setFakeBoldText(true);
paint.setTextSkewX((float) -0.25);
paint.setTextScaleX((float) 1.3);
canvas.drawText("君の名は。",10,400,paint);

还有一个属性是文字对齐方式的区别。其实这里面细致的东西有很多,我在这里放上一篇讲的很细致的文章以供参考。博客链接。我这里就只展示一下效果。

paint.setTextAlign(Paint.Align.LEFT);
canvas.drawText("君の名は。",360,100,paint);

paint.setTextAlign(Paint.Align.CENTER);
canvas.drawText("君の名は。",360,200,paint);

paint.setTextAlign(Paint.Align.RIGHT);
canvas.drawText("君の名は。",360,300,paint);

根据设置的位置、属性以及效果可以看出,如果设置左对齐,则文字会以设置的原点为左基准线临界点,向右展开;如果为居中对齐,则文字以原点为基准线中心点;右对齐则为右基准线临界点。

2、canvas绘制文字的函数

  • 方法:void drawText (String text, float x, float y, Paint paint)

  • 方法:void drawText (CharSequence text, int start, int end, float x, float y, Paint paint)

第一个方法在上面的示例已经用过了,x和y表示位置,不用多说。而第二个方法,则可以额外对传入的字符串进行截取。不过我觉得也可以对字符串进行提前截取,再放入第一个方法,所以暂时想不到第二个方法的优势之处,可能少一行代码也算优势吧。

  • 方法:void drawPosText (char[] text, int index, int count, float[] pos, Paint paint)

  • 方法:void drawPosText (String text, float[] pos, Paint paint)

这两个方法可以指定要绘制的文字的位置,跟上面一样,位置也是两两数字一组。

float pos[]=new float[]{10,200,60,300,110,400,160,500,210,600};
canvas.drawPosText("君の名は。",pos,paint);

  • 方法:void drawTextOnPath (String text, Path path, float hOffset, float vOffset, Paint paint)

  • 方法:void drawTextOnPath (char[] text, int index, int count, Path path, float hOffset, float vOffset, Paint paint)

这两个方法属于沿路径绘制,在上面的示例中也已经见过一次了。这里主要讲hOffset、vOffset,hOffset是与路径起始点的水平偏移增量,vOffset是与路径中心的垂直偏移增量。

String text = "君の名は。";

Path circlePath = new Path();
RectF rectF = new RectF(100,100,300,300);
circlePath.addRoundRect(rectF,10,15,Path.Direction.CCW);
canvas.drawPath(circlePath,paint);

Path circlePath2 = new Path();
RectF rectF2 = new RectF(400,100,600,300);
circlePath2.addRoundRect(rectF2,10,15, Path.Direction.CCW);
canvas.drawPath(circlePath2,paint);

canvas.drawTextOnPath(text,circlePath,0,0,paint);
canvas.drawTextOnPath(text,circlePath2,80,30,paint);

此外字体还有其他的样式设置等等。个人感觉暂时不会用到,就不做学习了。

后记

妈耶,我再也不一口气写这么长的博客了。写到后边,总觉得累了,都不想写的很详细了。真佩服那些能写出一本教程书的大佬。

-------------本文结束感谢您的阅读-------------