资讯

精准传达 • 有效沟通

从品牌网站建设到网络营销策划,从策略到执行的一站式服务

android绘制,android绘制人像轮廓

android绘图之Paint(1)

Paint 讲解开篇

创新互联从2013年成立,先为德州等服务建站,德州等地企业,进行企业商务咨询服务。为德州企业网站制作PC+手机+微官网三网同步一站式服务解决您的所有建站问题。

android中绘制特定图案类似显示中的绘画需要画笔和画纸,为此android提供了Paint和Canvas。

Paint和Canvas分别代表画笔和画布。

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

Paint类似画笔,保存着绘制文本,图形,图片的样式和颜色信息。(颜色,宽度,粗细,透明度,字体样式,字体大小)。

提供了三种初始化方法:

Paint(),

Paint(int flags),

Paint(Paint paint)。

第二种构造函数传入了flag,相当于无参构造之后调用了setFlags(int),有很多种flag,这里举个例子UNDERLINE_TEXT_FLAG(其他的flag见developers文档),绘制文字时会有下划线,多个flag或可以达到设置多个flag的目的。

Paint常用的设置函数:

setAlpha()透明度

setAntiAlias()抗锯齿

setColor(),setARGB()设置颜色

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

setStrokeCap(Paint.Cap cap) 画笔的样式(落笔,收笔时)

setStrokeJoin(Paint.Join join)连接点的样式

setStrokeWidth(float width)设置画笔宽度

setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置阴影

setTextSize(float textSize) 字体大小

setTextAlign(Paint.Align.RIGHT)设置字体对齐方式

后期讲解:

setColorFilter(ColorFilter filter) 设置颜色过滤

setUnderlineText(true) 下划线

setPathEffect() 设置路径效果

setTypeface() 设置字体风格

setFilterBitmap() 设置图片过滤

setXfermode(Xfermode xfermode) xfermode设置图像混合模式

setShader(Shader shader) 设置shader包括渐变shader,图片shader

。。。。。。。。

setAlpha(int a) 设置透明度,a 值得范围 [0..255],仅仅改变setColor()的颜色的透明度,不改变颜色值,0是完全透明,255完全不透明。

注意:需要先调用setColor(),再调用setAlpha才会生效,否则将会被覆盖,因为setColor中包含了alpha。

setStyle设置填充样式,所谓填充的样式指只绘制线或者绘制同时填充:

Paint.Style.FILL 填充内部,会把闭合区域填充颜色

Paint.Style.FILL_AND_STROKE 填充内部和描边

Paint.Style.STROKE 仅描边,仅仅绘制边界

默认FILL 填充内部,

STROKE样式

FILL_AND_STROKE样式和FILL 类似但当画笔宽度很宽时会产生不同

setColor,setARGB设置颜色,设置画笔的颜色,setARGB()参数范围0-255。

setAntiAlias(boolean) 抗锯齿,使边界更顺滑(有些屏幕分辨率不高,导致像素点比较大,绘制边界可能会有颗粒感,打开抗锯齿边界颗粒感会减少)。

setTextSize(float textSize) 字体大小,单位是px,如果是dp要注意转换。

setTextAlign(Paint.Align.RIGHT)设置字体对齐方式,根据下面的实例可以,对齐方式基于开始绘制的点。

一起讲解:

mPaint.setTextSize(50);

mPaint.setTextAlign(Paint.Align.LEFT);

canvas.drawText("android Paint 学习 LEFT",300,300,mPaint);

mPaint.setTextAlign(Paint.Align.CENTER);

canvas.drawText("android Paint 学习 CENTER",300,400,mPaint);

mPaint.setTextAlign(Paint.Align.RIGHT);

canvas.drawText("android Paint 学习 RIGHT",300,500,mPaint);

setTextAlign对齐是相对于开始绘制的坐标点。

setShadowLayer(float radius, float dx, float dy, int shadowColor) 设置阴影

mPaint.setShadowLayer(10,-20,30,Color.GRAY);

canvas.drawText("android Paint 学习 CENTER",getWidth()/2,450,mPaint);

setShadowLayer(float radius, float dx, float dy, int shadowColor)

