Android绘图之Paint的使用方法详解

前言

大家都知道在Android中,自定义控件是一个非常重要的知识点,而自定义控件对Android开发者是一个难点,考验开发人员对View原理的理解,对于继承View的自定义控件,一般都需要重写onDraw方法,而且往往需要开发人员能够掌握Paint这个类。

简介

The Paint class holds the style and color information about how to draw geometries, text and bitmaps.

Paint:该类保存了绘制几何图形、文本和位图的样式和颜色信息。也就是说我们可以使用Paint保存的样式和颜色,来绘制图形、文本和bitmap,这就是Paint的强大之处。接下来我们使用Paint来绘图,并且看看该类有哪些样式和颜色。

Paint的使用

使用Paint之前需要初始化

mPaint = new Paint();

设置笔(Paint)的颜色和alpha值:

mPaint.setColor(Color.BLUE);
mPaint.setAlpha(255);

注意:alpha的范围是[0..255],而不是[0..1],是一个int值。

设置画笔的样式:通过mPaint.setStyle()来设置样式。

 public enum Style {
 /**
  * Geometry and text drawn with this style will be filled, ignoring all
  * stroke-related settings in the paint.
  */
 FILL  (0),
 /**
  * Geometry and text drawn with this style will be stroked, respecting
  * the stroke-related fields on the paint.
  */
 STROKE  (1),
 /**
  * Geometry and text drawn with this style will be both filled and
  * stroked at the same time, respecting the stroke-related fields on
  * the paint. This mode can give unexpected results if the geometry
  * is oriented counter-clockwise. This restriction does not apply to
  * either FILL or STROKE.
  */
 FILL_AND_STROKE (2);

 Style(int nativeInt) {
  this.nativeInt = nativeInt;
 }
 final int nativeInt;
 }

总共有三种画笔的样式

FILL:填充内容;

STROKE:描边;

FILL_AND_STROKE:填充内容并描边。

设置画笔的宽度

mPaint.setStrokeWidth(50);

设置画笔的线帽

通过mPaint.setStrokeCap来设置线帽,总共有三种线帽

 /**
 * The Cap specifies the treatment for the beginning and ending of
 * stroked lines and paths. The default is BUTT.
 */
 public enum Cap {
 /**
  * The stroke ends with the path, and does not project beyond it.
  */
 BUTT (0),
 /**
  * The stroke projects out as a semicircle, with the center at the
  * end of the path.
  */
 ROUND (1),
 /**
  * The stroke projects out as a square, with the center at the end
  * of the path.
  */
 SQUARE (2);

 private Cap(int nativeInt) {
  this.nativeInt = nativeInt;
 }
 final int nativeInt;
 }

BUTT:没有线帽,默认模式

ROUND:圆形

SQUARE:方形

三种线帽对比:

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 mPaint.setColor(Color.BLUE);
 mPaint.setAlpha(255);

 //设置画笔的样式
 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 //画笔的宽度
 mPaint.setStrokeWidth(50);
 mPaint.setStrokeCap(Paint.Cap.SQUARE);//方形
 mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线

 Path path = new Path();
 path.moveTo(100, 100);
 path.lineTo(300, 100);
 canvas.drawPath(path, mPaint);

 mPaint.reset();//重置
 mPaint.setColor(Color.RED);
 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 mPaint.setStrokeWidth(50);
 mPaint.setStrokeCap(Paint.Cap.ROUND);//圆形
 mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线

 Path path1 = new Path();
 path1.moveTo(100, 200);
 path1.lineTo(300, 200);
 canvas.drawPath(path1, mPaint);

 mPaint.reset();//重置
 mPaint.setColor(Color.GREEN);
 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 mPaint.setStrokeWidth(50);
 mPaint.setStrokeCap(Paint.Cap.BUTT);//没有
 mPaint.setStrokeJoin(Paint.Join.BEVEL);//直线

 Path path2 = new Path();
 path2.moveTo(100, 300);
 path2.lineTo(300, 300);
 canvas.drawPath(path2, mPaint);

 }

