diff --git a/CHANGELOG.md b/CHANGELOG.md index 2002524..6966595 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,21 @@ +# 1.0.5 + +1. Fixed [**issue #12**: Lollipop elevation disable shadow](https://github.com/shell-software/fab/issues/12): + + The fix enables elevation on devices with **API 21 Lollipop** and higher. Now if elevation is set and the device *API* meets requirements (has *API 21 Lollipop* and higher) elevation will be drawn instead of the default shadow. + In this case configuration of any of the default shadow's parameters will be ignored. + Previously elevation was not drawn for such devices if set. + + A fix was applied to: + + * **hasShadow()** method: now if **Action Button** has elevation enabled (for *API 21 Lollipop* and higher) the shadow won't be drawn at all + * **calculateCenterX()** method: **getWidth()** method replaced by **getMeasuredWidth()** to calculate *X-axis* coordinate + * **calculateCenterY()** method: **getHeight()** method replaced by **getMeasuredHeight()** is used to calculate *Y-axis* coordinate + + New methods added: + + * **drawElevation()**: protected void method, which is called by **onDraw(Canvas)** to draw the elevation for *API 21 Lollipop* devices and higher + # 1.0.4 1. Fixed [**issue #8**: Both buttons show up when I only want one at a time](https://github.com/shell-software/fab/issues/8): diff --git a/README.md b/README.md index fd2eb06..b850d8d 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,12 @@ [](https://travis-ci.org/shell-software/fab) [](http://search.maven.org/#search|gav|1|g%3A%22com.github.shell-software%22%20AND%20a%3A%22fab%22) [](https://android-arsenal.com/details/1/1522) -[](https://gitter.im/shell-software/fab?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) + +## Donation + +Donation helps to improve the project development and speed up the release of new versions. I appreciate any contribution + +[](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=44CVJBPFRKXJL) ## Description @@ -19,7 +24,7 @@ The Library requires **Android SDK version 9 (Gingerbread)** and higher. ```java dependencies { - compile 'com.github.shell-software:fab:1.0.4' + compile 'com.github.shell-software:fab:1.0.5' } ``` @@ -27,7 +32,25 @@ dependencies { [**Full ChangeLog**](https://github.com/shell-software/fab/blob/master/CHANGELOG.md) -### 1.0.4 - *current* +### 1.0.5 - *current* + +1. Fixed [**issue #12**: Lollipop elevation disable shadow](https://github.com/shell-software/fab/issues/12): + + The fix enables elevation on devices with **API 21 Lollipop** and higher. Now if elevation is set and the device *API* meets requirements (has *API 21 Lollipop* and higher) elevation will be drawn instead of the default shadow. + In this case configuration of any of the default shadow's parameters will be ignored. + Previously elevation was not drawn for such devices if set. + + A fix was applied to: + + * **hasShadow()** method: now if **Action Button** has elevation enabled (for *API 21 Lollipop* and higher) the shadow won't be drawn at all + * **calculateCenterX()** method: **getWidth()** method replaced by **getMeasuredWidth()** to calculate *X-axis* coordinate + * **calculateCenterY()** method: **getHeight()** method replaced by **getMeasuredHeight()** is used to calculate *Y-axis* coordinate + + New methods added: + + * **drawElevation()**: protected void method, which is called by **onDraw(Canvas)** to draw the elevation for *API 21 Lollipop* devices and higher + +### 1.0.4 - *previous* 1. Fixed [**issue #8**: Both buttons show up when I only want one at a time](https://github.com/shell-software/fab/issues/8): @@ -35,15 +58,6 @@ dependencies { This happened because of using **android.view.View#isShown()** method, which returned *false* even if the button was shown. Now these methods relay on **VISIBILITY** and work as expected wherever they called. -### 1.0.3 - *previous* - -1. **Attention!** *Deprecated* XML attributes: - - * **normal** XML attribute renamed to **DEFAULT**. - You can still use **normal** XML attribute, however it will be removed in version 2.0.0. - * **mini** XML attribute renamed to **MINI**. - You can still use **mini** XML attribute, however it will be removed in version 2.0.0. - ### Features in the next versions: * **1.1.0**: @@ -293,6 +307,7 @@ actionButton.removeShadow(); ``` > Shadow radius and offset must be specified in density-independent pixels. +> For *API 21 Lollipop* and higher **elevation** can be enabled. In this case the default shadow becomes disabled and configuration of any of its parameters will be ignored. #### Image diff --git a/build.gradle b/build.gradle index 0268a38..8e8a886 100644 --- a/build.gradle +++ b/build.gradle @@ -22,7 +22,7 @@ ext { ANDROID_BUILD_TOOLS_VERSION = '21.1.2' ANDROID_MIN_SDK_VERSION = 9 ANDROID_TARGET_SDK_VERSION = 21 - ANDROID_VERSION_CODE = 5 + ANDROID_VERSION_CODE = 6 } diff --git a/fab/src/main/java/com/software/shell/fab/ActionButton.java b/fab/src/main/java/com/software/shell/fab/ActionButton.java index 35c045c..0da0da5 100644 --- a/fab/src/main/java/com/software/shell/fab/ActionButton.java +++ b/fab/src/main/java/com/software/shell/fab/ActionButton.java @@ -22,10 +22,7 @@ import android.annotation.TargetApi; import android.content.Context; import android.content.res.TypedArray; -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; +import android.graphics.*; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.os.Build; @@ -34,6 +31,7 @@ import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewOutlineProvider; import android.view.animation.Animation; import android.view.animation.AnimationUtils; @@ -42,7 +40,7 @@ * Material Design * * @author Vladislav - * @version 1.0.2 + * @version 1.0.3 * @since 1.0.0 */ public class ActionButton extends View { @@ -613,13 +611,14 @@ public void setButtonColorPressed(int buttonColorPressed) { } /** - * Checks whether Action Button has shadow - * by determining shadow radius + * Checks whether Action Button has shadow by determining shadow radius + *
+ * Shadow is disabled if elevation is set API level is {@code 21 Lollipop} and higher * * @return true if Action Button has radius, otherwise false */ public boolean hasShadow() { - return getShadowRadius() > 0.0f; + return !hasElevation() && getShadowRadius() > 0.0f; } /** @@ -1058,6 +1057,9 @@ protected void onDraw(Canvas canvas) { super.onDraw(canvas); Log.v(LOG_TAG, "Action Button onDraw called"); drawCircle(canvas); + if (hasElevation()) { + drawElevation(); + } if (hasStroke()) { drawStroke(canvas); } @@ -1068,7 +1070,7 @@ protected void onDraw(Canvas canvas) { /** * Draws the main circle of the Action Button and calls - * {@link #drawShadow()} to draw shadow if present + * {@link #drawShadow()} to draw the shadow if present * * @param canvas canvas, on which circle is to be drawn */ @@ -1089,7 +1091,7 @@ protected void drawCircle(Canvas canvas) { * @return X-axis center coordinate of the entire view */ protected float calculateCenterX() { - final float centerX = getWidth() / 2; + final float centerX = getMeasuredWidth() / 2; Log.v(LOG_TAG, "Calculated center X = " + centerX); return centerX; } @@ -1100,7 +1102,7 @@ protected float calculateCenterX() { * @return Y-axis center coordinate of the entire view */ protected float calculateCenterY() { - final float centerY = getHeight() / 2; + final float centerY = getMeasuredHeight() / 2; Log.v(LOG_TAG, "Calculated center Y = " + centerY); return centerY; } @@ -1120,12 +1122,23 @@ protected final float calculateCircleRadius() { * Draws the shadow if view elevation is not enabled */ protected void drawShadow() { - if (hasElevation()) { - Log.w(LOG_TAG, "Elevation is enabled, skipping shadow enabling"); - } else { - paint.setShadowLayer(getShadowRadius(), getShadowXOffset(), getShadowYOffset(), getShadowColor()); - Log.v(LOG_TAG, "Shadow enabled"); - } + paint.setShadowLayer(getShadowRadius(), getShadowXOffset(), getShadowYOffset(), getShadowColor()); + Log.v(LOG_TAG, "Shadow drawn"); + } + + /** + * Draws the elevation around the main circle + *
+ * Uses the stroke corrective, which helps to avoid the elevation overlapping issue + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + protected void drawElevation() { + final int strokeWeightCorrective = (int) (getStrokeWidth() / 1.5f); + final int width = getWidth() - strokeWeightCorrective; + final int height = getHeight() - strokeWeightCorrective; + final ViewOutlineProvider outlineProvider = new ActionButtonOutlineProvider(width, height); + setOutlineProvider(outlineProvider); + Log.v(LOG_TAG, "Elevation drawn"); } /** @@ -1192,7 +1205,7 @@ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { * @return measured width in actual pixels for the entire view */ private int calculateMeasuredWidth() { - final int measuredWidth = (int) (getButtonSize() + calculateShadowWidth() * 2 + getStrokeWidth() * 2); + final int measuredWidth = getButtonSize() + calculateShadowWidth() + calculateStrokeWeight(); Log.v(LOG_TAG, "Calculated measured width = " + measuredWidth); return measuredWidth; } @@ -1203,7 +1216,7 @@ private int calculateMeasuredWidth() { * @return measured width in actual pixels for the entire view */ private int calculateMeasuredHeight() { - final int measuredHeight = (int) (getButtonSize() + calculateShadowHeight() * 2 + getStrokeWidth() * 2); + final int measuredHeight = getButtonSize() + calculateShadowHeight() + calculateStrokeWeight(); Log.v(LOG_TAG, "Calculated measured height = " + measuredHeight); return measuredHeight; } @@ -1213,8 +1226,8 @@ private int calculateMeasuredHeight() { * * @return shadow width in actual pixels */ - private float calculateShadowWidth() { - final float shadowWidth = hasShadow() ? getShadowRadius() + Math.abs(getShadowXOffset()) : 0.0f; + private int calculateShadowWidth() { + final int shadowWidth = hasShadow() ? (int) ((getShadowRadius() + Math.abs(getShadowXOffset())) * 2) : 0; Log.v(LOG_TAG, "Calculated shadow width = " + shadowWidth); return shadowWidth; } @@ -1224,12 +1237,23 @@ private float calculateShadowWidth() { * * @return shadow height in actual pixels */ - private float calculateShadowHeight() { - final float shadowHeight = hasShadow() ? getShadowRadius() + Math.abs(getShadowYOffset()) : 0.0f; + private int calculateShadowHeight() { + final int shadowHeight = hasShadow() ? (int) ((getShadowRadius() + Math.abs(getShadowYOffset())) * 2) : 0; Log.v(LOG_TAG, "Calculated shadow height = " + shadowHeight); return shadowHeight; } + /** + * Calculates the stroke weight in actual pixels + * * + * @return stroke weight in actual pixels + */ + private int calculateStrokeWeight() { + final int strokeWeight = (int) (getStrokeWidth() * 2.0f); + Log.v(LOG_TAG, "Calculated stroke weight is: " + strokeWeight); + return strokeWeight; + } + /** * Determines the Action Button types */ diff --git a/fab/src/main/java/com/software/shell/fab/ActionButtonOutlineProvider.java b/fab/src/main/java/com/software/shell/fab/ActionButtonOutlineProvider.java new file mode 100644 index 0000000..d154c39 --- /dev/null +++ b/fab/src/main/java/com/software/shell/fab/ActionButtonOutlineProvider.java @@ -0,0 +1,74 @@ +/* + * Copyright 2015 Shell Software Inc. + * + * 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. + * + * File created: 2015-02-25 19:54:28 + */ + +package com.software.shell.fab; + +import android.annotation.TargetApi; +import android.graphics.Outline; +import android.os.Build; +import android.view.View; +import android.view.ViewOutlineProvider; + +/** + * An implementation of the {@link android.view.ViewOutlineProvider} + * for Action Button + * + * Used for drawing the elevation shadow for {@code API 21 Lollipop} and higher + * + * @author Vladislav + * @version 1.0.0 + * @since 1.0.0 + */ +@TargetApi(Build.VERSION_CODES.LOLLIPOP) +class ActionButtonOutlineProvider extends ViewOutlineProvider { + + /** + * Outline provider width + */ + private int width; + + /** + * Outline provider height + */ + private int height; + + /** + * Creates an instance of the {@link com.software.shell.fab.ActionButtonOutlineProvider} + * + * @param width initial outline provider width + * @param height initial outline provider height + */ + ActionButtonOutlineProvider(int width, int height) { + this.width = width; + this.height = height; + } + + /** + * Called to get the provider to populate the Outline. This method will be called by a View + * when its owned Drawables are invalidated, when the View's size changes, or if invalidateOutline() + * is called explicitly. The input outline is empty and has an alpha of 1.0f + * + * @param view a view, which builds the outline + * @param outline an empty outline, which is to be populated + */ + @Override + public void getOutline(View view, Outline outline) { + outline.setOval(0, 0, width, height); + } + +}