Radius设置角度,dx,dy控制字体的上下左右出现,有正负之分,dx的正负代表右左,dy的正负代表下上。

setStrokeCap(Paint.Cap.ROUND)

设置绘制起始点和结尾点的样式,

三种样式ROUND,BUTT,SQUARE

Cap.ROUND(圆形)、Cap.SQUARE(方形)、Paint.Cap.BUTT(无)

ROUND:

BUTT和SQUARE差别不大:

setStrokeJoin(Paint.Join join),设置绘制path连接点的样式

mPaint.setStrokeJoin(Paint.Join.ROUND);

// mPaint.setStrokeJoin(Paint.Join.MITER);

// mPaint.setStrokeJoin(Paint.Join.BEVEL);

Join.MITER(结合处为锐角)、

Join.Round(结合处为圆弧)、

Join.BEVEL(结合处为直线)

ROUND样式:

MITER样式:

BEVEL样式:

setXfermode(Xfermode xfermode) xfermode设置图像混合模式

setShader(Shader shader) 设置shader包括渐变shader,图片shader

还有很多其他属性会后续讲解。

android绘图之Paint(1)

android绘图之Canvas基础(2)

Android绘图之Path(3)

Android绘图之drawText绘制文本相关(4)

Android绘图之Canvas概念理解(5)

Android绘图之Canvas变换(6)

Android绘图之Canvas状态保存和恢复(7)

Android绘图之PathEffect (8)

Android绘图之LinearGradient线性渐变(9)

Android绘图之SweepGradient(10)

Android绘图之RadialGradient 放射渐变(11)

Android绘制之BitmapShader(12)

Android绘图之ComposeShader,PorterDuff.mode及Xfermode(13)

Android绘图之drawText,getTextBounds,measureText,FontMetrics,基线(14)

Android绘图之贝塞尔曲线简介(15)

Android绘图之PathMeasure(16)

Android 动态修改渐变 GradientDrawable

五、Android绘制知识总结(Xfermode和硬件加速)

Xfermode表示图层的混合模式,用于描述两个图层之间进行融合时,像素点进行计算的规则。

在API16之前,Xfermode有3个子类:AvoidXfermode、PixelXorXfermode、PorterDuffXfermode。但在API16以后,前两个已经过时,甚至从源码里移除,所以我们只需学习 PorterDuffXfermode 即可。

PorterDuffXfermode 最早是在1984年由Porter和Duff两人发表的论文《Compositing Digital Images》中出现,所以该混合模式也根据作者来命名。

PorterDuffXfermode 构造函数需要指定一个 PorterDuff.Mode ,而PorterDuff.Mode在以下地方都会涉及:

它提供18种模式可选项:

各种模式下的效果如下图所示:

这里可以发现,两种效果是不一样的,谷歌官方给的是第一种,但是,通常情况应该是第二种,具体原因可 参考该文章 。比如我们画一个矩形,应该按第二种效果来考虑,因为源图和目标图大小不一致;如果画相同大小的Bitmap,则按第一种做。

在实际应用中,我们可以从以下三个方面来决定使用哪种模式:

1、没有硬件加速:

invalidate the view hierarchy ------ draw the view hierarchy

2、有硬件加速:

invalidate the view hierarchy ------ record and update the display list ------ draw the display list

1、绘制不正确:可能使用了不支持硬件加速的操作, 需要关闭硬件加速或者绕过该操作

2、抛出异常:可能使用了不支持硬件加速的操作, 需要关闭硬件加速或者绕过该操作

在Android系统中,有4个不同级别的打开或者关闭硬件加速操作:

1、Application级别:

application android:hardwareAccelerated="false"

默认为true,用于控制这个app是否开启硬件加速。

2、Activity级别:

activity android:hardwareAccelerated="false"

3、Window级别:(只支持开启操作)

getWindow().setFlags(WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED,WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED);

4、View级别:(只支持关闭操作)

view.setLayerType(View.LAYER_TYPE_HARDWARE, null);

LAYER_TYPE_HARDWARE ,使用硬件加速(GPU)进行绘制

LAYER_TYPE_SOFTWARE ,使用CPU进行绘制

或者布局文件中,指定以下属性:

android:layerType="software"

1、view.isHardwareAccelerated()

