Problem With Gradient on Label

Bill Norris

Active Member
Licensed User
Longtime User
The following code is taken from the Users Guide, to create a custom gradient on a panel. It works just like it is supposed to.

B4X:
Dim pnlGradient As Panel
pnlGradient.Initialize("")
Activity.AddView(pnlGradient, 10%x, 140dip, 80%x, 80dip)
Dim gdwGradient As GradientDrawable
Dim Cols(2) As Int
Cols(0) = Colors.Blue
Cols(1) = Colors.White
gdwGradient.Initialize("TOP_BOTTOM", Cols)
gdwGradient.CornerRadius = 10dip
pnlGradient.Background = gdwGradient

I want to use same concept to add custom gradient to a label. Only difference is I have added the label in designer rather than at run time. It does not work as expected, and I cannot figure out why. If I create the label at runtime, like the panel above, it works fine. Why does it work on label created at runtime but not on label created in Designer.

B4X:
''lblGradient is declared in Sub Globals

Dim gdwGradient As GradientDrawable
Dim Cols(2) As Int
Cols(0) = Colors.Blue
Cols(1) = Colors.White
gdwGradient.Initialize("TOP_BOTTOM", Cols)
gdwGradient.CornerRadius = 10dip
lblGradient.Background = gdwGradient
 

Bill Norris

Active Member
Licensed User
Longtime User
RE:

Beginners mistake -- I was re-declaring the labels further down in the code which was overriding the gradient assignment.
I would like to declare and assign color values to the gradient drawable in the Process Globals section of activity Main, but I get an error on compile that says

B4X:
Cannot access activity object from sub Process_Globals.

Is there another way to accomplish this, or will I just need to declare and set the gradient values in each activity? The same gradient colors are used throughout the project as part of the color theme.
 
Upvote 0

lagore

Active Member
Licensed User
Longtime User
Panels are an 'Activity Object' as they are only relevant to the activity they are declared in and cannot be referenced by other activities directly so can only be declared in 'Globals'
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
You could put the code into a code module (cols in the example):
B4X:
Sub GradientDrawable(col0 As Int, col1 As Int, Radius As Float, Style As String) As GradientDrawable
    Dim gdw As GradientDrawable
    Dim Cols(2) As Int
    Cols(0) = col0
    Cols(1) = col1
    gdw.Initialize(Style, Cols)
    gdw.CornerRadius = Radius
    Return gdw
End Sub
and access it like this in the main module:
B4X:
Sub Globals
    Dim Label1 As Label
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Label1.Initialize("Label1")
    Activity.AddView(Label1, 10dip, 10dip, 200dip, 50dip)
    Label1.Text = "Test Test Test"
    Label1.Background = cols.GradientDrawable(Colors.Red, Colors.Blue, 10dip, "TOP_BOTTOM")
End Sub
Best regards.
 

Attachments

  • GradientDrawable.zip
    5.9 KB · Views: 261
Upvote 0

Bill Norris

Active Member
Licensed User
Longtime User
RE:

Thank you Klaus -- works like a charm. Wondering if a similar approach could be done for buttons. The problem I've run into is when assigning gradients and statelists to multiple buttons on one activity, sometimes when you touch one of the buttons, a different one changes color. I believe Erel once described that you have to do a separate statelist drawable for each button, which is the approach I am currently using.
 
Upvote 0

klaus

Expert
Licensed User
Longtime User
I believe Erel once described that you have to do a separate statelist drawable for each button, which is the approach I am currently using.
Yes, you need to Dim and Initialize a new StateListDrawable for each Button.

In the attached example you find a routine in the code module for a gradient StateListDrawable.

