Android Question Neumorphism UI - how would you do this

mfstuart

Active Member
Licensed User
Hi all,
I discovered this just yesterday and wanted to create it for the Android tablet platform.
This new UI comes from this website:
1577386286555.png

As you can see, it is a very subtle clean look, where the Elevation top color of the View object (most likely a Panel) is White and the bottom color is a very light gray.
(See the website link above for actual colors)
I've tried playing around with the standard properties of the Panel View and not able to get the effect. (In B4J, you can set CSS properties and most likely get this effect, but haven't tried)
I've also searched the forum and found some threads that use bitmaps and such, but I've never used any of that in my apps, and don't understand it.

Is this effect even possible for any B4A views, and if so, how would it be done?

Thanx,
Mark Stuart
 

sorex

Expert
Licensed User
I would just create it in photoshop and load it as background for a fully centered label.
As the dropshodow goes into opposite directions the image remains full square so the centering should work out of the box.
 
Upvote 0

mfstuart

Active Member
Licensed User
Hi Sorex, I don't use any image editing tool.

I was hoping to apply this effect within B4A only, using the CreateBitmap, or Canvas or something like that.
Again, not knowing any of this "imaging" stuff, it becomes a challenge for me.

Are there any videos (maybe from Erel) that shows how all this works using B4A?

Thanx,
Mark Stuart
 
Upvote 0

sorex

Expert
Licensed User
there's ways to do drop shadows with code but I doubt it will look as good as in your example.

and it looks way different on IOS aswell compared to Android.

with that common method you probably need 2 labels or placeholders on top of each other with each having their own dropshadow into opposite direction.

maybe there is a way with bitmap drawing as you suggested but I'll have to search for that myself as I don't know any straight method without additional libs.
 
Upvote 0

mfstuart

Active Member
Licensed User
Hey all,
Here's my attempt at this concept using only B4A's properties settings - no extra scripts to change any of the views used.
Views used: Panels and Labels. Panels are the main object and the images are in the Label views using FontAwesome images.

1577466699364.png

It gets somewhat there but missing the top white "shadow". B4A does not have that feature "out of the box". Would be nice to be able to apply CSS to the view objects like you can in B4J. I will have to try it in B4J just to see if it works.

Thanx guys for pointing out how to use an alternative avenue to apply effects to text. I assume they get saved as Bitmap images and are used as resources in the app.

Anyone know Bitmap or Canvas libraries well enough to apply these white shadow effects to view objects?

Thanx,
Mark Stuart
 
Upvote 0

sorex

Expert
Licensed User
It gets somewhat there but missing the top white "shadow". B4A does not have that feature "out of the box".

that's what I wrote in my earlier posts. you need 2 objects on top of each other to get the 2 opposite direction dropshadows.
 
Upvote 0

sorex

Expert
Licensed User
just had a look at it. the behaviour is different from IOS where you can cast a shadow from the label's bounding box.
here it only does it from the text inside the label.

you probably used elevation but you can't set the angle of it nor the colors.

with bitmap creator things are also limited unless you start using feathered brush images but then you better use the fully prepared images as it is a one pass thing then.