如果返回true,表示view挂在一个开启了硬件加速的Window之下,也就意味着,它在绘制时,并不一定开启了硬件加速。

2、canvas.isHardwareAccelerated()

如果返回true,因为着canvas在绘制的时候启用了硬件加速,尽量采用此方法来判断是否开启了硬件加速。

Android - View 绘制流程

我们知道,在 Android 中,View 绘制主要包含 3 大流程:

Android 中,主要有两种视图: View 和 ViewGroup ,其中:

虽然 ViewGroup 继承于 View ,但是在 View 绘制三大流程中,某些流程需要区分 View 和 ViewGroup ,它们之间的操作并不完全相同,比如:

对 View 进行测量,主要包含两个步骤:

对于第一个步骤,即求取 View 的 MeasureSpec ,首先我们来看下 MeasureSpec 的源码定义:

MeasureSpec 是 View 的一个公有静态内部类,它是一个 32 位的 int 值,高 2 位表示 SpecMode(测量模式),低 30 位表示 SpecSize(测量尺寸/测量大小)。

MeasureSpec 将两个数据打包到一个 int 值上,可以减少对象内存分配,并且其提供了相应的工具方法可以很方便地让我们从一个 int 值中抽取出 View 的 SpecMode 和 SpecSize。

一个 MeasureSpec 表达的是:该 View 在该种测量模式(SpecMode)下对应的测量尺寸(SpecSize)。其中,SpecMode 有三种类型:

对 View 进行测量,最关键的一步就是计算得到 View 的 MeasureSpec ,子View 在创建时,可以指定不同的 LayoutParams (布局参数), LayoutParams 的源码主要内容如下所示:

其中:

LayoutParams 会受到父容器的 MeasureSpec 的影响,测量过程会依据两者之间的相互约束最终生成子View 的 MeasureSpec ,完成 View 的测量规格。

简而言之,View 的 MeasureSpec 受自身的 LayoutParams 和父容器的 MeasureSpec 共同决定( DecorView 的 MeasureSpec 是由自身的 LayoutParams 和屏幕尺寸共同决定,参考后文)。也因此,如果要求取子View 的 MeasureSpec ,那么首先就需要知道父容器的 MeasureSpec ,层层逆推而上,即最终就是需要知道顶层View(即 DecorView )的 MeasureSpec ,这样才能一层层传递下来,这整个过程需要结合 Activity 的启动过程进行分析。

我们知道,在 Android 中, Activity 是作为视图组件存在,主要就是在手机上显示视图界面,可以供用户操作, Activity 就是 Andorid 中与用户直接交互最多的系统组件。

Activity 的基本视图层次结构如下所示:

Activity 中,实际承载视图的组件是 Window (更具体来说为 PhoneWindow ),顶层View 是 DecorView ,它是一个 FrameLayout , DecorView 内部是一个 LinearLayout ,该 LinearLayout 由两部分组成(不同 Android 版本或主题稍有差异): TitleView 和 ContentView ,其中, TitleView 就是标题栏,也就是我们常说的 TitleBar 或 ActionBar , ContentView 就是内容栏,它也是一个 FrameLayout ,主要用于承载我们的自定义根布局,即当我们调用 setContentView(...) 时,其实就是把我们自定义的布局设置到该 ContentView 中。

当 Activity 启动完成后,最终就会渲染出上述层次结构的视图。

因此,如果我们要求取得到子View 的 MeasureSpec ,那么第一步就是求取得到顶层View(即 DecorView )的 MeasureSpec 。大致过程如下所示:

经过上述步骤求取得到 View 的 MeasureSpec 后,接下来就可以真正对 View 进行测量,求取 View 的最终测量宽/高:

Android 内部对视图进行测量的过程是由 View#measure(int, int) 方法负责的,但是对于 View 和 ViewGroup ,其具体测量过程有所差异。

因此,对于测量过程,我们分别对 View 和 ViewGroup 进行分析:

综上,无论是对 View 的测量还是 ViewGroup 的测量,都是由 View#measure(int widthMeasureSpec, int heightMeasureSpec) 方法负责,然后真正执行 View 测量的是 View 的 onMeasure(int widthMeasureSpec, int heightMeasureSpec) 方法。

