Android Question What is best way to handle custom colors across many layouts?

Steve Miller

Active Member
Licensed User
Longtime User
I have a tabbed screen with 11 layouts. I want to give the user the ability to pick their own colors for background, label text, and tabs. This will allow them to match to their company colors.

What I did was write a program for the computer that updates a database with the selected colors of each item. The tablet has the same database on it and gets a copy of this updated color table. Now, I want to apply the customers colors to each item across all the screens. What is the most efficient method of doing this?
 

warwound

Expert
Licensed User
Longtime User
I think i'd make use of the View's Tag property.
Set the Tag property to a string which is the 'id' field of a custom color in your database table.

Now you can query the database for the value of the custom color that corresponds to the View's Tag property and set the View's color property.
Both Activity and Panel objects have a method:

GetAllViewsRecursive As IterableList

So once you have added all Views to your Activity you can then iterate all Views setting their color property if the View has a Tag property set and your table contains a corresponding custom color:

B4X:
Dim TagValue As String
For Each v As View In Activity.GetAllViewsRecursive
    If v.Tag<>Null AND v.Tag Is String Then
        TagValue=v.Tag
        If TagValue<>"" Then
            ' query the database for a custom color value whose 'id' equals TagValue and set the View's color property
        End if
    End If
Next

You can set the Tag property using the Designer or using code.
If a View is created using the Designer and you do not set a Tag property then the View's Tag property will be an empty string "".
If a View is created in code and you do not set a Tag property then the View's Tag property will be Null.

Create a code module containing subs (based on above code) to set the color of:
  • A single View.
  • All Views within an Activity.
  • All Views within a Panel.
Now you can call these subs from any activity when required - at the end of your Activity_Create sub for example or after adding or modifying a View in code.

Martin.
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
Last edited:
Upvote 0

Steve Miller

Active Member
Licensed User
Longtime User
Thanks for the reply, Martin. I'll give this a try today. Looks like a simple course of action.

Instead of using the tag property, I could do something like this:

B4X:
' query the database for custom color values and build array list of foreground and background colors

For Each v As View In Activity.GetAllViewsRecursive
    If v is Label Then
        'Apply foreground/background color for label
    else if v is Checkbox
        'Apply foreground/background color for checkbox
    'etc, for other view types....
    End If
Next

Appreciate the response!
 
Last edited:
Upvote 0

Steve Miller

Active Member
Licensed User
Longtime User
I think it is functional, but using the tag you could use different styles for different labels

Very true. I do need to use the Tags for labels, as I have a main caption at the top of each layout that would be different than the general labels on the rest of the layout. So, I'm suing a combination of both.

Thanks.
 
Upvote 0

Steve Miller

Active Member
Licensed User
Longtime User
Not working as expected.

Here's the scenario and what is happening...

I have a TabHost with 11 tabs set. Each tab has a layout. Each layout has some editboxes, labels, and checkboxes on them.

When I loop through the views in Activity.GetAllViewsRecursive, it only sets the colors for the first tab, The rest are not touched.
I notice one of the views is the TabHost, but I'm not sure how to loop through each layout in each tab and update each control on the layout.

Here's the code I've emplemented.

B4X:
    Dim pnl As Panel
    Dim chk As CheckBox
    Dim txt As EditText
    Dim rad As RadioButton
    Dim but As Button
    Dim lab As Label
    Dim th As TabHost

    If cColors.IsInitialized=False Then
        cColors.Initialize
    End If
   
    For Each v As View In Activity.GetAllViewsRecursive
        If v Is Panel Then
            pnl = v
            pnl.Color=cColors.GetBG("BACKGROUND")
        Else If v Is Label Then           
            lab = v
            If lab.Tag="CAPTION" Then
                lab.Color=cColors.GetBG("CAPTION")
                lab.TextColor=cColors.GetFG("CAPTION")
            Else
                lab.Color=cColors.GetBG("LABEL")
                lab.TextColor=cColors.GetFG("LABEL")
            End If
        Else If v Is CheckBox Then
            chk = v
            chk.Color=cColors.GetBG("CHECKBOX")
            chk.TextColor=cColors.GetFG("CHECKBOX")
        Else If v Is EditText Then
            txt = v
            txt.Color=cColors.GetBG("TEXTBOX")
            txt.TextColor=cColors.GetFG("TEXTBOX")
        Else If v Is RadioButton Then
            rad = v
            rad.Color=cColors.GetBG("RADIOBUTTON")
            rad.TextColor=cColors.GetFG("RADIOBUTTON")
        Else If v Is Button Then
            but = v
            but.Color=cColors.GetBG("BUTTON")
            but.TextColor=cColors.GetFG("BUTTON")
        End If
    Next