上面代码中有个重置画笔,这时候需要重新设置画笔。


线帽对比

设置Join

使用setStrokeJoin方法来设置Join,Join有三种类型:

BEVEL:直线

ROUND:圆角

MITER:锐角

 @Override
 protected void onDraw(Canvas canvas) {
 super.onDraw(canvas);
 mPaint.setColor(Color.BLUE);
 mPaint.setAlpha(255);
 mPaint.setStyle(Paint.Style.STROKE);//设置画笔的样式
 mPaint.setStrokeWidth(50);//画笔的宽度
 mPaint.setStrokeCap(Paint.Cap.BUTT);//线帽
 mPaint.setStrokeJoin(Paint.Join.BEVEL);

 Path path = new Path();
 path.moveTo(100, 100);
 path.lineTo(300, 100);
 path.lineTo(100, 300);
 path.close();
 canvas.drawPath(path, mPaint);

 mPaint.reset();//重置
 mPaint.setColor(Color.RED);
 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 mPaint.setStrokeWidth(50);
 mPaint.setStrokeCap(Paint.Cap.BUTT);//圆形
 mPaint.setStrokeJoin(Paint.Join.ROUND);//圆弧

 Path path1 = new Path();
 path1.moveTo(100, 400);
 path1.lineTo(300, 400);
 path1.lineTo(100, 700);
 path1.close();
 canvas.drawPath(path1, mPaint);

 mPaint.reset();//重置
 mPaint.setColor(Color.GREEN);
 mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
 mPaint.setStrokeWidth(50);
 mPaint.setStrokeCap(Paint.Cap.BUTT);//没有
 mPaint.setStrokeJoin(Paint.Join.MITER);//锐角

 Path path2 = new Path();
 path2.moveTo(100, 800);
 path2.lineTo(300, 800);
 path2.lineTo(100, 1100);
 path2.close();
 canvas.drawPath(path2, mPaint);

 }

Join对比

以上就是Join三种类型对比。

设置防锯齿

mPaint.setAntiAlias(true);

如果设置防锯齿,会损失一定的性能

抖动处理

使用mPaint.setDither()方法,设置是否使用图像抖动处理。会使绘制的图片等颜色更加的清晰以及饱满,也是损失性能。

使用Path绘制图形


Path绘制图形

点组成线,线组成面,这样Path可以绘制各种各样的图形,可以说是无所不能的了,但是Path也提供了很多方法,来绘制图形。

文本绘制

上文中,介绍了Paint画笔,和绘制了一些图形。但是介绍Paint的时候,我们知道它可以绘制图形,文本和bitmap,所以Paint是非常强大的了,我们看下Paint是如何绘制文本的。

设置字符之间的间距

setLetterSpacing

设置文本删除线

mPaint.setStrikeThruText(true);

是否设置下划线

mPaint.setUnderlineText(true);

设置文本大小

mPaint.setTextSize(textSize);

设置字体类型

mPaint.setTypeface(Typeface.BOLD);
// Style
public static final int NORMAL = 0;//常规
public static final int BOLD = 1;//粗体
public static final int ITALIC = 2; //斜体
public static final int BOLD_ITALIC = 3;//粗斜体

字体类型有以上四种类型可以设置。

加载自定义字体

Typeface.create(familyName, style)

文字倾斜

mPaint.setTextSkewX(-0.25f);

文字倾斜默认为0,官方推荐的-0.25f是斜体

文本对齐方式

mPaint.setTextAlign(Align.LEFT)

有三种:

public enum Align {
  /**
   * The text is drawn to the right of the x,y origin
   */
  LEFT (0),//左对齐
  /**
   * The text is drawn centered horizontally on the x,y origin
   */
  CENTER (1),//居中
  /**
   * The text is drawn to the left of the x,y origin
   */
  RIGHT (2);//右对齐

  private Align(int nativeInt) {
   this.nativeInt = nativeInt;
  }
  final int nativeInt;
 }

计算制定长度的字符串

