下面列出了android.graphics.Path.Direction#android.graphics.Region 实例代码,或者点击链接到github查看源代码,也可以在右侧发表评论。
private void drawProgressRectWithClip(Canvas canvas) {
mPaint.setColor(mProgressBarColor);
mPaint.setStyle(Paint.Style.FILL);
//根据进度比率计算出当前的进度值对应的宽度
int progress = (int) (mValidWidth * (getProgress() * 1.0f / getMax()));
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
//裁剪圆角矩形路径
drawRoundRectPath();
canvas.clipPath(mRoundRectPath);//裁剪之后此时画布就变成了裁剪之后的圆角矩形
//裁剪进度路径
drawProgressPath(progress);
canvas.clipPath(mProgressPath, Region.Op.INTERSECT);
canvas.drawColor(mProgressBarColor);
canvas.restore();
}
@Override
public void draw(Canvas canvas) {
// If text is transparent, don't draw any shadow
if (getCurrentTextColor() == getResources().getColor(android.R.color.transparent)) {
getPaint().clearShadowLayer();
super.draw(canvas);
return;
}
// We enhance the shadow by drawing the shadow twice
getPaint().setShadowLayer(BubbleTextView.SHADOW_LARGE_RADIUS, 0.0f,
BubbleTextView.SHADOW_Y_OFFSET, BubbleTextView.SHADOW_LARGE_COLOUR);
super.draw(canvas);
canvas.save(Canvas.CLIP_SAVE_FLAG);
canvas.clipRect(getScrollX(), getScrollY() + getExtendedPaddingTop(),
getScrollX() + getWidth(),
getScrollY() + getHeight(), Region.Op.INTERSECT);
getPaint().setShadowLayer(BubbleTextView.SHADOW_SMALL_RADIUS, 0.0f, 0.0f,
BubbleTextView.SHADOW_SMALL_COLOUR);
super.draw(canvas);
canvas.restore();
}
/**
* 绘制C区域内容
* @param canvas
* @param pathA
*/
private void drawPathCContent(Canvas canvas, Path pathA){
canvas.save();
canvas.clipPath(pathA);
canvas.clipPath(getPathC(), Region.Op.REVERSE_DIFFERENCE);//裁剪出C区域不同于A区域的部分
// canvas.drawPath(getPathC(),pathCPaint);
float eh = (float) Math.hypot(f.x - e.x,h.y - f.y);
float sin0 = (f.x - e.x) / eh;
float cos0 = (h.y - f.y) / eh;
//设置翻转和旋转矩阵
mMatrixArray[0] = -(1-2 * sin0 * sin0);
mMatrixArray[1] = 2 * sin0 * cos0;
mMatrixArray[3] = 2 * sin0 * cos0;
mMatrixArray[4] = 1 - 2 * sin0 * sin0;
mMatrix.reset();
mMatrix.setValues(mMatrixArray);//翻转和旋转
mMatrix.preTranslate(-e.x, -e.y);//沿当前XY轴负方向位移得到 矩形A₃B₃C₃D₃
mMatrix.postTranslate(e.x, e.y);//沿原XY轴方向位移得到 矩形A4 B4 C4 D4
canvas.drawBitmap(pathCContentBitmap, mMatrix, null);
drawPathCShadow(canvas);
canvas.restore();
}
private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
mPath0.reset();
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,
mBezierEnd1.y);
mPath0.lineTo(mTouchX, mTouchY);
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,
mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);
mPath0.close();
canvas.save();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(path);
} else {
canvas.clipPath(path, Region.Op.XOR);
}
canvas.drawBitmap(bitmap, 0, 0, null);
try {
canvas.restore();
} catch (Exception e) {
e.printStackTrace();
}
}
private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
mPath0.reset();
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,
mBezierEnd1.y);
mPath0.lineTo(mTouch.x, mTouch.y);
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,
mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);
mPath0.close();
canvas.save();
canvas.clipPath(path, Region.Op.XOR);
canvas.drawBitmap(bitmap, 0, 0, null);
try {
canvas.restore();
} catch (Exception e) {
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// Draw background with transparent circular hole
final float radius = Math.min((float) canvas.getWidth(), canvas.getHeight()) / 2 - cropCirclePadding;
mRectF.set((float) canvas.getWidth() / 2 - radius, (float) canvas.getHeight() / 2 - radius, (float) canvas.getWidth() / 2 + radius, (float) canvas.getHeight() / 2 + radius);
circleSelectionPath.reset();
circleSelectionPath.addOval(mRectF, Path.Direction.CW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
canvas.clipOutPath(circleSelectionPath);
} else {
canvas.clipPath(circleSelectionPath, Region.Op.XOR);
}
canvas.drawRect(0, 0, canvas.getWidth(), canvas.getHeight(), backgroundPaint);
// Canvas did not save and called restore due to which app crashes, so we have to save first then call restore
canvas.save();
canvas.restore();
// Draw circle border
canvas.drawCircle((float) canvas.getWidth() / 2, (float) canvas.getHeight() / 2, radius, borderPaint);
}
public void setClip(int x, int y, int width, int height) {
//System.out.println("Setting clip "+x+","+y+","+width+", "+height);
if (clipSet) {
canvas.restore();
}
canvas.save();
clipSet = true;
clipFresh = false;
if (getTransform().isIdentity() || transformSemaphore > 0) {
canvas.clipRect(x, y, x + width, y + height, Region.Op.INTERSECT);
} else {
this.tmppath.rewind();
this.tmppath.addRect((float) x, (float) y, (float) width + x, (float) height + y, Path.Direction.CW);
this.tmppath.transform(getTransformMatrix());
canvas.clipPath(this.tmppath, Region.Op.INTERSECT);
}
}
@Override
public void draw(Canvas canvas) {
// We don't have access to the OverlayViewGroup instance directly, but we can manipulate
// its Canvas via the Drawables' draw(). This allows us to draw outside the View bounds,
// so we can position the margin overlays correctly.
Rect newRect = canvas.getClipBounds();
// Make the Canvas Rect bigger according to the View margins.
newRect.inset(-(mMargins.right + mMargins.left), -(mMargins.top + mMargins.bottom));
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
canvas.clipRect(newRect, Region.Op.REPLACE);
} else {
canvas.clipOutRect(newRect);
}
super.draw(canvas);
}
private void drawBackground(Canvas canvas) {
Paint paint = new Paint();
paint.setColor(Color.parseColor("#66000000"));
paint.setStyle(Paint.Style.FILL);
Path path = new Path();
path.moveTo(topLeft.x, topLeft.y);
path.lineTo(topRight.x, topRight.y);
path.lineTo(bottomRight.x, bottomRight.y);
path.lineTo(bottomLeft.x, bottomLeft.y);
path.close();
canvas.save();
canvas.clipPath(path, Region.Op.DIFFERENCE);
canvas.drawColor(Color.parseColor("#66000000"));
canvas.restore();
}
private void prepareCanvasForShadow(@NonNull Canvas canvas) {
// Calculate the translation to offset the canvas for the given offset and rotation.
int shadowOffsetX = getShadowOffsetX();
int shadowOffsetY = getShadowOffsetY();
// We only handle clipping as a convenience for older apis where we are trying to seamlessly
// provide fake shadows. On newer versions of android, we require that the parent is set so that
// clipChildren is false.
if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP && shadowBitmapDrawingEnable) {
// Add space and offset the canvas for the shadows. Otherwise any shadows drawn outside would
// be clipped and not visible.
Rect canvasClipBounds = canvas.getClipBounds();
canvasClipBounds.inset(-drawableState.shadowCompatRadius, -drawableState.shadowCompatRadius);
canvasClipBounds.offset(shadowOffsetX, shadowOffsetY);
canvas.clipRect(canvasClipBounds, Region.Op.REPLACE);
}
// Translate the canvas by an amount specified by the shadowCompatOffset. This will make the
// shadow appear at and angle from the shape.
canvas.translate(shadowOffsetX, shadowOffsetY);
}
public void findAccessibilityNodeInfoByAccessibilityIdClientThread(
long accessibilityNodeId, Region interactiveRegion, int interactionId,
IAccessibilityInteractionConnectionCallback callback, int flags, int interrogatingPid,
long interrogatingTid, MagnificationSpec spec, Bundle arguments) {
final Message message = mHandler.obtainMessage();
message.what = PrivateHandler.MSG_FIND_ACCESSIBILITY_NODE_INFO_BY_ACCESSIBILITY_ID;
message.arg1 = flags;
final SomeArgs args = SomeArgs.obtain();
args.argi1 = AccessibilityNodeInfo.getAccessibilityViewId(accessibilityNodeId);
args.argi2 = AccessibilityNodeInfo.getVirtualDescendantId(accessibilityNodeId);
args.argi3 = interactionId;
args.arg1 = callback;
args.arg2 = spec;
args.arg3 = interactiveRegion;
args.arg4 = arguments;
message.obj = args;
scheduleMessage(message, interrogatingPid, interrogatingTid, CONSIDER_REQUEST_PREPARERS);
}
/**
* This method draws dimmed area around the crop bounds.
*
* @param canvas - valid canvas object
*/
protected void drawDimmedLayer(@NonNull Canvas canvas) {
canvas.save();
if (mCircleDimmedLayer) {
canvas.clipPath(mCircularPath, Region.Op.DIFFERENCE);
} else {
canvas.clipRect(mCropViewRect, Region.Op.DIFFERENCE);
}
canvas.drawColor(mDimmedColor);
canvas.restore();
if (mCircleDimmedLayer) { // Draw 1px stroke to fix antialias
canvas.drawCircle(mCropViewRect.centerX(), mCropViewRect.centerY(),
Math.min(mCropViewRect.width(), mCropViewRect.height()) / 2.f, mDimmedStrokePaint);
}
}
/**
* 绘制外发光阴影
*
* @param canvas
*/
private void onDrawShadow(Canvas canvas)
{
if (mShadowWidth > 0)
{
canvas.save();
// 裁剪处理(使阴影矩形框内变为透明)
if (mRoundRadius > 0)
{
canvas.clipRect(0, 0, getWidth(), getHeight());
mTempRectF.set(mFrameRectF);
mTempRectF.inset(mRoundRadius / 2f, mRoundRadius / 2f);
canvas.clipRect(mTempRectF, Region.Op.DIFFERENCE);
}
// 绘制外发光阴影效果
canvas.drawRoundRect(mFrameRectF, mRoundRadius, mRoundRadius, mShadowPaint);
canvas.restore();
}
}
@Override
protected void dispatchDraw(Canvas canvas) {
// Draw the background below children.
if (mBackgroundAlpha > 0.0f) {
// Update the scroll position first to ensure scrim cutout is in the right place.
mLauncher.getWorkspace().computeScrollWithoutInvalidation();
int alpha = (int) (mBackgroundAlpha * 255);
CellLayout currCellLayout = mLauncher.getWorkspace().getCurrentDragOverlappingLayout();
canvas.save();
if (currCellLayout != null && currCellLayout != mLauncher.getHotseat().getLayout()) {
// Cut a hole in the darkening scrim on the page that should be highlighted, if any.
getDescendantRectRelativeToSelf(currCellLayout, mHighlightRect);
canvas.clipRect(mHighlightRect, Region.Op.DIFFERENCE);
}
canvas.drawColor((alpha << 24) | SCRIM_COLOR);
canvas.restore();
}
mFocusIndicatorHelper.draw(canvas);
super.dispatchDraw(canvas);
}
private void drawBallPath(Canvas canvas) {
//x轴的移动路径
mBallCentreX = (mWidth + mBallRadius) - (2.0f * mRadius + 2.0f * mBallRadius) * mRatio;
mBallPath.reset();
mBallPath.addCircle(mBallCentreX, mCenterY, mBallRadius, Path.Direction.CW);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
mBallPath.op(mWavePath, Path.Op.DIFFERENCE);
} else {
canvas.clipPath(mWavePath, Region.Op.DIFFERENCE);
}
mBallPath.close();
canvas.drawPath(mBallPath, mWavePaint);
}
private void drawCurrentPageArea(Canvas canvas, Bitmap bitmap, Path path) {
mPath0.reset();
mPath0.moveTo(mBezierStart1.x, mBezierStart1.y);
mPath0.quadTo(mBezierControl1.x, mBezierControl1.y, mBezierEnd1.x,
mBezierEnd1.y);
mPath0.lineTo(mTouch.x, mTouch.y);
mPath0.lineTo(mBezierEnd2.x, mBezierEnd2.y);
mPath0.quadTo(mBezierControl2.x, mBezierControl2.y, mBezierStart2.x,
mBezierStart2.y);
mPath0.lineTo(mCornerX, mCornerY);
mPath0.close();
canvas.save();
canvas.clipPath(path, Region.Op.XOR);
canvas.drawBitmap(bitmap, 0, 0, null);
try {
canvas.restore();
} catch (Exception e) {
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
// 计算在当前大小下,内容应该显示的大小和起始位置
int safeW = w - getPaddingLeft() - getPaddingRight();
int safeH = h - getPaddingTop() - getPaddingBottom();
float edgeLength, startX, startY;
float fix = mArcWidth / 2 + mBorderWidth + mShadowRadius * 2; // 修正距离,画笔宽度的修正
if (safeW < safeH) {
// 宽度小于高度,以宽度为准
edgeLength = safeW - fix;
startX = getPaddingLeft();
startY = (safeH - safeW) / 2.0f + getPaddingTop();
} else {
// 宽度大于高度,以高度为准
edgeLength = safeH - fix;
startX = (safeW - safeH) / 2.0f + getPaddingLeft();
startY = getPaddingTop();
}
// 得到显示区域和中心的
RectF content = new RectF(startX + fix, startY + fix, startX + edgeLength, startY + edgeLength);
mCenterX = content.centerX();
mCenterY = content.centerY();
// 得到路径
mSeekPath.reset();
mSeekPath.addArc(content, mOpenAngle / 2, CIRCLE_ANGLE - mOpenAngle);
mSeekPathMeasure.setPath(mSeekPath, false);
computeThumbPos(mProgressPresent);
resetShaderColor();
mInvertMatrix.reset();
mInvertMatrix.preRotate(-mRotateAngle, mCenterX, mCenterY);
mArcPaint.getFillPath(mSeekPath, mBorderPath);
mBorderPath.close();
mArcRegion.setPath(mBorderPath, new Region(0, 0, w, h));
}
protected void onDraw(Canvas canvas) {
if (isCircularReveal) {
canvas.save();
canvas.translate(0, 0);
mPath.reset();
mPath.addCircle(mAnchorX, mAnchorY, mRadius, Path.Direction.CCW);
canvas.clipPath(mPath, Region.Op.REPLACE);
mCircleRevealEnable.superOnDraw(canvas);
canvas.restore();
} else {
mCircleRevealEnable.superOnDraw(canvas);
}
}
@Override
public void draw(final Canvas canvas)
{
final Rect r = getBounds();
final int saveCount = canvas.save();
canvas.clipRect(r, Region.Op.REPLACE);
canvas.translate(r.left, r.top);
canvas.drawBitmap(background, 0, 0, paint);
drawCanvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
drawCanvas.drawBitmap(dynamic, 0, 0, null);
final float angle;
if (maxAngle >= 0)
angle = -(float) (360d - (maxAngle * value));
else
angle = (float) (360d - (-maxAngle * value));
drawCanvas.drawArc(ovalRect, startAngle, angle, true, erasePaint);
canvas.drawBitmap(drawBitmap, 0, 0, paint);
canvas.drawBitmap(foreground, 0, 0, paint);
// canvas.drawText(formatter.format(value), textX, textY, textPaint);
canvas.restoreToCount(saveCount);
}
/**
* @param x the X coordinate of this event for the touching pointer
* @param y the Y coordinate of this event for the touching pointer
* @return Is touching the wheel
*/
private boolean isEventInWheel(float x, float y) {
float[] pts = new float[2];
pts[0] = x;
pts[1] = y;
mBgMatrix.mapPoints(pts);
RectF bounds = new RectF();
mWheelBgPath.computeBounds(bounds, true);
mBgRegion.setPath(mWheelBgPath, new Region((int) bounds.left, (int) bounds.top, (int) bounds.right, (int) bounds.bottom));
return mBgRegion.contains((int) pts[0], (int) pts[1]);
}
public void setBounds(Region bounds) {
synchronized (mService.mWindowMap) {
if (mBounds.equals(bounds)) {
return;
}
mBounds.set(bounds);
invalidate(mDirtyRect);
if (DEBUG_VIEWPORT_WINDOW) {
Slog.i(LOG_TAG, "ViewportWindow set bounds: " + bounds);
}
}
}
private boolean isEventInPath(MotionEvent event) {
RectF bounds = new RectF();
mDrawPath.computeBounds(bounds, true);
mRegion.setPath(mDrawPath, new Region((int)bounds.left,
(int)bounds.top, (int)bounds.right, (int)bounds.bottom));
return mRegion.contains((int) event.getX(), (int) event.getY());
}
private void logRegion() {
// ▼在屏幕中间添加一个圆
Path circlePath = new Path();
circlePath.addCircle(mViewWidth/2, mViewHeight/2, 300, Path.Direction.CW);
// ▼将剪裁边界设置为视图大小
Region globalRegion = new Region(0, 0, mViewWidth, mViewHeight/2);
// ▼将 Path 添加到 Region 中
Region circleRegion=new Region();
circleRegion.setPath(circlePath, globalRegion);
System.err.println("circleRegion"+(circleRegion.contains(mViewWidth/2,mViewHeight/2-100)?"包含":"不包含")+"圆心h-100");
System.err.println("circleRegion"+(circleRegion.contains(mViewWidth/2,mViewHeight/2+100)?"包含":"不包含")+"圆心h+100");
System.err.println("circleRegion"+(circleRegion.contains(mViewWidth/2,mViewHeight/2-400)?"包含":"不包含")+"圆心h-400");
}
protected void draw(Canvas canvas) {
canvas.save();
Path path = new Path();
outlinePaint.setStrokeWidth(outlineWidth);
if (!hasFocus()) {
outlinePaint.setColor(Color.BLACK);
canvas.drawRect(drawRect, outlinePaint);
} else {
Rect viewDrawingRect = new Rect();
viewContext.getDrawingRect(viewDrawingRect);
path.addRect(new RectF(drawRect), Path.Direction.CW);
outlinePaint.setColor(highlightColor);
if (isClipPathSupported(canvas)) {
canvas.clipPath(path, Region.Op.DIFFERENCE);
canvas.drawRect(viewDrawingRect, outsidePaint);
} else {
drawOutsideFallback(canvas);
}
canvas.restore();
canvas.drawPath(path, outlinePaint);
if (showThirds) {
drawThirds(canvas);
}
if (handleMode == HandleMode.Always ||
(handleMode == HandleMode.Changing && modifyMode == ModifyMode.Grow)) {
drawHandles(canvas);
}
}
}
/**
* 创建裁剪的背景图片
*
* @param paint
* @param path
* @param bitmap
* @return
*/
private Bitmap creatBitmap(Paint paint, Path path, Bitmap bitmap) {
Canvas newCanvas = new Canvas(bitmap);
newCanvas.setDrawFilter(new PaintFlagsDrawFilter(0,
Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
newCanvas.clipPath(path, Region.Op.INTERSECT);
newCanvas.drawBitmap(mGradientLine, 0, 0, paint);
return bitmap;
}
/**
* Insets the reference frame of the cutout in the given directions.
*
* @return a copy of this instance which has been inset
* @hide
*/
public DisplayCutout inset(int insetLeft, int insetTop, int insetRight, int insetBottom) {
if (mBounds.isEmpty()
|| insetLeft == 0 && insetTop == 0 && insetRight == 0 && insetBottom == 0) {
return this;
}
Rect safeInsets = new Rect(mSafeInsets);
Region bounds = Region.obtain(mBounds);
// Note: it's not really well defined what happens when the inset is negative, because we
// don't know if the safe inset needs to expand in general.
if (insetTop > 0 || safeInsets.top > 0) {
safeInsets.top = atLeastZero(safeInsets.top - insetTop);
}
if (insetBottom > 0 || safeInsets.bottom > 0) {
safeInsets.bottom = atLeastZero(safeInsets.bottom - insetBottom);
}
if (insetLeft > 0 || safeInsets.left > 0) {
safeInsets.left = atLeastZero(safeInsets.left - insetLeft);
}
if (insetRight > 0 || safeInsets.right > 0) {
safeInsets.right = atLeastZero(safeInsets.right - insetRight);
}
bounds.translate(-insetLeft, -insetTop);
return new DisplayCutout(safeInsets, bounds, false /* copyArguments */);
}
private void init() {
paint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.DITHER_FLAG | Paint.LINEAR_TEXT_FLAG);
paint.setTextAlign(Paint.Align.CENTER);
paint.setColor(Color.GRAY);
int padding = dip2px(getContext());
setPadding(0, padding, 0, padding);
for (int i = 0; i < DAYS_IN_WEEK; i++) {
Region region = new Region();
weekRegion[i] = region;
}
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.getClipBounds(mBounds);
if (mBounds != null && mConf.needShrink()) {
mBounds.inset(mConf.getInsetX(), mConf.getInsetY());
canvas.clipRect(mBounds, Region.Op.REPLACE);
canvas.translate(mConf.getInsetBounds().left, mConf.getInsetBounds().top);
}
boolean useGeneralDisableEffect = !isEnabled() && this.notStatableDrawable();
if (useGeneralDisableEffect) {
canvas.saveLayerAlpha(mSaveLayerZone, 255 / 2, Canvas.MATRIX_SAVE_FLAG | Canvas.CLIP_SAVE_FLAG | Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.FULL_COLOR_LAYER_SAVE_FLAG
| Canvas.CLIP_TO_LAYER_SAVE_FLAG);
}
mConf.getOffDrawable().draw(canvas);
mConf.getOnDrawable().setAlpha(calcAlpha());
mConf.getOnDrawable().draw(canvas);
mConf.getThumbDrawable().draw(canvas);
if (useGeneralDisableEffect) {
canvas.restore();
}
if (SHOW_RECT) {
mRectPaint.setColor(Color.parseColor("#AA0000"));
canvas.drawRect(mBackZone, mRectPaint);
mRectPaint.setColor(Color.parseColor("#00FF00"));
canvas.drawRect(mSafeZone, mRectPaint);
mRectPaint.setColor(Color.parseColor("#0000FF"));
canvas.drawRect(mThumbZone, mRectPaint);
}
}
private static Region boundingRectsToRegion(List<Rect> rects) {
Region result = Region.obtain();
if (rects != null) {
for (Rect r : rects) {
result.op(r, Region.Op.UNION);
}
}
return result;
}
private void drawRect(Canvas canvas) {
float radiusIn = this.radiusIn * theme.getInner();
float radiusOut = this.radiusOut * theme.getOuter();
canvas.drawRect(center.x - radiusOut, center.y - radiusOut, center.x + radiusOut, center.y + radiusOut, paintOut);
canvas.save();
if (viewBound != null) {
canvas.clipPath(viewBound, Region.Op.REPLACE);
}
canvas.drawRect(center.x - radiusIn, center.y - radiusIn, center.x + radiusIn, center.y + radiusIn, paintIn);
if (Build.VERSION.SDK_INT != 23) {
canvas.restore();
}
}