I guess the image method is the only (read easiest) way then. (or use one of the old image processing libs but I don't know if they have fixed shadow angles or not)
 
Upvote 0

Sagenut

Well-Known Member
Licensed User
Just my 2 Cents to this discussion.
Here I provide a transparent image with just the Shadows.
For now only the Round version.
It has large size so it should look good and detailed even on large screens.
Try to put it in an ImageView on different colors background to see how it react.
If you like the effect then we can work to perfect the shadows, size and all the rest.
 

Attachments

  • Round Shadows Button.png
    Round Shadows Button.png
    28.5 KB · Views: 139
Upvote 0

Sagenut

Well-Known Member
Licensed User
To me it looks like this.
 

Attachments

  • Result.jpg
    Result.jpg
    87.8 KB · Views: 143
Upvote 0

JordiCP

Well-Known Member
Licensed User
I like this UI so I made another approach, with code. CornerRadius can be specified and could be applied to any View (Labels have been used in this example)
Could be completed to calculate the darkcolor and lightcolor from a main given color (here the colors from the web have been used)

Screenshot_20191228-164257.png


Code
B4X:
Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim mainColor As Int = 0xFFE0E5EC
    Dim lightColor As Int = 0xFFFFFFFF
    Dim darkColor As Int  = 0xFFA3B1C6
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")

    Activity.Color = mainColor

    Dim P As Label = createLabel("Happy")
    Dim WW As Int = Activity.Width/2
    Dim HH As Int = WW/2
    Activity.AddView(P, (Activity.Width-WW)/2, Activity.Height/4-HH/2, WW, HH)
    GenerateViewShadow(P, WW/6, 0.2)

    Dim P2 As Label = createLabel("NEW")
    Dim WW As Int = Activity.Width/2
    Dim HH As Int = WW
    Activity.AddView(P2, (Activity.Width-WW)/2, 2*Activity.Height/4-HH/2, WW, HH)
    GenerateViewShadow(P2, WW/2, 0.2)

    Dim P3 As Label = createLabel("Year!!")
    Dim WW As Int = Activity.Width/3
    Dim HH As Int = WW/2
    Activity.AddView(P3, (Activity.Width-WW)/2, 3*Activity.Height/4-HH/2, WW, HH)

    GenerateViewShadow(P3, P.Width/12, 0.2)
    
End Sub
    
Sub createLabel(myText As String) As Label
    Dim l As Label
    l.Initialize("")
    l.Gravity = Gravity.CENTER
    l.TextColor = Colors.Red
    l.TextSize = 20
    l.Text = myText
    Return l
End Sub
    
    
Sub GenerateViewShadow( P As B4XView, cornerRadius As Int, insetPercentage As Float)

    Dim BigWW As Int  = P.Width
    Dim BigHH As Int = P.Height
    
    ' inset percentage will be according to the smallest dim, but will be the same for both direction
    Dim absInset As Float = insetPercentage*Min(BigWW, BigHH)
    Dim SmallWW As Int = BigWW - absInset
    Dim SmallHH As Int = BigHH - absInset

    Dim CVX As B4XCanvas        
    CVX.Initialize(P)

    Dim PX As B4XPath
    Dim R0 As B4XRect
    Dim dW As Int = (BigWW-SmallWW)/2
    Dim dH As Int = (BigHH-SmallHH)/2
    Dim d As Int = Max(dW, dH)
    
    ' UPPER (light) shadow    
    For k = 0 To d         
        Dim kW As Float = k*dW/d
        Dim kH As Float = k*dH/d
        R0.Initialize( kW , kH, SmallWW+dW-kW, SmallHH+dH-kH)
        PX.InitializeRoundedRect(R0, cornerRadius)    
        CVX.ClipPath(PX)
        Dim pColor As Int = FindSolidColorBetween(mainColor, lightColor, 1.0*k*k*k/(d*d*d))
        CVX.DrawRect(R0, pColor, True, 0)
        CVX.RemoveClip
    Next

    ' LOWER (dark) shadow
    For k = 0 To d
        Dim kW As Float = k*dW/d
        Dim kH As Float = k*dH/d
        R0.Initialize( 2*dW-kW , 2*dH-kH, 2*dW+SmallWW-kW, 2*dH+SmallHH-kH)
        PX.InitializeRoundedRect(R0, cornerRadius)
        CVX.ClipPath(PX)
        Dim pColor As Int = FindSolidColorBetween(mainColor, darkColor, 1.0*k*k*k/(d*d*d))
        CVX.DrawRect(R0, pColor, True, 0)
        CVX.RemoveClip
    Next

    ' Draw the 'plain' area with the main color.
    R0.Initialize( dW , dH, dW+SmallWW, dH+SmallHH)
    PX.InitializeRoundedRect(R0, cornerRadius)
    CVX.ClipPath(PX)
    CVX.DrawRect(R0, mainColor, True, 0)
    CVX.RemoveClip

End Sub

' progress=0 --> we get colorA
' progress=1 -> we get colorB
Sub FindSolidColorBetween(colorA As Int, colorB As Int, progress As Float) As Int
    Dim weight As Int = 256*(1-Max(0, Min(progress, 1)))
    Dim finalRed As Int   = Bit.ShiftRight(weight*Bit.And(Bit.ShiftRight(colorA,16),0xFF) + (256-weight)*Bit.And(Bit.ShiftRight(colorB,16),0xFF),8)
    Dim finalGreen As Int = Bit.ShiftRight(weight*Bit.And(Bit.ShiftRight(colorA, 8),0xFF) + (256-weight)*Bit.And(Bit.ShiftRight(colorB, 8),0xFF),8)
    Dim finalBlue As Int  = Bit.ShiftRight(weight*Bit.And(Bit.ShiftRight(colorA, 0),0xFF) + (256-weight)*Bit.And(Bit.ShiftRight(colorB, 0),0xFF),8)    
    Return Colors.RGB(finalRed, finalGreen, finalBlue)
End Sub
 
Upvote 0

Sagenut

Well-Known Member
Licensed User
And the Winner is.......... @JordiCP 🏆
But We are Winners too Thanks to this nice piece of code. 😁;)
 
