Android Question Neumorphism UI - how would you do this

Mark Stuart

Active Member
Licensed User
Longtime 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
 

Samara

Member
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)

View attachment 86825

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

This is a good idea, but it would be great if we could make the shadow effect more flexible for click interaction:
  1. Ability to swap the positions of the colors (so the light and dark shadows can switch places).
  2. The shadow effect should be applied inside the view instead of outside (inset shadows).
  3. press (click), so the inset shadows can change or animate when the view is pressed.
any idea?
 
Upvote 0
Top