From ce4be503c27d6b824a1f3349e251a983c3e553df Mon Sep 17 00:00:00 2001 From: Balys Valentukevicius Date: Sat, 31 Jan 2015 15:52:45 +0000 Subject: [PATCH] Added rounded corner support #7 #16 - Note: software rendering layer is used for API 17 and below, use with caution --- README.md | 1 + .../materialripple/MaterialRippleLayout.java | 72 +++++++++++++++---- library/src/main/res/values/attributes.xml | 1 + 3 files changed, 61 insertions(+), 13 deletions(-) diff --git a/README.md b/README.md index 0795025..25cd4ad 100644 --- a/README.md +++ b/README.md @@ -67,6 +67,7 @@ app:rippleFadeDuration="75" // duration of fade out effect on ripple app:rippleDelayClick="true" // if true, delays calls to OnClickListeners until ripple effect ends app:rippleBackground="#FFFFFF" // background under ripple drawable; used with rippleOverlay="false" app:ripplePersistent="true" // if true, ripple background color persists after animation, until setRadius(0) is called +app:rippleRoundedCorners="10dp" // radius of corners of ripples. Note: it uses software rendering pipeline for API 17 and below ``` Set an `OnClickListener` to `MaterialRippleLayout`: diff --git a/library/src/main/java/com/balysv/materialripple/MaterialRippleLayout.java b/library/src/main/java/com/balysv/materialripple/MaterialRippleLayout.java index 1176c66..6e77985 100644 --- a/library/src/main/java/com/balysv/materialripple/MaterialRippleLayout.java +++ b/library/src/main/java/com/balysv/materialripple/MaterialRippleLayout.java @@ -26,10 +26,13 @@ import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; +import android.graphics.Path; import android.graphics.Point; import android.graphics.Rect; +import android.graphics.RectF; import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.Drawable; +import android.os.Build; import android.util.AttributeSet; import android.util.Property; import android.util.TypedValue; @@ -50,17 +53,18 @@ public class MaterialRippleLayout extends FrameLayout { - private static final int DEFAULT_DURATION = 350; - private static final int DEFAULT_FADE_DURATION = 75; - private static final float DEFAULT_DIAMETER_DP = 35; - private static final float DEFAULT_ALPHA = 0.2f; - private static final int DEFAULT_COLOR = Color.BLACK; - private static final int DEFAULT_BACKGROUND = Color.TRANSPARENT; - private static final boolean DEFAULT_HOVER = true; - private static final boolean DEFAULT_DELAY_CLICK = true; - private static final boolean DEFAULT_PERSISTENT = false; - private static final boolean DEFAULT_SEARCH_ADAPTER = false; - private static final boolean DEFAULT_RIPPLE_OVERLAY = false; + private static final int DEFAULT_DURATION = 350; + private static final int DEFAULT_FADE_DURATION = 75; + private static final float DEFAULT_DIAMETER_DP = 35; + private static final float DEFAULT_ALPHA = 0.2f; + private static final int DEFAULT_COLOR = Color.BLACK; + private static final int DEFAULT_BACKGROUND = Color.TRANSPARENT; + private static final boolean DEFAULT_HOVER = true; + private static final boolean DEFAULT_DELAY_CLICK = true; + private static final boolean DEFAULT_PERSISTENT = false; + private static final boolean DEFAULT_SEARCH_ADAPTER = false; + private static final boolean DEFAULT_RIPPLE_OVERLAY = false; + private static final int DEFAULT_ROUNDED_CORNERS = 0; private static final int FADE_EXTRA_DELAY = 50; private static final long HOVER_DURATION = 2500; @@ -79,6 +83,7 @@ public class MaterialRippleLayout extends FrameLayout { private boolean ripplePersistent; private Drawable rippleBackground; private boolean rippleInAdapter; + private float rippleRoundedCorners; private float radius; @@ -91,6 +96,8 @@ public class MaterialRippleLayout extends FrameLayout { private Point currentCoords = new Point(); private Point previousCoords = new Point(); + private int layerType; + private boolean eventCancelled; private boolean prepressed; private int positionInAdapter; @@ -132,11 +139,14 @@ public MaterialRippleLayout(Context context, AttributeSet attrs, int defStyle) { rippleBackground = new ColorDrawable(a.getColor(R.styleable.MaterialRippleLayout_rippleBackground, DEFAULT_BACKGROUND)); ripplePersistent = a.getBoolean(R.styleable.MaterialRippleLayout_ripplePersistent, DEFAULT_PERSISTENT); rippleInAdapter = a.getBoolean(R.styleable.MaterialRippleLayout_rippleInAdapter, DEFAULT_SEARCH_ADAPTER); + rippleRoundedCorners = a.getDimensionPixelSize(R.styleable.MaterialRippleLayout_rippleRoundedCorners, DEFAULT_ROUNDED_CORNERS); a.recycle(); paint.setColor(rippleColor); paint.setAlpha(rippleAlpha); + + enableClipPathSupportIfNecessary(); } @@ -272,13 +282,14 @@ private void cancelPressedEvent() { private SimpleOnGestureListener longClickListener = new GestureDetector.SimpleOnGestureListener() { public void onLongPress(MotionEvent e) { mHasPerformedLongPress = childView.performLongClick(); - if(mHasPerformedLongPress){ + if (mHasPerformedLongPress) { if (rippleHover) { startRipple(null); } cancelPressedEvent(); } } + @Override public boolean onDown(MotionEvent e) { mHasPerformedLongPress = false; @@ -439,6 +450,12 @@ public void draw(Canvas canvas) { } super.draw(canvas); if (!positionChanged) { + if (rippleRoundedCorners != 0) { + Path clipPath = new Path(); + RectF rect = new RectF(0, 0, canvas.getWidth(), canvas.getHeight()); + clipPath.addRoundRect(rect, rippleRoundedCorners, rippleRoundedCorners, Path.Direction.CW); + canvas.clipPath(clipPath); + } canvas.drawCircle(currentCoords.x, currentCoords.y, radius, paint); } } else { @@ -546,19 +563,41 @@ public void setRippleInAdapter(boolean rippleInAdapter) { this.rippleInAdapter = rippleInAdapter; } + public void setRippleRoundedCorners(int rippleRoundedCorner) { + this.rippleRoundedCorners = rippleRoundedCorner; + enableClipPathSupportIfNecessary(); + } + public void setDefaultRippleAlpha(int alpha) { this.rippleAlpha = alpha; paint.setAlpha(alpha); invalidate(); } + /** + * {@link Canvas#clipPath(Path)} is not supported in hardware accelerated layers + * before API 18. Use software layer instead + *