Upvote 0

sorex

Expert
Licensed User
nice approach, Jordi.

I had something similar by using bitmapcreator drawing but the shadow didn't look feathered/blurred as I didn't go for looped drawing
and it needed seperate code for the rounded square one.
 
Upvote 0

LucaMs

Expert
Licensed User
I like this UI so I made another approach, with code. CornerRadius can be specified and could be applied to any View (Labels have been used in this example)
Could be completed to calculate the darkcolor and lightcolor from a main given color (here the colors from the web have been used)
Great job, Jordi.

I also like that style very much, although it makes me feel cold, it looks like snow, freezed views! Klaus probably likes it more :)
 
Upvote 0

JordiCP

Well-Known Member
Licensed User
I also like that style very much, although it makes me feel cold, it looks like snow, freezed views!
A warmer approach :cool:


Screenshot_20191228-223937.png


New Code (simplified original routine and added the hexagonal shape)
B4X:
Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim mainColor As Int  = 0xFFC08000    '0xFFE0E5EC
    Dim lightColor As Int = 0xFFE0A000    '0xFFFFFFFF
    Dim darkColor As Int  = 0xFFA06000    '0xFFA3B1C6
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    'Activity.LoadLayout("Layout1")

    Activity.Color = mainColor

    Dim P As Label = createLabel("Warmer")
    Dim WW As Int = Activity.Width/2
    Dim HH As Int = WW/2
    Activity.AddView(P, (Activity.Width-WW)/2, Activity.Height/4-HH/2, WW, HH)
    GenerateViewShadow(P, WW/6, 0.2)

    Dim P2 As Label = createLabel("NEW")
    Dim WW As Int = Activity.Width/2
    Dim HH As Int = WW
    Activity.AddView(P2, (Activity.Width-WW)/2, 2*Activity.Height/4-HH/2, WW, HH)
    GenerateViewShadowWithPath(P2, 0.2)

    Dim P3 As Label = createLabel("Year!!")
    Dim WW As Int = Activity.Width/3
    Dim HH As Int = WW/2
    Activity.AddView(P3, (Activity.Width-WW)/2, 3*Activity.Height/4-HH/2, WW, HH)
    GenerateViewShadow(P3, P.Width/12, 0.2)    
End Sub

    
Sub createLabel(myText As String) As Label
    Dim l As Label
    l.Initialize("")
    l.Gravity = Gravity.CENTER
    l.TextColor = Colors.White
    l.TextSize = 20
    l.Text = myText
    Return l
End Sub
    
    
Sub GenerateViewShadow( P As B4XView, cornerRadius As Int, insetPercentage As Float)

    Dim CVX As B4XCanvas

    Dim BigWW As Int  = P.Width
    Dim BigHH As Int = P.Height
    
    ' inset percentage will be according to the smallest dim, but will be the same for both direction
    Dim absInset As Float = insetPercentage*Min(BigWW, BigHH)
    Dim SmallWW As Int = BigWW - absInset
    Dim SmallHH As Int = BigHH - absInset
        
    CVX.Initialize(P)

    Dim PX As B4XPath
    Dim R0 As B4XRect
    Dim dW As Int = (BigWW-SmallWW)/2
    Dim dH As Int = (BigHH-SmallHH)/2
    Dim d As Int = Max(dW, dH)
    
    ' UPPER (light) shadow    
    For k = 0 To d         
        Dim kW As Float = k*dW/d
        Dim kH As Float = k*dH/d
        R0.Initialize( kW , kH, SmallWW+dW-kW, SmallHH+dH-kH)
        PX.InitializeRoundedRect(R0, cornerRadius)    
        Dim pColor As Int = FindSolidColorBetween(mainColor, lightColor, 1.0*k*k*k/(d*d*d))
        CVX.DrawPath(PX, pColor, True, 0)
    Next

    ' LOWER (dark) shadow
    For k = 0 To d
        Dim kW As Float = k*dW/d
        Dim kH As Float = k*dH/d
        R0.Initialize( 2*dW-kW , 2*dH-kH, 2*dW+SmallWW-kW, 2*dH+SmallHH-kH)
        PX.InitializeRoundedRect(R0, cornerRadius)
        Dim pColor As Int = FindSolidColorBetween(mainColor, darkColor, 1.0*k*k*k/(d*d*d))
        CVX.DrawPath(PX, pColor, True, 0)
    Next
    
    ' Draw the 'plain' area with the main color.
    R0.Initialize( dW , dH, dW+SmallWW, dH+SmallHH)
    PX.InitializeRoundedRect(R0, cornerRadius)
    CVX.DrawPath(PX, mainColor, True, 0)