Any ideas?
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
Here's the code I've emplemented.

I would place the DIMs inside the loop. there could be more than one panel, edittext and so on...

B4X:
If cColors.IsInitialized=False Then
        cColors.Initialize
    End If
  
    For Each v As View In Activity.GetAllViewsRecursive
        If v Is Panel Then
            Dim pnl As Panel = v
            pnl.Color=cColors.GetBG("BACKGROUND")
        Else If v Is Label Then          
            Dim lab As Label = v
            If lab.Tag="CAPTION" Then
                lab.Color=cColors.GetBG("CAPTION")
                lab.TextColor=cColors.GetFG("CAPTION")
            Else
                lab.Color=cColors.GetBG("LABEL")
                lab.TextColor=cColors.GetFG("LABEL")
            End If
        Else If v Is CheckBox Then
            Dim chk As CheckBox = v
            chk.Color=cColors.GetBG("CHECKBOX")
            chk.TextColor=cColors.GetFG("CHECKBOX")
        Else If v Is EditText Then
            Dim txt As EditText = v
            txt.Color=cColors.GetBG("TEXTBOX")
            txt.TextColor=cColors.GetFG("TEXTBOX")
        Else If v Is RadioButton Then
            Dim rad As RadioButton = v
            rad.Color=cColors.GetBG("RADIOBUTTON")
            rad.TextColor=cColors.GetFG("RADIOBUTTON")
        Else If v Is Button Then
            dim but as button = v
            but.Color=cColors.GetBG("BUTTON")
            but.TextColor=cColors.GetFG("BUTTON")
        else if v is Tabhost then
          Dim th As TabHost = v
          ' Change tabhosts properties here...
        End If
    Next
 
Upvote 0

Steve Miller

Active Member
Licensed User
Longtime User
Manfred, I don't think that would really help. Dimming inside the loop isn't doing anything that I can determine, unless I'm just not seeing it. Dimming outside the loop just recycles the variable for the current view without having to instantiate it over and over. As for the v is Tabhost, I am not sure what to do inside that part of the code. I'm looking at the StateManager as Martin suggested.

Martin - Some parts of the innerSaveState subroutine are not clear to me. I see the following code:

B4X:
    Else If v Is TabHost Then
        Dim th As TabHost
        th = v
        data = Array As Object(th.CurrentTab)
        For i = 0 To th.TabCount - 1
            th.CurrentTab = i
        Next
        list1.Add(data)
        Dim data() As Object
        Dim r As Reflector
        r.Target = th
        Dim tabParentPanel As Panel
        tabParentPanel = r.RunMethod("getTabContentView")
        For i = 0 To tabParentPanel.NumberOfViews - 1
            innerSaveState(tabParentPanel.GetView(i), list1)
        Next

What I'm unclear about the purpose is the following loop, I don't see that it's doing anything. If there are 11 tabs, it loops through all 11 tabs but does what? Nothing that I can really see. If it set the current tab and then looped through all views on that tab, then I could understand, but this is just looping through the tabs doing nothing with them.

B4X:
For i = 0 To th.TabCount - 1
      th.CurrentTab = i
Next

I assume the following code gets the views on the current tab and loops through each view on the current tab? Shouldn't this be inside the loop mentioned above? These two pieces of code confuse me as I really can't determine their purpose.