int breadText = mPaint.breakText(text, measureForwards, maxWidth, measuredWidth)

注意:字符长度、字符个数、显示的时候是真实的长度

Rect bounds获取文本的矩形区域(宽高)
mPaint.getTextBounds(text, index, count, bounds)
mPaint.getTextBounds(text, start, end, bounds)

//获取文本的宽度,和上面类似,但是是一个比较粗略的结果
float measureText = mPaint.measureText(str);
//获取文本的宽度,和上面类似,但是是比较精准的。
float[] measuredWidth = new float[10];

//measuredWidth得到每一个字符的宽度;textWidths字符数
int textWidths = mPaint.getTextWidths(str, measuredWidth);
mPaint.getTextWidths(text, start, end, widths)

使用drawText绘制文本

public class PaintView extends View {

 private Paint mPaint;
 private String text = "你是我世界之光,我心另一半";

 public PaintView(Context context) {
  this(context,null);
 }

 public PaintView(Context context, @Nullable AttributeSet attrs) {
  this(context, attrs,0);
 }

 public PaintView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  super(context, attrs, defStyleAttr);
  init(context, attrs, defStyleAttr);
 }

 private void init(Context context, AttributeSet attrs, int defStyleAttr) {
  mPaint = new Paint();
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  mPaint.setColor(Color.BLUE);
  mPaint.setStyle(Paint.Style.STROKE);//设置画笔的样式
  mPaint.setStrokeCap(Paint.Cap.BUTT);//线帽
  mPaint.setStrokeJoin(Paint.Join.BEVEL);

  int top = 100;
  int baselineX = 0;
  mPaint.setTextSize(50);
  mPaint.setTextAlign(Paint.Align.LEFT);

  canvas.drawLine(0, top, 2000, top, mPaint);

  //文本Metrics
  Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
  float baselineY = top - fontMetrics.top;
  canvas.drawText(text, baselineX, baselineY, mPaint);

 }
}

绘制文本

绘制文本时,还有一个很重要的知识点就是基线的确定

DrawText 基线的确定

在自定义控件的时候,有时候会用到DrawText 方法.

先把自定义TextView的贴出来

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

  int x = getPaddingLeft();

  //dy 代表的是:高度的一半到 baseLine的距离
  Paint.FontMetricsInt fontMetrics = paint.getFontMetricsInt();
  // top 是一个负值 bottom 是一个正值 top,bttom的值代表是 bottom是baseLine到文字底部的距离(正值)
  // 必须要清楚的,可以自己打印就好
  int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
  int baseLine = getHeight()/2 + dy;

  canvas.drawText(costom_text,x,baseLine,paint);
 }
/**
  * Draw the text, with origin at (x,y), using the specified paint. The
  * origin is interpreted based on the Align setting in the paint.
  *
  * @param text The text to be drawn
  * @param x  The x-coordinate of the origin of the text being drawn
  * @param y  The y-coordinate of the baseline of the text being drawn
  * @param paint The paint used for the text (e.g. color, size, style)
  */
 public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {
  native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,
    paint.getNativeInstance(), paint.mNativeTypeface);
 }

x,y 分别表示 基线的开始坐标,并不是 文字左上角的坐标,因为文字的绘制是以基线为基础的

图中的 五角星 所在的线 就是基线 BaseLine,那么如何确定基线的x,y坐标呢?

首写我们先确定一下x坐标 :int x = getPaddingLeft(); 也就是文字距左边的距离

y坐标:

1、我们先计算一下文字高度的一半到 baseLine的距离。

int dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;

2、之后我们再使用控件高度的一般,加上文字高度的一半到 baseLine的距离,就是基线的y坐标

