【Android_View】ImageView源码简析笔记(三)
来源:互联网 发布:禁止网络标语 编辑:程序博客网 时间:2024/06/03 15:16
ImageView源码简析
ImageView的绘制–onDraw()
在【Android_View】ImageView源码简析笔记(二)一文中,我们简要回顾了ImageView的测量方法即onMeasure().
我们知道,View的绘制基本上有个【三步论】:测量—布局—绘制。
对于【布局】,很明显,这适应于ViewGroup,即父布局对子View进行布局操作,确定各个子View的布局位置。而这次我们探讨的ImageView是直接继承于VIew的,那么在这就不需考虑相关的布局问题。接下来我们一起看看ImageView的绘制—onDraw()吧!
那么老规矩,我们先看onDraw()方法的源代码:
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //Drawable对象为空,不作任何操作 if (mDrawable == null) { return; } //Drawable对象的宽高为0,不作任何操作 if (mDrawableWidth == 0 || mDrawableHeight == 0) { return; } if (mDrawMatrix == null && mPaddingTop == 0 && mPaddingLeft == 0) { //Drawable的转换矩阵为空 且 图像紧邻坐标轴与周围没有边距 //===> 直接绘制 mDrawable.draw(canvas); } else { //返回Canvas私有堆栈上矩阵/剪辑状态的数量。 //这等于#save()调用 - #restore()调用。 final int saveCount = canvas.getSaveCount(); //保存画布状态 canvas.save(); if (mCropToPadding) { final int scrollX = mScrollX; final int scrollY = mScrollY; //canvas生成矩形裁剪区域 canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop, scrollX + mRight - mLeft - mPaddingRight, scrollY + mBottom - mTop - mPaddingBottom); } //canvas移动 canvas.translate(mPaddingLeft, mPaddingTop); if (mDrawMatrix != null) { //矩阵转换 canvas.concat(mDrawMatrix); } //在canvas上绘制mDrawable mDrawable.draw(canvas); //canvas恢复状态 canvas.restoreToCount(saveCount); } }
首先来看clipRect()方法。
public boolean clipRect(float left, float top, float right, float bottom) { return native_clipRect(mNativeCanvasWrapper, left, top, right, bottom, Region.Op.INTERSECT.nativeInt); }
这是Canvas类中的方法,我们都知道canvas就是View体系中的画布,所有的图形图像都要在这上面显示。
clipRect()看名称是”裁剪”矩形区域的。而实际上,见名之意,这个方法主要是用来设定canvas的显示区域的。
四个形参分别代表—矩形裁剪区左、上、右、下坐标的位置。
当生成的裁剪区域不为空时,返回true。
特别注意,这个剪切,是不影响【原有的】【已经绘制好的】图形的。
再来看translate()方法:
public void translate(float dx, float dy) { native_translate(mNativeCanvasWrapper, dx, dy); }
很明显,根据前缀我们可以看出,native_translate()这个方法是没有java方法体的。根据注释:
Preconcat the current matrix with the specified translation * @param dx The distance to translate in X * @param dy The distance to translate in Y
可知,这是用对【当前矩阵】进行指定的转换。
Preconcat
上面这个单词,在实际查询中并没有找到与之匹配的直接到含义。
在Stack Overflow中找到一个答案供大家参考:
上面说到:这只是定义的一种矩阵操作。当然与之对应的还有一种操作叫做:【postConcat】。
还有一篇比较详细的解读,也来自Stack Overflow。附上地址,供大家参考:参考解释
不过,简单点看:【preconcat】和【postConcat】实际代表着两种不同的矩阵操作。
举个栗子:M.preConcat(other) 与M.postConcat(other)
【preconcat】 意味着 M’ = M * other
【postConcat】意味着 M’ = other * M
好了,再来看canvas.concat(mDrawMatrix);
public void concat(@Nullable Matrix matrix) { if (matrix != null) native_concat(mNativeCanvasWrapper, matrix.native_instance); }
原方法的注释为:
Preconcat the current matrix with the specified matrix. If the specified matrix is null, this method does nothing.
很明显,就是用指定的矩阵(the specified matrix)【Preconcat操作】当前的矩阵。还有:若指定的矩阵为空,则不进行任何操作。
在onDraw()方法中,
canvas.concat(mDrawMatrix);
的含义是:用onMeasure()方法设定好的图像转换矩阵mDrawMatrix来操作canvas已达到完成相应的显示效果。有关mDrawMatrix的相应操作与赋值,请参考上篇文章所描述的内容。
通过源码我们可以看到,执行完了相应的矩阵操作后,紧接着就是绘制:
mDrawable.draw(canvas);
draw()方法的源码如下:
public abstract void draw(@NonNull Canvas canvas);
这是Drawable类中的方法。可以看到,因为有”abstract”我们知道这是一个抽象方法,当中是不存在方法体的。
再看注释:
Draw in its bounds (set via setBounds) respecting optional effects such as alpha (set via setAlpha) and color filter (set via setColorFilter).* @param canvas The canvas to draw into
这个方法是运用【透明度】【颜色滤镜】等效果在【边界】内绘制相关内容。
实际上,Drawable类中有一个
setBounds(int left, int top, int right, int bottom)
方法来设定绘制的边界(实质上是一个矩形)。
在制定完绘制的边界后,就调用Drawable.draw()在刚才指定的边界区域之内绘制图像。
再来看最后一个方法:restoreToCount(int saveCount);
public void restoreToCount(int saveCount) { boolean throwOnUnderflow = !sCompatibilityRestore || !isHardwareAccelerated(); native_restoreToCount(mNativeCanvasWrapper, saveCount, throwOnUnderflow); }
很明显,这个方法和之前的canvas.save()方法相辅相成。
- canvas.save()保存了当前画布的状态后,
- 我们又在canvas绘制了内容,
- 待绘制结束后,随即调用canvas.restoreToCount(int saveCount)来恢复canvas的状态。
这样看来,canvas.restoreToCount(int saveCount)与canvas.save()都是在保存canvas的状态,怎么没什么区别呢?
没关系,那肯定是我们想的不够细致。
看到restoreToCount()方法中还有一个int 形参saveCount了么??
实际上,在一次操作中我们可以多次保存canvas的状态,举个栗子:
canvas.translate(500,500); // 第一次保存Canvas的状态 ===> 状态1 int save1 = canvas.save(); canvas.scale(2, 2); // 第二次保存Canvas的状态 ===> 状态2 int save2 = canvas.save(); canvas.translate(100,100); // 第三次保存Canvas的状态 ===> 状态3 int save3 = canvas.save(); canvas.restore(); // 返回最近保存的save状态,即状态3 canvas.restoreToCount(save1);//返回到指定的状态,此处为状态1
看完上述栗子之后,相信大家对此已经比较清楚了。
Ok。到这里,我们就简单的看完了ImageView的onDraw()的方法了。
这就是ImageView 最基本的【绘制】过程。要想透彻的理解ImageView,仅仅知道”三步论”可能还远远不够。没关系,接下来我们在一起看看ImageView还有什么神秘的内容,毕竟源码可是有1588行啊。
接下来,继续努力!
- 【Android_View】ImageView源码简析笔记(三)
- 【Android_View】ImageView源码简析笔记(一)
- 【Android_View】ImageView源码简析笔记(二)
- 【Android_View】ImageView源码简析笔记(四)
- 【Android_View】ImageView源码简析笔记(五)
- android_view
- android_view
- Android_VIEW
- android_View Animation
- Android_View动画
- Android_View详解
- STL源码剖析笔记三
- FastDFS源码阅读笔记(三)
- nginx 源码学习笔记(三)
- libevent源码阅读笔记(三)
- RealVNC源码学习笔记 三
- dubbo源码 学习笔记(三)
- Android ImageView-ScaleType源码
- 在Drupal Commerce上运行的真功夫餐饮系统十亿级交易业务
- windows下使用dos命令查看每个java文件的行数
- javaEE之Hibernate架构之数据库连接工具模版制作
- Linux内核启动及根文件系统加载过程
- 免费软件真的免费吗?
- 【Android_View】ImageView源码简析笔记(三)
- NDK运行时库简介
- Weblogic安装教程(转载)
- 根据ip区分内外网
- Python学习(组织文件)
- 2017百度之星1001 chess(排列组合)
- 数据可视化(1)--Chart.js
- AskncTask
- jqgrid中edittype为text、CheckBox、select、textarea、function等,为jqgrid添加自定义按钮