B4X:
        Dim tabParentPanel As Panel
        tabParentPanel = r.RunMethod("getTabContentView")
        For i = 0 To tabParentPanel.NumberOfViews - 1
            innerSaveState(tabParentPanel.GetView(i), list1)
        Next
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
B4X:
Private Sub Test
  
    For Each v As View In Activity.GetAllViewsRecursive
        If v Is TabHost Then
            Dim th As TabHost
            th = v
            For i = 0 To th.TabCount - 1
                th.CurrentTab = i
                Dim r As Reflector
                r.Target = th
                Dim tabParentPanel As Panel
                tabParentPanel = r.RunMethod("getTabContentView")
                Log(i & " tabParentPanel NumberOfViews = " & tabParentPanel.NumberOfViews)
'                For Each vv In tabParentPanel
'                    Log(GetType(vv))
'                Next
            Next
        End If
    Next
  
End Sub

--- LOG ---
0 tabParentPanel NumberOfViews = 1
1 tabParentPanel NumberOfViews = 2
2 tabParentPanel NumberOfViews = 3
3 tabParentPanel NumberOfViews = 4
4 tabParentPanel NumberOfViews = 5
5 tabParentPanel NumberOfViews = 6
6 tabParentPanel NumberOfViews = 7
7 tabParentPanel NumberOfViews = 8
8 tabParentPanel NumberOfViews = 9
9 tabParentPanel NumberOfViews = 10
10 tabParentPanel NumberOfViews = 11

It seems that:
B4X:
 r.RunMethod("getTabContentView")
returns ALWAYS the main tabhost, which SEEMS contain a greater number of tabs when you select a tab with index higher!
 
Last edited:
Upvote 0

Steve Miller

Active Member
Licensed User
Longtime User
Ok, I implemented the below code and put some logging in the setting of the color for the label view. The looping takes about 1 minute to go through a tabhost with 12 tabs on it, updating the foreground and background colors of the views in each tab.

What I am seeing is a repeat pattern as follows:

Tab #0 View #0
Tab #1 View #0
Tab #1 View #1
Tab #2 View #0
Tab #2 View #1
Tab #2 View #2
Tab #3 View #0
Tab #3 View #1
Tab #3 View #2
Tab #3 View #3

Here is the output in the log file for just 3 tabs (it repeats this pattern for all 12 tabs):
B4X:
Tab #0 View #0
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Tab #1 View #0
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Tab #1 View #1
Text=Text on tab 1
Text=More text on tab 1
Text=Text on tab 1
Text=More text on tab 1
Tab #2 View #0
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Tab #2 View #1
Text=Text on tab 1
Text=More text on tab 1
Text=Text on tab 1
Text=More text on tab 1
Tab #2 View #2
Text=Text on tab 2
Text=More text on tab 2
Text=Text on tab 2
Text=More text on tab 2
Tab #3 View #0
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Text=Welcome screen text here.
Text=Copyright ©2014 xxxx
Tab #3 View #1
Text=Text on tab 1
Text=More text on tab 1
Text=Text on tab 1
Text=More text on tab 1
Tab #3 View #2
Text=Text on tab 2
Text=More text on tab 2
Text=Text on tab 2
Text=More text on tab 2
Tab #3 View #3
Text=Text on tab 3
Text=More text on tab 3
Text=Text on tab 3
Text=More text on tab 3

Here is the code. Can anyone tell me what I'm doing wrong and how to correct this?

B4X:
Sub SetCustomColors
    Dim i As Int
    If cColors.IsInitialized=False Then
        cColors.Initialize
    End If
   
    For i = 0 To TabHost1.TabCount - 1
        TabHost1.CurrentTab=i
        Dim r As Reflector
        r.Target = TabHost1
        Dim tabParentPanel As Panel
        tabParentPanel = r.RunMethod("getTabContentView")
        For x = 0 To tabParentPanel.NumberOfViews - 1
            Log("Tab #" & i & " View #" & x)
            SetViewColors(tabParentPanel.GetView(x))
        Next       
    Next