B4X:
Sub StateListDrawable(col0 As Int, col1 As Int, col2 As Int, col3 As Int, Radius As Float, Style0 As String, Style1 As String) As StateListDrawable
    Dim colsEnabled(2) As Int
    colsEnabled(0) = col0
    colsEnabled(1) = col1
    ' Define a GradientDrawable for Enabled state
    Dim gdwEnabled As GradientDrawable
    gdwEnabled.Initialize(Style0, colsEnabled)
    gdwEnabled.CornerRadius = Radius
    ' Define two gradient colors for Pressed state
    Dim colsPressed(2) As Int
    colsPressed(0) = col2
    colsPressed(1) = col3
    ' Define a GradientDrawable for Pressed state
    Dim gdwPressed As GradientDrawable
    gdwPressed.Initialize(Style1, colsPressed)
    gdwPressed.CornerRadius = Radius
    ' Define a StateListDrawable
    Dim stdGradient As StateListDrawable
    stdGradient.Initialize
    Dim states(2) As Int
    states(0) = stdGradient.state_enabled
    states(1) = -stdGradient.state_pressed
    stdGradient.addState2(states, gdwEnabled)
    Dim states(1) As Int
    states(0) = stdGradient.state_pressed
    stdGradient.addState2(states, gdwPressed)
    Return stdGradient
End Sub
and in the activity module:
B4X:
    Button1.Background = cols.StateListDrawable(Colors.Red, Colors.Blue, Colors.Green, Colors.Yellow, 10dip, "TOP_BOTTOM", "TOP_BOTTOM")
Best regards.
 

Attachments

  • GradientDrawable1.zip
    6.3 KB · Views: 207
Upvote 0

Roger Garstang

Well-Known Member
Licensed User
Longtime User
My most recent ButtonGradient function is:

B4X:
Sub ButtonGradient(LightColor As Int, DarkColor As Int) As StateListDrawable
Dim ref As Reflector

   ' Define a GradientDrawable for Enabled state
   Dim gdwEnabled As GradientDrawable
   gdwEnabled.Initialize("TOP_BOTTOM", Array As Int(LightColor, Colors.White, DarkColor))
   gdwEnabled.CornerRadius = 15
   ' Define a GradientDrawable for Pressed state
   Dim gdwPressed As GradientDrawable
   gdwPressed.Initialize("TOP_BOTTOM",Array As Int(DarkColor, DarkColor, Colors.White, LightColor))
   ref.Target = gdwPressed
   ref.RunMethod4("setCornerRadii", Array As Object(Array As Float(10,15,10,15,10,15,10,15)), Array As String("[F"))
   ' Define a GradientDrawable for Disabled state
   Dim gdwDisabled As GradientDrawable
   gdwDisabled.Initialize("TOP_BOTTOM", Array As Int(Colors.LightGray, Colors.DarkGray))
   gdwDisabled.CornerRadius = 20
   ' Define a StateListDrawable
   Dim stdGradient As StateListDrawable
   stdGradient.Initialize
   stdGradient.AddState2(Array As Int(stdGradient.State_enabled, -stdGradient.State_Pressed), gdwEnabled)
   stdGradient.AddState(stdGradient.State_Pressed, gdwPressed)
   stdGradient.AddState(stdGradient.State_Disabled, gdwDisabled)
   Return stdGradient
End Sub

I also use the setCornerRadii part to make arrow buttons that are rounded/pointed on one side and flat on the other in areas where Forward/Back/Next buttons are needed. The only part I can't get to work nice is making the button and/or text shift like it is actually being pressed. Closest I got is doubling up on the pressed gradient top so it looks like it is pushed down. Actually being able to do more than 2 colors in a gradient was a nice find too.
 
Last edited:
Upvote 0

nfordbscndrd

Well-Known Member
Licensed User
Longtime User
I hope that this question is related closely enough that I don't get arrested for hijacking:

Once I have set a Label's background to a gradient in code, how do I change it back to just one color? I've searched and tried several things but have not been successful (other than using the same color code for both colors in the "gradient", but I assume there is a more direct method).
 
Upvote 0

nfordbscndrd

Well-Known Member
Licensed User
Longtime User
Did you try setting the .Color? That always did it for me. I was actually kind of disappointed that Android didn't allow setting both so I could have a color and a gradient background that used alpha blends over the color. Setting the color overrode the gradient and setting the gradient after color overrode it.

That did it. :sign0161: I was trying stuff like setting the Background to a color. I had set the base color in Designer and just wanted to get rid of the background. I even set Background to Null and the result was really weird.
 
Upvote 0
Top