diff options
Diffstat (limited to 'src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java')
-rw-r--r-- | src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java b/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java new file mode 100644 index 0000000..d5d04b3 --- /dev/null +++ b/src/com/android/bitmap/drawable/StyledCornersBitmapDrawable.java @@ -0,0 +1,310 @@ +/* + * Copyright (C) 2014 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.bitmap.drawable; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.Paint.Style; +import android.graphics.Path; +import android.graphics.Rect; +import android.graphics.RectF; + +import com.android.bitmap.BitmapCache; + +/** + * A custom ExtendedBitmapDrawable that styles the corners in configurable ways. + * + * All four corners can be configured as {@link #CORNER_STYLE_SHARP}, + * {@link #CORNER_STYLE_ROUND}, or {@link #CORNER_STYLE_FLAP}. + * This is accomplished applying a non-rectangular clip applied to the canvas. + * + * A border is draw that conforms to the styled corners. + * + * {@link #CORNER_STYLE_FLAP} corners have a colored flap drawn within the bounds. + */ +public class StyledCornersBitmapDrawable extends ExtendedBitmapDrawable { + + public static final int CORNER_STYLE_SHARP = 0; + public static final int CORNER_STYLE_ROUND = 1; + public static final int CORNER_STYLE_FLAP = 2; + + private static final int START_RIGHT = 0; + private static final int START_BOTTOM = 90; + private static final int START_LEFT = 180; + private static final int START_TOP = 270; + private static final int QUARTER_CIRCLE = 90; + private static final RectF sRectF = new RectF(); + + private final Paint mFlapPaint = new Paint(); + private final Paint mBorderPaint = new Paint(); + private final Path mClipPath = new Path(); + private final float mCornerRoundRadius; + private final float mCornerFlapSide; + + private int mTopLeftCornerStyle = CORNER_STYLE_SHARP; + private int mTopRightCornerStyle = CORNER_STYLE_SHARP; + private int mBottomRightCornerStyle = CORNER_STYLE_SHARP; + private int mBottomLeftCornerStyle = CORNER_STYLE_SHARP; + private int mScrimColor; + private float mBorderWidth; + + /** + * Create a new StyledCornersBitmapDrawable. + */ + public StyledCornersBitmapDrawable(Resources res, BitmapCache cache, + boolean limitDensity, ExtendedOptions opts, float cornerRoundRadius, + float cornerFlapSide) { + super(res, cache, limitDensity, opts); + + mCornerRoundRadius = cornerRoundRadius; + mCornerFlapSide = cornerFlapSide; + + mFlapPaint.setColor(Color.TRANSPARENT); + mFlapPaint.setStyle(Style.FILL); + + mBorderPaint.setColor(Color.TRANSPARENT); + mBorderPaint.setStyle(Style.STROKE); + mBorderPaint.setStrokeWidth(mBorderWidth); + mBorderPaint.setAntiAlias(true); + + mScrimColor = Color.TRANSPARENT; + } + + /** + * Set the border stroke width of this drawable. + */ + public void setBorderWidth(final float borderWidth) { + final boolean changed = mBorderPaint.getStrokeWidth() != borderWidth; + mBorderPaint.setStrokeWidth(borderWidth); + mBorderWidth = borderWidth; + + if (changed) { + invalidateSelf(); + } + } + + /** + * Set the border stroke color of this drawable. Set to {@link Color#TRANSPARENT} to disable. + */ + public void setBorderColor(final int color) { + final boolean changed = mBorderPaint.getColor() != color; + mBorderPaint.setColor(color); + + if (changed) { + invalidateSelf(); + } + } + + /** Set the corner styles for all four corners */ + public void setCornerStyles(int topLeft, int topRight, int bottomRight, int bottomLeft) { + boolean changed = mTopLeftCornerStyle != topLeft + || mTopRightCornerStyle != topRight + || mBottomRightCornerStyle != bottomRight + || mBottomLeftCornerStyle != bottomLeft; + + mTopLeftCornerStyle = topLeft; + mTopRightCornerStyle = topRight; + mBottomRightCornerStyle = bottomRight; + mBottomLeftCornerStyle = bottomLeft; + + if (changed) { + recalculatePath(); + } + } + + /** + * Set the flap color for all corners that have style {@link #CORNER_STYLE_SHARP}. + * + * Use {@link android.graphics.Color#TRANSPARENT} to disable flap colors. + */ + public void setFlapColor(int flapColor) { + boolean changed = mFlapPaint.getColor() != flapColor; + mFlapPaint.setColor(flapColor); + + if (changed) { + invalidateSelf(); + } + } + + /** + * Get the color of the scrim that is drawn over the contents, but under the flaps and borders. + */ + public int getScrimColor() { + return mScrimColor; + } + + /** + * Set the color of the scrim that is drawn over the contents, but under the flaps and borders. + * + * Use {@link android.graphics.Color#TRANSPARENT} to disable the scrim. + */ + public void setScrimColor(int color) { + boolean changed = mScrimColor != color; + mScrimColor = color; + + if (changed) { + invalidateSelf(); + } + } + + @Override + protected void onBoundsChange(Rect bounds) { + super.onBoundsChange(bounds); + + recalculatePath(); + } + + /** + * Override draw(android.graphics.Canvas) instead of + * {@link #onDraw(android.graphics.Canvas)} to clip all the drawable layers. + */ + @Override + public void draw(Canvas canvas) { + final Rect bounds = getBounds(); + if (bounds.isEmpty()) { + return; + } + + // Clip to path. + canvas.save(); + canvas.clipPath(mClipPath); + + // Draw parent within path. + super.draw(canvas); + + // Draw scrim on top of parent. + canvas.drawColor(mScrimColor); + + // Draw flap. + float left = bounds.left + mBorderWidth / 2; + float top = bounds.top + mBorderWidth / 2; + float right = bounds.right - mBorderWidth / 2; + float bottom = bounds.bottom - mBorderWidth / 2; + RectF flapCornerRectF = sRectF; + flapCornerRectF.set(0, 0, mCornerFlapSide + mCornerRoundRadius, + mCornerFlapSide + mCornerRoundRadius); + + if (mTopLeftCornerStyle == CORNER_STYLE_FLAP) { + flapCornerRectF.offsetTo(left, top); + canvas.drawRoundRect(flapCornerRectF, mCornerRoundRadius, + mCornerRoundRadius, mFlapPaint); + } + if (mTopRightCornerStyle == CORNER_STYLE_FLAP) { + flapCornerRectF.offsetTo(right - mCornerFlapSide, top); + canvas.drawRoundRect(flapCornerRectF, mCornerRoundRadius, + mCornerRoundRadius, mFlapPaint); + } + if (mBottomRightCornerStyle == CORNER_STYLE_FLAP) { + flapCornerRectF.offsetTo(right - mCornerFlapSide, bottom - mCornerFlapSide); + canvas.drawRoundRect(flapCornerRectF, mCornerRoundRadius, + mCornerRoundRadius, mFlapPaint); + } + if (mBottomLeftCornerStyle == CORNER_STYLE_FLAP) { + flapCornerRectF.offsetTo(left, bottom - mCornerFlapSide); + canvas.drawRoundRect(flapCornerRectF, mCornerRoundRadius, + mCornerRoundRadius, mFlapPaint); + } + + canvas.restore(); + + // Draw border around path. + canvas.drawPath(mClipPath, mBorderPaint); + } + + protected Path getClipPath() { + return mClipPath; + } + + private void recalculatePath() { + Rect bounds = getBounds(); + + if (bounds.isEmpty()) { + return; + } + + // Setup. + float left = bounds.left + mBorderWidth / 2; + float top = bounds.top + mBorderWidth / 2; + float right = bounds.right - mBorderWidth / 2; + float bottom = bounds.bottom - mBorderWidth / 2; + RectF roundedCornerRectF = sRectF; + roundedCornerRectF.set(0, 0, 2 * mCornerRoundRadius, 2 * mCornerRoundRadius); + mClipPath.rewind(); + + switch (mTopLeftCornerStyle) { + case CORNER_STYLE_SHARP: + mClipPath.moveTo(left, top); + break; + case CORNER_STYLE_ROUND: + roundedCornerRectF.offsetTo(left, top); + mClipPath.arcTo(roundedCornerRectF, START_LEFT, QUARTER_CIRCLE); + break; + case CORNER_STYLE_FLAP: + mClipPath.moveTo(left, top - mCornerFlapSide); + mClipPath.lineTo(left + mCornerFlapSide, top); + break; + } + + switch (mTopRightCornerStyle) { + case CORNER_STYLE_SHARP: + mClipPath.lineTo(right, top); + break; + case CORNER_STYLE_ROUND: + roundedCornerRectF.offsetTo(right - roundedCornerRectF.width(), top); + mClipPath.arcTo(roundedCornerRectF, START_TOP, QUARTER_CIRCLE); + break; + case CORNER_STYLE_FLAP: + mClipPath.lineTo(right - mCornerFlapSide, top); + mClipPath.lineTo(right, top + mCornerFlapSide); + break; + } + + switch (mBottomRightCornerStyle) { + case CORNER_STYLE_SHARP: + mClipPath.lineTo(right, bottom); + break; + case CORNER_STYLE_ROUND: + roundedCornerRectF.offsetTo(right - roundedCornerRectF.width(), + bottom - roundedCornerRectF.height()); + mClipPath.arcTo(roundedCornerRectF, START_RIGHT, QUARTER_CIRCLE); + break; + case CORNER_STYLE_FLAP: + mClipPath.lineTo(right, bottom - mCornerFlapSide); + mClipPath.lineTo(right - mCornerFlapSide, bottom); + break; + } + + switch (mBottomLeftCornerStyle) { + case CORNER_STYLE_SHARP: + mClipPath.lineTo(left, bottom); + break; + case CORNER_STYLE_ROUND: + roundedCornerRectF.offsetTo(left, bottom - roundedCornerRectF.height()); + mClipPath.arcTo(roundedCornerRectF, START_BOTTOM, QUARTER_CIRCLE); + break; + case CORNER_STYLE_FLAP: + mClipPath.lineTo(left + mCornerFlapSide, bottom); + mClipPath.lineTo(left, bottom - mCornerFlapSide); + break; + } + + // Finish. + mClipPath.close(); + } +} |