int baseLine = getHeight()/2 + dy;

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • Android中区别Drawable Bitmap Canvas Paint

    1.概念区别: 很多网友刚刚开始学习Android平台,对于Drawable.Bitmap.Canvas和Paint它们之间的概念不是很清楚,其实它们除了Drawable外早在Sun的J2ME中就已经出现了,但是在Android平台中,Bitmap.Canvas相关的都有所变化. 首先让我们理解下Android平台中的显示类是View,但是还提供了底层图形类android.graphics,今天所说的这些均为graphics底层图形接口. Bitmap - 称作位图 一般位图的文件格式后缀为bm

  • Android自定义View中Paint、Rect、Canvas介绍(一)

    自定义View对于新手而言貌似是一个很复杂的东西.格式,各函数的意义.对于大神经常忘记各函数及一些参数的具体写法及意义,刚好在做一个风车效果,把过程及遇到的问题都写下来 1.如何自定义一个View public class LeafView extends View { private String TAG = "--------LeafView"; public LeafView(Context context, AttributeSet attrs) { super(context

  • Android画图之抗锯齿paint和Canvas两种方式实例

    在画图的时候,图片如果旋转或缩放之后,总是会出现那些华丽的锯齿.其实Android自带了解决方式. 方法一:给Paint加上抗锯齿标志.然后将Paint对象作为参数传给canvas的绘制方法. paint.setAntiAlias(true); 方法二:给Canvas加上抗锯齿标志. 有些地方不能用paint的,就直接给canvas加抗锯齿,更方便. 复制代码 代码如下: canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_AL

  • Android开发之图形图像与动画(一)Paint和Canvas类学习

    Paint类 *Paint类代表画笔,用来描述图形的颜色和风格,如线宽,颜色,透明度和填充效果等信息. *使用Paint类时,需要先创建该类的对象,可以通过该类的构造函数实现.通常情况的实现代码是: *Paintpaint=newPaint(); *创建完Paint对象后,可以通过该对象提供的方法对画笔的默认设置进行改变 Canvas *Canvas类代表画布,通过该类提供的构造方法,可以绘制各种图形. *通常情况下,要在Android中绘图,需要先创建一个继承自View类的视图,并且在该类中重

  • Android中Paint类和Canvas类的方法汇总

    Paint类的常用的方法 1.setColor方法,用于设置画笔的颜色, public void setColor(int color)//参数color为颜色值,也可以使用Color类定义的颜色 Color.BLACK:黑色 Color.BLUE:蓝色 Color.CYAN:青绿色 Color.DKGRAY:灰黑色 Color.YELLOW:黄色 Color.GRAY:灰色 Color.GREEN:绿色 Color.LTGRAY:浅绿色 Color.MAGENTA:红紫色 Color.TRAN

  • Android绘图之Paint的使用方法详解

    前言 大家都知道在Android中,自定义控件是一个非常重要的知识点,而自定义控件对Android开发者是一个难点,考验开发人员对View原理的理解,对于继承View的自定义控件,一般都需要重写onDraw方法,而且往往需要开发人员能够掌握Paint这个类. 简介 The Paint class holds the style and color information about how to draw geometries, text and bitmaps. Paint:该类保存了绘制几何

  • 利用Android实现光影流动特效的方法详解

    目录 前言 MaskFilter 类简介 MaskFilter 的几种效果对比 光影流动 光影流动效果1 光影流动效果2 光影流动效果3 光影流动效果4:光影沿贝塞尔曲线流动 总结 前言 Flutter 的画笔类 Paint 提供了很多图形绘制的配置属性,来供我们绘制更丰富多彩的图形.前面几篇我们介绍了 shader 属性来绘制全屏渐变的聊天气泡背景.渐变流动的边框和毛玻璃效果的背景图片,具体可以参考下面几篇文章. 让你的聊天气泡丰富多彩! 手把手教你实现一个流动的渐变色边框 利用光影变化构建立

  • Android程序打包为APK的方法详解

    Andriod安装包文件(Android Package),简称APK,后缀名为.apk. 1.生成未签名的安装包 Build -> Build Bundle(s)/APK(s) -> Build APK(s)    会生成一个未签名的apk文件,默认为debug版,可以正常安装使用. 可以 Build -> Select Build Variant -> 选择生成的apk版本(debug.release),再 Build -> Build Bundle(s)/APK(s)

  • Android适配底部虚拟按键的方法详解

    最近项目进行适配的时候发现部分(如华为手机)存在底部虚拟按键的手机会因为虚拟按键的存在导致挡住部分界面,因为需要全屏显示,故调用虚拟按键隐藏方法使之隐藏,然而发现出现如下问题: 手动操作隐藏虚拟按键后出现长白条区域 不自动隐藏 滑出状态栏后虚拟按键也出来,状态栏隐藏后虚拟却不跟着隐藏 在没有虚拟按键的设备上影响了SurfaceView全屏显示图传(原本全屏显示的图传在切出去再进来时变成了小屏显示) 通过google了很多方法并尝试终于解决了这个问题,达到如下效果: 每次进入界面时虚拟按键自动隐藏

  • Android 通过代码安装 APK的方法详解

    在 APK 开发中,通过 Java 代码来打开系统的安装程序以安装 APK 并不是什么难事,一般的 Android 系统都有开放这一功能. 但随着 Android系统版本的迭代,其对于权限的把控越来越严格,或者说是变得越来越注重安全性.这就导致了以前可以通过很简单的几行代码就能实现的功能,现在要复杂很多. 对于通过代码打开系统安装程序这一功能的限制,其分水岭在 Android7.0,即 Android N 上.通常在 Android N以上的系统使用一种做法,以下则使用另一种做法. 传统的通过代

  • Android学习之Span的使用方法详解

    目录 Span集合 段落类Span 其他Span 展示效果 小试牛刀 小结 Span集合 段落类Span BulletSpan 为段落开头增加项目符号并支持大小.颜色.弧度 span.append(SpannableString("BulletSpan").also { it.setSpan(BulletSpan(40, Color.RED), 0, 10, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) }) QuoteSpan 为段落开头增加垂直引用线 sp

  • Android编程自定义AlertDialog样式的方法详解

    本文实例讲述了Android编程自定义AlertDialog样式的方法.分享给大家供大家参考,具体如下: 开发的时候,通常我们要自定义AlertDialog来满足我们的功能需求: 比如弹出对话框中可以输入信息,或者要展示且有选择功能的列表,或者要实现特定的UI风格等.那么我们可以通过以下方式来实现. 方法一:完全自定义AlertDialog的layout.如我们要实现有输入框的AlertDialog布局custom_dialog.xml: <?xml version="1.0"

  • Android使用jni调用c++/c方法详解

    1.下载ndk 2.编写jni的加载类 参考例子: public class JniTest { public native String append(String str1, String str2); static { System.loadLibrary("JniTest"); } } 以上append方法就是要调用c++/c中的方法. JniTest是在Android.mk里约束好的,关于Android.mk的编写具体在后面详解. 3.使用javah -jni生成.h文件 编

  • Android编程解析XML文件的方法详解【基于XmlPullParser】

    本文实例讲述了Android编程解析XML文件的方法.分享给大家供大家参考,具体如下: 前言 在学习Android的Framework层源码时,Android大量的使用XmlPullParser来解析xml文件的源码.因此,这里也顺道介绍一下XmlPullParser的使用. XML XML(eXtensible Markup Language)中文名为可扩展标记语言.标记指计算机所能理解的信息符号,通过此种标记,计算机之间可以处理包含各种信息的文章等. 用途 XML设计用了传送及携带数据信息,

  • Android编程实现自定义手势的方法详解

    本文实例讲述了Android编程实现自定义手势的方法.分享给大家供大家参考,具体如下: 之前介绍过如何在Android程序中使用手势,主要是系统默认提供的几个手势,这次介绍一下如何自定义手势,以及如何对其进行管理. 先介绍一下Android系统对手势的管理,Android系统允许应用程序把用户的手势以文件的形式保存以前,以后要使用这些手势只需要加载这个手势库文件即可,同时Android系统还提供了诸如手势识别.查找及删除等的函数接口,具体如下: 一.加载手势库文件: staticGestureL

随机推荐