+ * https://developer.android.com/guide/topics/graphics/hardware-accel.html#unsupported + */ + private void enableClipPathSupportIfNecessary() { + if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.JELLY_BEAN_MR1) { + if (rippleRoundedCorners != 0) { + layerType = getLayerType(); + setLayerType(LAYER_TYPE_SOFTWARE, null); + } else { + setLayerType(layerType, null); + } + } + } + /* * Helper */ private class PerformClickEvent implements Runnable { @Override public void run() { - if(mHasPerformedLongPress) return; + if (mHasPerformedLongPress) return; // if parent is an AdapterView, try to call its ItemClickListener if (getParent() instanceof AdapterView) { @@ -627,6 +666,7 @@ public static class RippleBuilder { private boolean ripplePersistent = DEFAULT_PERSISTENT; private int rippleBackground = DEFAULT_BACKGROUND; private boolean rippleSearchAdapter = DEFAULT_SEARCH_ADAPTER; + private float rippleRoundedCorner = DEFAULT_ROUNDED_CORNERS; public RippleBuilder(View child) { this.child = child; @@ -688,6 +728,11 @@ public RippleBuilder rippleInAdapter(boolean inAdapter) { return this; } + public RippleBuilder rippleRoundedCorners(int radiusDp) { + this.rippleRoundedCorner = radiusDp; + return this; + } + public MaterialRippleLayout create() { MaterialRippleLayout layout = new MaterialRippleLayout(context); layout.setRippleColor(rippleColor); @@ -701,6 +746,7 @@ public MaterialRippleLayout create() { layout.setRippleOverlay(rippleOverlay); layout.setRippleBackground(rippleBackground); layout.setRippleInAdapter(rippleSearchAdapter); + layout.setRippleRoundedCorners((int) dpToPx(context.getResources(), rippleRoundedCorner)); ViewGroup.LayoutParams params = child.getLayoutParams(); ViewGroup parent = (ViewGroup) child.getParent(); diff --git a/library/src/main/res/values/attributes.xml b/library/src/main/res/values/attributes.xml index 6574b5c..03fc85b 100644 --- a/library/src/main/res/values/attributes.xml +++ b/library/src/main/res/values/attributes.xml @@ -12,5 +12,6 @@ + \ No newline at end of file