@act262
2017-06-01T12:57:52.000000Z
字数 3902
阅读 2314
Android_Drawable
Java层的Path.java 对应C层的Path.cpp,SkPath.cpp
对应的C代码位置分别在:
android / platform / frameworks / base / core / jni / android / graphics / Path.cpp
android / platform / external / skia / src / core / SkPath.cpp
API 18+开始SkPath.cpp的实现有大变动,在API 18之前设置radius>矩形的宽高时不会变成半圆形效果
Q:为什么在xml文件中定义自定义shape时,设置radius属性大于控件本身的大小时就产生了圆角矩形效果
A:xml文件自定义的shape对应到GradientDrawable,设置圆角的具体实现在Path类中。
如果要实现半边圆角矩形效果,则需要矩形的rx/ry分别为矩形的高的一半即可。
基于API 17的源码分析
Path.java
public void addRoundRect(float left, float top, float right, float bottom, float[] radii,Direction dir) {if (radii.length < 8) {throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");}isSimplePath = false;native_addRoundRect(mNativePath, left, top, right, bottom, radii, dir.nativeInt);}// ① 4个rx相同的情况下 -> Path.addRoundRectXYprivate static native void native_addRoundRect(long nPath, float left, float top,float right, float bottom,float rx, float ry, int dir);// ② 4个rx分别设置的情况 -> Path.addRoundRect8private static native void native_addRoundRect(long nPath, float left, float top,float right, float bottom,float[] radii, int dir);
Path.cpp
// ①static void addRoundRectXY(JNIEnv* env, jobject clazz, jlong objHandle, jfloat left, jfloat top,jfloat right, jfloat bottom, jfloat rx, jfloat ry, jint dirHandle) {SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);SkPath* obj = reinterpret_cast<SkPath*>(objHandle);SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);obj->addRoundRect(rect, rx, ry, dir); // -> SkPath.addRoundRect -> SkPaht.add_corner_arc}// ②static void addRoundRect8(JNIEnv* env, jobject, jlong objHandle, jfloat left, jfloat top,jfloat right, jfloat bottom, jfloatArray array, jint dirHandle) {SkRect rect = SkRect::MakeLTRB(left, top, right, bottom);SkPath* obj = reinterpret_cast<SkPath*>(objHandle);SkPath::Direction dir = static_cast<SkPath::Direction>(dirHandle);AutoJavaFloatArray afa(env, array, 8);#ifdef SK_SCALAR_IS_FLOATconst float* src = afa.ptr();#else#error Need to convert float array to SkScalar array before calling the following function.#endifobj->addRoundRect(rect, src, dir); // -> SkPath.addRoundRect()}
SkPath.cpp
// ① 画圆角static void add_corner_arc(SkPath* path, const SkRect& rect,SkScalar rx, SkScalar ry, int startAngle,SkPath::Direction dir, bool forceMoveTo) {// rx取 指定的rx或者矩形宽度一半的较小的一个rx = SkMinScalar(SkScalarHalf(rect.width()), rx);ry = SkMinScalar(SkScalarHalf(rect.height()), ry);SkRect r;r.set(-rx, -ry, rx, ry);switch (startAngle) {case 0:r.offset(rect.fRight - r.fRight, rect.fBottom - r.fBottom);break;case 90:r.offset(rect.fLeft - r.fLeft, rect.fBottom - r.fBottom);break;case 180: r.offset(rect.fLeft - r.fLeft, rect.fTop - r.fTop); break;case 270: r.offset(rect.fRight - r.fRight, rect.fTop - r.fTop); break;default: SkDEBUGFAIL("unexpected startAngle in add_corner_arc");}SkScalar start = SkIntToScalar(startAngle);SkScalar sweep = SkIntToScalar(90);if (SkPath::kCCW_Direction == dir) {start += sweep;sweep = -sweep;}path->arcTo(r, start, sweep, forceMoveTo);}// ①void SkPath::addRoundRect(const SkRect& rect, const SkScalar rad[],Direction dir) {// abort before we invoke SkAutoPathBoundsUpdate()if (rect.isEmpty()) {return;}SkAutoPathBoundsUpdate apbu(this, rect);if (kCW_Direction == dir) {add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);} else {add_corner_arc(this, rect, rad[0], rad[1], 180, dir, true);add_corner_arc(this, rect, rad[6], rad[7], 90, dir, false);add_corner_arc(this, rect, rad[4], rad[5], 0, dir, false);add_corner_arc(this, rect, rad[2], rad[3], 270, dir, false);}this->close();}
// ②void SkPath::addRoundRect(const SkRect& rect, SkScalar rx, SkScalar ry, Direction dir) {SkScalar w = rect.width();SkScalar halfW = SkScalarHalf(w); // rect.width * 0.5fSkScalar h = rect.height();SkScalar halfH = SkScalarHalf(h);if (halfW <= 0 || halfH <= 0) {return;}// x方向的宽度大于矩形宽度的一半则跳过bool skip_hori = rx >= halfW;bool skip_vert = ry >= halfH;// 变成椭圆了if (skip_hori && skip_vert) {this->addOval(rect, dir);return;}SkAutoPathBoundsUpdate apbu(this, rect);// 这里就是关键了// 椭圆宽度==矩形宽度/2if (skip_hori) {rx = halfW;} else if (skip_vert) {ry = halfH;}
所以在shape中设定的宽高大于控件的宽高时就会变成圆角矩形了。
以下基于API 25的源码分析