Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Feature: Added support for background drawables #42

Open
wants to merge 45 commits into
base: main
Choose a base branch
from
Open
Changes from 3 commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
a5719b0
Library: Added background drawable support
Aug 27, 2020
8257d29
Sample: Added background drawable examples
Aug 27, 2020
5df7ef4
Merge pull request #1 from tamimattafi/dev
tamimattafi Aug 28, 2020
17bc908
Library: Fix #41 - setBackgroundDrawable called before shapeDrawable …
tamimattafi Aug 28, 2020
6be1002
Merge branch 'master' of https://github.com/fornewid/neumorphism
tamimattafi Aug 28, 2020
ca9c881
Library: Fixed app crashing when initializing a component that is bas…
tamimattafi Aug 28, 2020
a066254
Library: Code refactoring
tamimattafi Aug 28, 2020
e1d287d
Library: added reusability for shadow and background drawables
tamimattafi Jan 22, 2021
106a526
Optimizations: Made reusable bitmaps and shapes references softly rea…
Feb 20, 2021
cfcdbc2
Project: Updated target api version, updated kotlin and gradle wrappe…
Feb 20, 2021
0954522
Optimization: Changed rect hash code calculation to depend on width a…
Feb 20, 2021
8d045ae
fix: Added max cache size, removed bitmap cache
Nov 12, 2021
d866bac
feat: Add button click animation (partially)
Nov 12, 2021
cc2c2dd
update: Upgrade dependecies versions
Nov 24, 2021
42b18ad
config: Downgrade kotlin and gradle versions
Nov 24, 2021
a923580
feat: Create shadow drawable
Feb 4, 2022
c2c1d82
fix: Correct press animation
Feb 4, 2022
db5a05f
Merge pull request #2 from tamimattafi/feat/press_animation
tamimattafi Feb 4, 2022
d834ffd
fix: Remove inset, adjust pressed shape shadows
Feb 5, 2022
61434c8
fix: Correct flat shape shadow
Feb 5, 2022
2b43c88
fix: Add oval shadow coverage
Feb 6, 2022
a392fca
fix: Merge light and dark shadow in one drawable
Feb 7, 2022
9c9775c
fix: Combine light and dark shadows in one bitmap
Feb 8, 2022
ee4b3ff
fix: Correct radius and elevation
Feb 8, 2022
4cbddce
fix: Use arcs to draw shadow
Feb 8, 2022
b21aad3
fix: Correct outer shadow appearance
Feb 8, 2022
67180ad
fix: Add new shadow drawable
Feb 9, 2022
090659f
fix: Inner and outer shadow elevation
Feb 9, 2022
7236b99
fix: Use blur paint instead of rs and stack
Feb 9, 2022
b918a23
fix: Correct inner shadow
Feb 9, 2022
07c3106
fix: Fixed bitmap caching
Feb 10, 2022
1a8d79d
fix: Add simple press animation
Feb 10, 2022
5597bc8
feat: Add radius attribute to xml
Feb 10, 2022
4f3f9a4
Merge pull request #3 from tamimattafi/fix/shadow_elevation
tamimattafi Feb 10, 2022
e7b6f91
feat: Add flat press animation
Feb 10, 2022
bfd73b6
fix: add basin press animation
Feb 10, 2022
33fcaf8
feat: Add support for shape drawable
Feb 10, 2022
189ffa6
Merge pull request #4 from tamimattafi/feat/click_animation
tamimattafi Feb 10, 2022
605e9cc
fix: Crash when setting drawable state from xml
Feb 10, 2022
700907a
update: Migrate to material components
Feb 10, 2022
a288797
fix: Oval shape is sharp for rectangles
Feb 10, 2022
2d8519c
refactor: Use LruCache for caching bitmaps, use 1/6 of available VM m…
Feb 11, 2022
f0c6f56
refactor: Add possibility to configure cache size
Feb 11, 2022
9dbaced
fix: Optimize basin shape
Feb 11, 2022
349c266
fix: put a low cache size instead of dynamic one
Apr 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion neumorphism/src/main/java/soup/neumorphism/NeumorphButton.kt
Original file line number Diff line number Diff line change
@@ -28,6 +28,7 @@ class NeumorphButton @JvmOverloads constructor(
val a = context.obtainStyledAttributes(
attrs, R.styleable.NeumorphButton, defStyleAttr, defStyleRes
)
val backgroundDrawable = a.getDrawable(R.styleable.NeumorphImageButton_neumorph_backgroundDrawable)
val fillColor = a.getColorStateList(R.styleable.NeumorphButton_neumorph_backgroundColor)
val strokeColor = a.getColorStateList(R.styleable.NeumorphButton_neumorph_strokeColor)
val strokeWidth = a.getDimension(R.styleable.NeumorphButton_neumorph_strokeWidth, 0f)
@@ -68,6 +69,7 @@ class NeumorphButton @JvmOverloads constructor(
setShadowElevation(shadowElevation)
setShadowColorLight(shadowColorLight)
setShadowColorDark(shadowColorDark)
setBackgroundDrawable(backgroundDrawable)
setFillColor(fillColor)
setStroke(strokeWidth, strokeColor)
setTranslationZ(translationZ)
@@ -87,7 +89,7 @@ class NeumorphButton @JvmOverloads constructor(
}

override fun setBackgroundDrawable(drawable: Drawable?) {
Log.i(LOG_TAG, "Setting a custom background is not supported.")
shapeDrawable.setBackgroundDrawable(drawable)
}

private fun setBackgroundInternal(drawable: Drawable?) {
Original file line number Diff line number Diff line change
@@ -30,6 +30,7 @@ class NeumorphCardView @JvmOverloads constructor(
val a = context.obtainStyledAttributes(
attrs, R.styleable.NeumorphCardView, defStyleAttr, defStyleRes
)
val backgroundDrawable = a.getDrawable(R.styleable.NeumorphImageButton_neumorph_backgroundDrawable)
val fillColor = a.getColorStateList(R.styleable.NeumorphCardView_neumorph_backgroundColor)
val strokeColor = a.getColorStateList(R.styleable.NeumorphCardView_neumorph_strokeColor)
val strokeWidth = a.getDimension(R.styleable.NeumorphCardView_neumorph_strokeWidth, 0f)
@@ -70,6 +71,7 @@ class NeumorphCardView @JvmOverloads constructor(
setShadowElevation(shadowElevation)
setShadowColorLight(shadowColorLight)
setShadowColorDark(shadowColorDark)
setBackgroundDrawable(backgroundDrawable)
setFillColor(fillColor)
setStroke(strokeWidth, strokeColor)
setTranslationZ(translationZ)
@@ -100,7 +102,7 @@ class NeumorphCardView @JvmOverloads constructor(
}

override fun setBackgroundDrawable(drawable: Drawable?) {
Log.i(LOG_TAG, "Setting a custom background is not supported.")
shapeDrawable.setBackgroundDrawable(drawable)
}

private fun setBackgroundInternal(drawable: Drawable?) {
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ class NeumorphFloatingActionButton @JvmOverloads constructor(
val a = context.obtainStyledAttributes(
attrs, R.styleable.NeumorphFloatingActionButton, defStyleAttr, defStyleRes
)

val backgroundDrawable = a.getDrawable(R.styleable.NeumorphImageButton_neumorph_backgroundDrawable)
val fillColor = a.getColorStateList(R.styleable.NeumorphFloatingActionButton_neumorph_backgroundColor)
val strokeColor = a.getColorStateList(R.styleable.NeumorphFloatingActionButton_neumorph_strokeColor)
val strokeWidth = a.getDimension(R.styleable.NeumorphFloatingActionButton_neumorph_strokeWidth, 0f)
@@ -69,6 +71,7 @@ class NeumorphFloatingActionButton @JvmOverloads constructor(
setShadowElevation(shadowElevation)
setShadowColorLight(shadowColorLight)
setShadowColorDark(shadowColorDark)
setBackgroundDrawable(backgroundDrawable)
setFillColor(fillColor)
setStroke(strokeWidth, strokeColor)
setTranslationZ(translationZ)
@@ -88,7 +91,7 @@ class NeumorphFloatingActionButton @JvmOverloads constructor(
}

override fun setBackgroundDrawable(drawable: Drawable?) {
Log.i(LOG_TAG, "Setting a custom background is not supported.")
shapeDrawable.setBackgroundDrawable(drawable)
}

private fun setBackgroundInternal(drawable: Drawable?) {
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ class NeumorphImageButton @JvmOverloads constructor(
val a = context.obtainStyledAttributes(
attrs, R.styleable.NeumorphImageButton, defStyleAttr, defStyleRes
)

val backgroundDrawable = a.getDrawable(R.styleable.NeumorphImageButton_neumorph_backgroundDrawable)
val fillColor = a.getColorStateList(R.styleable.NeumorphImageButton_neumorph_backgroundColor)
val strokeColor = a.getColorStateList(R.styleable.NeumorphImageButton_neumorph_strokeColor)
val strokeWidth = a.getDimension(R.styleable.NeumorphImageButton_neumorph_strokeWidth, 0f)
@@ -68,10 +70,12 @@ class NeumorphImageButton @JvmOverloads constructor(
setShadowElevation(shadowElevation)
setShadowColorLight(shadowColorLight)
setShadowColorDark(shadowColorDark)
setBackgroundDrawable(backgroundDrawable)
setFillColor(fillColor)
setStroke(strokeWidth, strokeColor)
setTranslationZ(translationZ)
}

internalSetInset(
if (insetStart >= 0) insetStart else inset,
if (insetTop >= 0) insetTop else inset,
@@ -87,7 +91,7 @@ class NeumorphImageButton @JvmOverloads constructor(
}

override fun setBackgroundDrawable(drawable: Drawable?) {
Log.i(LOG_TAG, "Setting a custom background is not supported.")
shapeDrawable.setBackgroundDrawable(drawable)
}

private fun setBackgroundInternal(drawable: Drawable?) {
Original file line number Diff line number Diff line change
@@ -28,6 +28,8 @@ class NeumorphImageView @JvmOverloads constructor(
val a = context.obtainStyledAttributes(
attrs, R.styleable.NeumorphImageView, defStyleAttr, defStyleRes
)

val backgroundDrawable = a.getDrawable(R.styleable.NeumorphImageButton_neumorph_backgroundDrawable)
val fillColor = a.getColorStateList(R.styleable.NeumorphImageView_neumorph_backgroundColor)
val strokeColor = a.getColorStateList(R.styleable.NeumorphImageView_neumorph_strokeColor)
val strokeWidth = a.getDimension(R.styleable.NeumorphImageView_neumorph_strokeWidth, 0f)
@@ -68,6 +70,7 @@ class NeumorphImageView @JvmOverloads constructor(
setShadowElevation(shadowElevation)
setShadowColorLight(shadowColorLight)
setShadowColorDark(shadowColorDark)
setBackgroundDrawable(backgroundDrawable)
setFillColor(fillColor)
setStroke(strokeWidth, strokeColor)
setTranslationZ(translationZ)
@@ -87,7 +90,7 @@ class NeumorphImageView @JvmOverloads constructor(
}

override fun setBackgroundDrawable(drawable: Drawable?) {
Log.i(LOG_TAG, "Setting a custom background is not supported.")
shapeDrawable.setBackgroundDrawable(drawable)
}

private fun setBackgroundInternal(drawable: Drawable?) {
Original file line number Diff line number Diff line change
@@ -13,6 +13,9 @@ import soup.neumorphism.internal.shape.BasinShape
import soup.neumorphism.internal.shape.FlatShape
import soup.neumorphism.internal.shape.PressedShape
import soup.neumorphism.internal.shape.Shape
import soup.neumorphism.internal.util.BitmapUtils.clipToPath
import soup.neumorphism.internal.util.BitmapUtils.clipToRadius


class NeumorphShapeDrawable : Drawable {

@@ -24,6 +27,7 @@ class NeumorphShapeDrawable : Drawable {
style = Paint.Style.FILL
color = Color.TRANSPARENT
}

private val strokePaint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
style = Paint.Style.STROKE
color = Color.TRANSPARENT
@@ -88,6 +92,15 @@ class NeumorphShapeDrawable : Drawable {
return drawableState.shapeAppearanceModel
}

fun setBackgroundDrawable(drawable: Drawable?) {
this.drawableState.backgroundDrawable = drawable
invalidateSelf()
}

fun getBackgroundDrawable(): Drawable? {
return this.drawableState.backgroundDrawable
}

fun setFillColor(fillColor: ColorStateList?) {
if (drawableState.fillColor != fillColor) {
drawableState.fillColor = fillColor
@@ -241,6 +254,10 @@ class NeumorphShapeDrawable : Drawable {
invalidateSelfIgnoreShape()
}

private fun hasBackgroundDrawable(): Boolean {
return drawableState.backgroundDrawable?.let(Drawable::isVisible) ?: false
}

private fun hasFill(): Boolean {
return (drawableState.paintStyle === Paint.Style.FILL_AND_STROKE
|| drawableState.paintStyle === Paint.Style.FILL)
@@ -258,8 +275,11 @@ class NeumorphShapeDrawable : Drawable {
}

override fun draw(canvas: Canvas) {
val prevAlpha = fillPaint.alpha
fillPaint.alpha = modulateAlpha(prevAlpha, drawableState.alpha)
val prevBackgroundAlpha = drawableState.backgroundDrawable?.alpha ?: 0
drawableState.backgroundDrawable?.alpha = modulateAlpha(prevBackgroundAlpha, drawableState.alpha)

val prevFillAlpha = fillPaint.alpha
fillPaint.alpha = modulateAlpha(prevFillAlpha, drawableState.alpha)

strokePaint.strokeWidth = drawableState.strokeWidth
val prevStrokeAlpha = strokePaint.alpha
@@ -275,16 +295,42 @@ class NeumorphShapeDrawable : Drawable {
drawFillShape(canvas)
}

if (hasBackgroundDrawable()) {
drawBackgroundDrawable(canvas)
}

shadow?.draw(canvas, outlinePath)

if (hasStroke()) {
drawStrokeShape(canvas)
}

fillPaint.alpha = prevAlpha
drawableState.backgroundDrawable?.alpha = prevBackgroundAlpha
fillPaint.alpha = prevFillAlpha
strokePaint.alpha = prevStrokeAlpha
}

private fun drawBackgroundDrawable(canvas: Canvas) {
drawableState.backgroundDrawable?.let { drawable ->

val rect = RectF()
outlinePath.computeBounds(rect, true)
val bitmap = drawable.clipToPath(rect)

val roundBitmap = when (drawableState.shapeAppearanceModel.getCornerFamily()) {
CornerFamily.OVAL -> {
bitmap.clipToRadius(bitmap.height / 2f)
}
else -> {
val cornerSize = drawableState.shapeAppearanceModel.getCornerSize()
bitmap.clipToRadius(cornerSize)
}
}

canvas.drawBitmap(roundBitmap, rect.left, rect.top, null)
}
}

private fun drawFillShape(canvas: Canvas) {
canvas.drawPath(outlinePath, fillPaint)
}
@@ -376,6 +422,7 @@ class NeumorphShapeDrawable : Drawable {
var inEditMode: Boolean = false

var inset: Rect = Rect()
var backgroundDrawable: Drawable? = null
var fillColor: ColorStateList? = null
var strokeColor: ColorStateList? = null
var strokeWidth = 0f
@@ -404,6 +451,7 @@ class NeumorphShapeDrawable : Drawable {
blurProvider = orig.blurProvider
inEditMode = orig.inEditMode
inset = Rect(orig.inset)
backgroundDrawable = orig.backgroundDrawable
fillColor = orig.fillColor
strokeColor = orig.strokeColor
strokeWidth = orig.strokeWidth
@@ -434,5 +482,6 @@ class NeumorphShapeDrawable : Drawable {
val scale = alpha + (alpha ushr 7) // convert to 0..256
return paintAlpha * scale ushr 8
}

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package soup.neumorphism.internal.util

import android.graphics.*
import android.graphics.drawable.Drawable

object BitmapUtils {

fun Drawable.clipToPath(rect: RectF): Bitmap {
val output = Bitmap.createBitmap(rect.width().toInt(), rect.height().toInt(), Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
this.setBounds(0, 0, canvas.width, canvas.height)
this.draw(canvas)
return output
}

fun Bitmap.clipToRadius(radius: Float): Bitmap {
val output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(output)
val paint = Paint()
val rect = Rect(0, 0, width, height)
val rectF = RectF(rect)
paint.isAntiAlias = true
canvas.drawARGB(0, 0, 0, 0)
canvas.drawRoundRect(rectF, radius, radius, paint)
paint.xfermode = PorterDuffXfermode(PorterDuff.Mode.SRC_IN)
canvas.drawBitmap(this, rect, rect, paint)
return output
}

}
6 changes: 6 additions & 0 deletions neumorphism/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
@@ -4,6 +4,7 @@
<attr name="colorShadowLight" format="color"/>
<attr name="colorShadowDark" format="color"/>

<attr name="neumorph_backgroundDrawable" format="reference"/>
<attr name="neumorph_backgroundColor" format="color" />
<attr name="neumorph_strokeColor" format="color" />
<attr name="neumorph_strokeWidth" format="dimension" />
@@ -37,6 +38,7 @@
<attr name="neumorph_shadowColorLight" />
<attr name="neumorph_shadowColorDark" />
<attr name="neumorph_shapeAppearance" />
<attr name="neumorph_backgroundDrawable" />
</declare-styleable>

<attr name="neumorphCardViewStyle" format="reference" />
@@ -54,6 +56,7 @@
<attr name="neumorph_shadowColorLight" />
<attr name="neumorph_shadowColorDark" />
<attr name="neumorph_shapeAppearance" />
<attr name="neumorph_backgroundDrawable" />
</declare-styleable>

<attr name="neumorphFloatingActionButtonStyle" format="reference" />
@@ -71,6 +74,7 @@
<attr name="neumorph_shadowColorLight" />
<attr name="neumorph_shadowColorDark" />
<attr name="neumorph_shapeAppearance" />
<attr name="neumorph_backgroundDrawable" />
</declare-styleable>

<attr name="neumorphImageViewStyle" format="reference" />
@@ -88,6 +92,7 @@
<attr name="neumorph_shadowColorLight" />
<attr name="neumorph_shadowColorDark" />
<attr name="neumorph_shapeAppearance" />
<attr name="neumorph_backgroundDrawable" />
</declare-styleable>

<attr name="neumorphImageButtonStyle" format="reference" />
@@ -105,6 +110,7 @@
<attr name="neumorph_shadowColorLight" />
<attr name="neumorph_shadowColorDark" />
<attr name="neumorph_shapeAppearance" />
<attr name="neumorph_backgroundDrawable" />
</declare-styleable>

<attr name="neumorphTextViewStyle" format="reference" />
36 changes: 35 additions & 1 deletion sample/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
@@ -51,7 +51,29 @@
android:layout_marginTop="48dp"
app:layout_constraintEnd_toStartOf="@id/pressed_card"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/textview" />
app:layout_constraintTop_toBottomOf="@id/textview" >


</soup.neumorphism.NeumorphCardView>

<soup.neumorphism.NeumorphCardView
android:id="@+id/neumorphCardView"
style="@style/Widget.Neumorph.CardView"
android:layout_width="150dp"
android:layout_height="150dp"
app:layout_constraintStart_toStartOf="@+id/flat_image_view"
app:layout_constraintTop_toBottomOf="@+id/pressed_image_view"
app:neumorph_backgroundDrawable="@drawable/gradation" />

<soup.neumorphism.NeumorphCardView
style="@style/Widget.Neumorph.CardView"
android:layout_width="150dp"
android:layout_height="150dp"
app:neumorph_shapeType="pressed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/neumorphCardView"
app:layout_constraintTop_toBottomOf="@+id/pressed_image_view"
app:neumorph_backgroundDrawable="@drawable/gradation" />

<soup.neumorphism.NeumorphCardView
android:id="@+id/pressed_card"
@@ -120,6 +142,17 @@
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />

<soup.neumorphism.NeumorphButton
style="@style/Widget.Neumorph.Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="36dp"
android:text="Button"
app:neumorph_backgroundDrawable="@drawable/gradation" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/button"
app:layout_constraintHorizontal_bias="0.862"
app:layout_constraintStart_toStartOf="parent" />

<soup.neumorphism.NeumorphFloatingActionButton
android:id="@+id/fab"
style="@style/Widget.Neumorph.FloatingActionButton"
@@ -132,4 +165,5 @@
app:layout_constraintEnd_toEndOf="parent"
app:neumorph_shapeAppearance="@style/CustomShapeAppearance" />


</androidx.constraintlayout.widget.ConstraintLayout>