End Sub


Sub GenerateViewShadowWithPath( P As B4XView,  insetPercentage As Float)

    Dim CVX As B4XCanvas

    Dim BigWW As Int  = P.Width
    Dim BigHH As Int = P.Height
    
    ' inset percentage will be according to the smallest dim, but will be the same for both direction
    Dim absInset As Float = insetPercentage*Min(BigWW, BigHH)
    Dim SmallWW As Int = BigWW - absInset
    Dim SmallHH As Int = BigHH - absInset

    ' Let's build an hexagonal (closed) Path
    Dim PathPoints(7,2) As Float
    For k=0 To 6
        PathPoints(k,0) = SmallWW/2*(1+CosD(60*k))
        PathPoints(k,1) = SmallHH/2*(1+SinD(60*k))
    Next
        
    CVX.Initialize(P)

    Dim PX As B4XPath
    Dim dW As Int = (BigWW-SmallWW)/2
    Dim dH As Int = (BigHH-SmallHH)/2
    Dim d As Int = Max(dW, dH)
    
    ' UPPER (light) shadow    
    For k = 0 To d         
        Dim kW As Float = k*dW/d
        Dim kH As Float = k*dH/d
        PX.Initialize(PathPoints(0,0)+kW, PathPoints(0,1)+kH)
        For c=1 To 6
            PX.LineTo(PathPoints(c,0)+kW, PathPoints(c,1)+kH)            
        Next
        Dim pColor As Int = FindSolidColorBetween(mainColor, lightColor, 1.0*k*k*k/(d*d*d))
        CVX.DrawPath(PX, pColor, True, 0)
    Next

    ' LOWER (dark) shadow
    For k = 0 To d
        Dim kW As Float = k*dW/d
        Dim kH As Float = k*dH/d
        PX.Initialize(PathPoints(0,0)+2*dW-kW, PathPoints(0,1)+2*dH-kH)
        For c=1 To 6
            PX.LineTo(PathPoints(c,0)+2*dW-kW, PathPoints(c,1)+2*dH-kH)
        Next
        Dim pColor As Int = FindSolidColorBetween(mainColor, darkColor, 1.0*k*k*k/(d*d*d))
        CVX.DrawPath(PX, pColor, True, 0)
    Next

    ' Draw the 'plain' area with the main color.
    PX.Initialize(PathPoints(0,0)+dW, PathPoints(0,1)+dH)
    For k=0 To 6
        PX.LineTo(PathPoints(k,0)+dW, PathPoints(k,1)+dH)
    Next
    CVX.DrawPath(PX, mainColor, True, 0)

End Sub


' progress=0 --> we get colorA
' progress=1 -> we get colorB
Sub FindSolidColorBetween(colorA As Int, colorB As Int, progress As Float) As Int

    Dim weight As Int = 256*(1-Max(0, Min(progress, 1)))
    Log("val:"&weight)
    Dim finalRed As Int   = Bit.ShiftRight(weight*Bit.And(Bit.ShiftRight(colorA,16),0xFF) + (256-weight)*Bit.And(Bit.ShiftRight(colorB,16),0xFF),8)
    Dim finalGreen As Int = Bit.ShiftRight(weight*Bit.And(Bit.ShiftRight(colorA, 8),0xFF) + (256-weight)*Bit.And(Bit.ShiftRight(colorB, 8),0xFF),8)
    Dim finalBlue As Int  = Bit.ShiftRight(weight*Bit.And(Bit.ShiftRight(colorA, 0),0xFF) + (256-weight)*Bit.And(Bit.ShiftRight(colorB, 0),0xFF),8)    
    Return Colors.RGB(finalRed, finalGreen, finalBlue)
End Sub
 
Upvote 0
Top