具体来说, View 直接在 onMeasure(...) 中测量并设置自己的最终测量宽/高。在默认测量情况下, View 的测量宽/高由其父容器的 MeasureSpec 和自身的 LayoutParams 共同决定,当 View 自身的测量模式为 LayoutParams.UNSPECIFIED 时,其测量宽/高为 android:minWidth / android:minHeight 和其背景宽/高之间的较大值,其余情况皆为自身 MeasureSpec 指定的测量尺寸。

而对于 ViewGroup 来说,由于布局特性的丰富性,只能自己手动覆写 onMeasure(...) 方法,实现自定义测量过程,但是总的思想都是先测量 子View 大小,最终才能确定自己的测量大小。

当确定了 View 的测量大小后,接下来就可以来确定 View 的布局位置了,也即将 View 放置到屏幕具体哪个位置。

View 的布局过程由 View#layout(...) 负责,其源码如下:

View#layout(...) 主要就做了两件事:

ViewGroup 的布局流程由 ViewGroup#layout(...) 负责,其源码如下:

可以看到, ViewGroup#layout(...) 最终也是通过 View#layout(...) 完成自身的布局过程,一个注意的点是, ViewGroup#layout(...) 是一个 final 方法,因此子类无法覆写该方法,主要是 ViewGroup#layout(...) 方法内部对子视图动画效果进行了相关设置。

由于 ViewGroup#layout(...) 内部最终调用的还是 View#layout(...) ,因此, ViewGroup#onLayout(...) 就会得到回调,用于处理 子View 的布局放置,其源码如下:

由于不同的 ViewGroup ,其布局特性不同,因此 ViewGroup#onLayout(...) 是一个抽象方法,交由 ViewGroup 子类依据自己的布局特性,摆放其 子View 的位置。

当 View 的测量大小,布局位置都确定后,就可以最终将该 View 绘制到屏幕上了。

View 的绘制过程由 View#draw(...) 方法负责,其源码如下:

其实注释已经写的很清楚了, View#draw(...) 主要做了以下 6 件事:

我们知道,在 Activity 启动过程中,会调用到 ActivityThread.handleResumeActivity(...) ,该方法就是 View 视图绘制的起始之处:

可以看到, ActivityThread.handleResumeActivity(...) 主要就是获取到当前 Activity 绑定的 ViewManager ,最后调用 ViewManager.addView(...) 方法将 DecorView 设置到 PhoneWindow 上,也即设置到当前 Activity 上。 ViewManager 是一个接口, WindowManager 继承 ViewManager ,而 WindowManagerImpl 实现了接口 WindowManager ,此处的 ViewManager.addView(...) 实际上调用的是 WindowManagerImpl.addView(...) ,源码如下所示:

WindowManagerImpl.addView(...) 内部转发到 WindowManagerGlobal.addView(...) :

在 WindowManagerGlobal.addView(...) 内部,会创建一个 ViewRootImpl 实例,然后调用 ViewRootImpl.setView(...) 将 ViewRootImpl 与 DecorView 关联到一起:

ViewRootImpl.setView(...) 内部首先关联了传递过来的 DecorView (通过属性 mView 指向 DecorView 即可建立关联),然后最终调用 requestLayout() ,而 requestLayout() 内部又会调用方法 scheduleTraversals() :

ViewRootImpl.scheduleTraversals() 内部主要做了两件事:

Choreographer.postCallback(...) 会申请一次 VSYNC 中断信号,当 VSYNC 信号到达时,便会回调 Choreographer.doFrame(...) 方法,内部会触发已经添加的回调任务, Choreographer 的回调任务有以下四种类型:

因此, ViewRootImpl.scheduleTraversals(...) 内部通过 mChoreographer.postCallback(Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null) 发送的异步视图渲染消息就会得到回调,即回调 mTra


文章名称:android绘制,android绘制人像轮廓
文章位置:http://cdkjz.cn/article/dscojpc.html
多年建站经验

多一份参考,总有益处

联系快上网,免费获得专属《策划方案》及报价

咨询相关问题或预约面谈,可以通过以下方式与我们联系

大客户专线   成都:13518219792   座机:028-86922220