End Sub
Sub SetViewColors(v As View)

    Select Case True
        Case v Is CheckBox
            Dim chk As CheckBox
            chk = v
            chk.Color=cColors.GetBG("CHECKBOX")
            chk.TextColor=cColors.GetFG("CHECKBOX")
        Case v Is EditText
            Dim txt As EditText
            txt = v
            txt.Color=cColors.GetBG("TEXTBOX")
            txt.TextColor=cColors.GetFG("TEXTBOX")
        Case v Is RadioButton
            Dim rad As RadioButton
            rad = v
            rad.Color=cColors.GetBG("RADIOBUTTON")
            rad.TextColor=cColors.GetFG("RADIOBUTTON")
        Case v Is Button
            Dim but As Button
            but = v
            but.Color=cColors.GetBG("BUTTON")
            but.TextColor=cColors.GetFG("BUTTON")
        Case v Is Spinner
            Dim spi As Spinner
            spi = v
            spi.Color=cColors.GetBG("LISTBOX")
            spi.TextColor=cColors.GetFG("LISTBOX")
        Case v Is Panel
            Dim pnl As Panel
            pnl = v
            pnl.Color=cColors.GetBG("BACKGROUND")
            For Each v1 As View In pnl.GetAllViewsRecursive
                SetViewColors(v1)
            Next
        Case v Is Label           
            Dim lab As Label
            lab = v
            Log("Text=" & lab.Text)
            If lab.Tag="CAPTION" Then
                lab.Color=cColors.GetBG("CAPTION")
                lab.TextColor=cColors.GetFG("CAPTION")
            Else
                lab.Color=cColors.GetBG("LABEL")
                lab.TextColor=cColors.GetFG("LABEL")
            End If
    End Select
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
I do not see errors.

But I see that Target is always TabHost1, then getTabContentView acts on the TabHost, not on individual "internal" Tabs.

At each cycle, setting the CurrentTab and then using getTabContentView, seems it considers a new TabHost container which lists all the views in all of the tabs.

In short, it may not work, even if it don't contains errors.

Missing a CurrentTab method that returns the internal tab.
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
I found this code in the thread "How do they do..." by Informatix:

B4X:
Dim cdEnabled, cdSelected As ColorDrawable
cdEnabled.Initialize(Colors.Black, 0)
cdSelected.Initialize(Colors.Gray, 0)
Dim r As Reflector
r.Target = TabHost1
r.Target = r.RunMethod("getTabWidget")
For t = 0 To TabHost1.TabCount - 1
  Dim TabPanel As Panel
  TabPanel = r.RunMethod2("getChildAt", t, "java.lang.int")
  Dim sd As StateListDrawable
  sd.Initialize
  sd.AddState(sd.State_Selected, cdSelected)
  sd.AddState(sd.State_Pressed, LoadDrawable("highlight_pressed"))
  sd.AddState(sd.State_Enabled, cdEnabled)
  TabPanel.Background = sd
Next

Since he is very accurate, we try to replace.

We must, however, read the whole thread, because it uses EVEN the TabHostExtras library.
 
Upvote 0

LucaMs

Expert
Licensed User
Longtime User
Yes, it works (Informatix is great :)).

B4X:
Private Sub Test

    Dim r As Reflector
    For Each v As View In Activity.GetAllViewsRecursive
        If v Is TabHost Then
            r.Target = thMain
            r.Target = r.RunMethod("getTabWidget")
            For t = 0 To thMain.TabCount - 1
              Dim TabPanel As Panel
              TabPanel = r.RunMethod2("getChildAt", t, "java.lang.int")
                Log(t & " TabPanel NumberOfViews = " & TabPanel.NumberOfViews)
                For Each vv In TabPanel
                    Log(GetType(vv))
                Next
            Next
        End If
    Next
   
End Sub
 
Upvote 0

Steve Miller

Active Member
Licensed User
Longtime User
I can't seem to get it to work for what I need. I'm a .Net developer, not a java developer. So, some of this stuff is foreign to me. I'll have to play with it and hope I stumble on something that works.
 
Upvote 0
Top