前几天做了一个气泡形状的组件,类似微信的聊天对话框的形状,所以需要把图片裁剪出一个不规则的形状,虽然最终完美解决,但是中间遇到了比较多的问题,暴露了自己在这方面的不足,所以开始系统的学习这方面的知识,并写下这篇文章。
用canvas画点,画线,画面,类似的常规操作我们就不涉及了,在这里我们主要研究Canvas类中的save和restore两类方法。
/**
* Saves the current matrix and clip onto a private stack.
*
* 将当前的矩阵存入一个私有的栈中
*
* <p>
* Subsequent calls to translate,scale,rotate,skew,concat or clipRect,
* clipPath will all operate as usual, but when the balancing call to
* restore() is made, those calls will be forgotten, and the settings that
* existed before the save() will be reinstated.
*
* 随后canvas调用的平移,缩放,旋转,倾斜,合并,裁剪矩形,裁剪轨迹等等都可以正常使用,
* 但是当调用了restore()之后,之前提到的那些操作都将无效,save()方法之前的设置将会恢复
*
* @return The value to pass to restoreToCount() to balance this save()
*
* 返回值是用来传递给restoreToCount()方法来抵消这次save的
*/
public int save() {
return nSave(mNativeCanvasWrapper, MATRIX_SAVE_FLAG | CLIP_SAVE_FLAG);
}
/**
* This behaves the same as save(), but in addition it allocates and
* redirects drawing to an offscreen rendering target.
* <p class="note"><strong>Note:</strong> this method is very expensive,
* incurring more than double rendering cost for contained content. Avoid
* using this method when possible and instead use a
* {@link android.view.View#LAYER_TYPE_HARDWARE hardware layer} on a View
* to apply an xfermode, color filter, or alpha, as it will perform much
* better than this method.
* <p>
* All drawing calls are directed to a newly allocated offscreen rendering target.
* Only when the balancing call to restore() is made, is that offscreen
* buffer drawn back to the current target of the Canvas (which can potentially be a previous
* layer if these calls are nested).
* <p>
* Attributes of the Paint - {@link Paint#getAlpha() alpha},
* {@link Paint#getXfermode() Xfermode}, and
* {@link Paint#getColorFilter() ColorFilter} are applied when the
* offscreen rendering target is drawn back when restore() is called.
*
* @param bounds May be null. The maximum size the offscreen render target
* needs to be (in local coordinates)
* @param paint This is copied, and is applied to the offscreen when
* restore() is called.
* @return value to pass to restoreToCount() to balance this save()
*/
public int saveLayer(@Nullable RectF bounds, @Nullable Paint paint) {
return saveLayer(bounds, paint, ALL_SAVE_FLAG);
}
这个方法的注释有点乱,我把翻译放在下面:
这个方法与save()的效果一样,但是它会额外分配和重定向绘制到一个离屏渲染的目标上。这个方法非常消耗性能, 会对包含的内容造成超过两倍的渲染开销。
所有的绘制都会被导向一个新分配的离屏渲染目标上。只有当用来平衡这次save的restore()方法被调用之后, 离屏缓冲区中的内容才会被绘制回canvas的当前目标上(如果saveLayer有嵌套调用的话也可能是上一个layer)。
/**
* This call balances a previous call to save(), and is used to remove all
* modifications to the matrix/clip state since the last save call. It is
* an error to call restore() more times than save() was called.
*
* 这个方法调用可以抵消(平衡)一次之前的save方法调用,并且可以用来移除所有上一个save方法调用
* 之后的对矩阵或者裁剪状态的修改。restore方法的调用次数超过save将会报错
*/
public void restore() {
if (!nRestore(mNativeCanvasWrapper)
&& (!sCompatibilityRestore || !isHardwareAccelerated())) {
throw new IllegalStateException("Underflow in restore - more restores than saves");
}
}
/**
* Efficient way to pop any calls to save() that happened after the save
* count reached saveCount. It is an error for saveCount to be less than 1.
*
* 将当前栈中layer的数量减少到saveCount,也就是说超过的layer全部出栈
*
* Example:
* int count = canvas.save();
* ... // more calls potentially to save()
* canvas.restoreToCount(count);
* // now the canvas is back in the same state it was before the initial
* // call to save().
*
* @param saveCount The save level to restore to.
*/
public void restoreToCount(int saveCount) {
if (saveCount < 1) {
if (!sCompatibilityRestore || !isHardwareAccelerated()) {
// do nothing and throw without restoring
throw new IllegalArgumentException(
"Underflow in restoreToCount - more restores than saves");
}
// compat behavior - restore as far as possible
saveCount = 1;
}
nRestoreToCount(mNativeCanvasWrapper, saveCount);
}
最后我根据自己的理解说明一下Canvas和Bitmap的关系。
Bitmap是一个类,它存贮了一个矩形区域内所有像素点的信息。我们可以读取矩形区域内的每一个像素点的颜色值, 在android屏幕上的给定区域显示一张图片。 而Canvas类,是一张盖在Bitmap上的透明的坐标纸,我们可以根据这张坐标纸在bitmap上进行绘制,所有的这些绘制, 其实最后都存贮在了Canvas持有的bitmap对象中。