B4A Library TabHostExtras

Here is a new library i have created that allows you to customise a TabHost.

TabHostExtras
Version: 2.2
  • TabHostExtras
    Methods:
    • GetTagWidget (TabHost1 As TabHost) As TabWidget
      Returns TabHost1's TabWidget.
      Allowing you to customize various settings.
    • getTabContentViewPadding (tabHost1 As TabHost) As RectWrapper
      Get the layout padding of tabHost1 TabContentView
      Returns a Rect object containing pixel values
    • getTabEnabled (tabHost1 As TabHost, index As Int) As Boolean
      Get the Enabled state of TabIndicator #index in tabHost1
    • getTabHeight (tabHost1 As TabHost) As Int
      Get the height (in pixels) of the TabIndicators in tabHost1
    • getTabHostPadding (tabHost1 As TabHost) As RectWrapper
      Get the layout padding of tabHost1 container View
      Returns a Rect object containing pixel values
    • getTabIcon (tabHost1 As TabHost, TabIndex As Int) As ImageViewWrapper
      Get the (icon) ImageView of TabIndicator #TabIndex in tabHost1
    • getTabTextSize (tabHost1 As TabHost) As Float
      Get the text size (in pixels) of all TabIndicators
    • getTabVisibility (tabHost1 As TabHost, index As Int) As Boolean
      Get the visibility of TabIndicators #index in tabHost1
    • setTabContentViewPadding (tabHost1 As TabHost, left As Int, top As Int, right As Int, bottom As Int)
      Set the layout padding (in dip) of tabHost1 TabContentView
    • setTabEnabled (tabHost1 As TabHost, enabled As Boolean)
      Enable or disable all TabIndicators in tabHost1
    • setTabEnabled2 (tabHost1 As TabHost, enabled As Boolean, index As Int)
      Enable or disable TabIndicator #index in tabHost1
    • setTabGradientDrawable (tabHost1 As TabHost, orientation As String, color1 As Int, color2 As Int, cornerRadius As Float)
      Set a GradientDrawable as the background on all TabIndicators in tabHost1
      All fours corner radii of the GradientDrawable are set to the value of cornerRadius (in pixels)
    • setTabGradientDrawable2 (tabHost1 As TabHost, orientation As String, color1 As Int, color2 As Int, cornerRadius() As Float)
      Set a GradientDrawable as the background on all TabIndicators in tabHost1
      Corner radii of the GradientDrawable are set individually (in pixels) based upon the number of elements in the array cornerRadius:
      1 element defines all corner radii
      2 elements define corner radii in order top left and right, bottom left and right
      4 elements define corner radii in order top-left, top-right, bottom-right, bottom-left
    • setTabHeight (tabHost1 As TabHost, tabHeight As Int)
      Set the height (in pixels) of all TabIndicators in tabHost1
    • setTabHostPadding (tabHost1 As TabHost, left As Int, top As Int, right As Int, bottom As Int)
      Set the layout padding (in dip) of tabHost1 container View
    • setTabTextColor (tabHost1 As TabHost, Color As Int)
      Set the color to be used for all tab indicators text.
      This color will be used for all tab indicators regardless of their selected state.
    • setTabTextColorStateList (tabHost1 As TabHost, ColorStateListName As String)
      Set a ColorStateList to be used for the text color of all tab indicators.
      The ColorStateList must be defined in XML in your application Objects/res/drawable folder.
      Color for selected and not selected tab state can be defined.
    • setTabTextSize (tabHost1 As TabHost, TextSize As Float)
      Set the text size of all TabIndicators
      TextSize is assumed to be in units of dip.
    • setTabTitle (tabHost1 As TabHost, Title As String, TabIndex As Int)
      Set the Title text of TabIndicator #TabIndex in tabHost1
    • setTabVisibility (tabHost1 As TabHost, visible As Boolean)
      Set the visibility of all TabIndicators in tabHost1
    • setTabVisibility2 (tabHost1 As TabHost, visible As Boolean, index As Int)
      Set the visibility of TabIndicator #index in tabHost1
  • TabWidget
    Methods:
    • BringToFront
    • GetChildTabViewAt (TabIndex As Int) As View
      Returns the tab indicator view at the given index.
      The returned View will be a ViewGroup with 2 child Views:
      An ImageView at index 0 and a TextView at Index 1.
    • GetTabIcon (TabIndex As Int) As ImageViewWrapper
      Get the (icon) ImageView of the tab indicator view at the given index.
    • GetTabLabel (TabIndex As Int) As LabelWrapper
      Get the (TextView) Label of the tab indicator view at the given index.
    • Initialize (arg1 As String)
    • Invalidate
    • Invalidate2 (arg0 As Rect)
    • Invalidate3 (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
    • IsInitialized As Boolean
    • RemoveView
    • RequestFocus As Boolean
    • SendToBack
    • SetBackgroundImage (arg0 As Bitmap)
    • SetLayout (arg0 As Int, arg1 As Int, arg2 As Int, arg3 As Int)
    Properties:
    • Background As Drawable
    • Color As Int [write only]
    • DividerDrawable As Drawable [write only]
      Sets the drawable to use as a divider between the tab indicators.
    • Enabled As Boolean
      Get or Set the enabled state of the TabWidget.
    • Height As Int
    • Left As Int
    • LeftStripDrawable As Drawable [write only]
      Sets the drawable to use as the left part of the strip below the tab indicators.
    • RightStripDrawable As Drawable [write only]
      Sets the drawable to use as the right part of the strip below the tab indicators.
    • StripEnabled As Boolean
      Get or Set whether the bottom strips on the tab indicators are drawn or not.
    • TabCount As Int [read only]
      Get the number of tab indicator views.
    • Tag As Object
    • Top As Int
    • Visible As Boolean
    • Width As Int

Attached are the library files and a little demo.
If you run the demo try your device's 'Menu' key and toggle the tabs from visible to hidden.

I originally created this functionality in a code module and once it was working created the library.
So i have attached the code module as a separate attachment in case anyone wants to use it - the Reflection library is required if you use the code module but is not required if you use the library of course.

The code module has a Sub (not included in the library):

GetTabIndicators(TabHost1 As TabHost) As View()

This will return an array of Views, these Views are the TabHost's TabIndicators.
Anyone hoping to further customise the look of a TabHost may find this Sub useful - see the code module demo for an example of changing the TabIndicators' Color property.

Martin.
 

Attachments

  • TabHostExtrasCodeModule.zip
    8.9 KB · Views: 2,232
  • TabHostExtras_v2_20.zip
    22 KB · Views: 3,083
Last edited:

RjCl

Member
Licensed User
Longtime User
The TabWidget is a View that contains TabIndicators, your code has set the Color property of the TabWidget whereas you want to set the Color property of each TabIndicator?

I think you can do that with the method:

setTabTextColorStateList (tabHost1 As TabHost, ColorStateListName As String)
Set a ColorStateList to be used for the text color of all tab indicators.
The ColorStateList must be defined in XML in your application Objects/res/drawable folder.
Color for selected and not selected tab state can be defined.

The ColorStateList defines the color of each TabIndicator depending on whether it's selected or not selected.
There's some more info here: http://www.b4x.com/android/forum/threads/tabhostextras.11056/page-3#post-109232.

Martin.

No, sorry, not the text of whats written on the tabs, i managed to do that from your previous example a few pages back and is working great.
I'm wanting to change the actual color of the tab not the text on the tab ?
 

warwound

Expert
Licensed User
Longtime User
Ah yes that'll only modify the tab indicator text color not background color.

I've modified the TabHost tutorial example:

B4X:
Sub Activity_Create(FirstTime As Boolean)
	Activity.LoadLayout("main")
	Dim bmp1, bmp2 As Bitmap
	bmp1 = LoadBitmap(File.DirAssets, "ic.png")
	bmp2 = LoadBitmap(File.DirAssets, "ic_selected.png")
	
	TabHost1.AddTabWithIcon ("Name", bmp1, bmp2, "page1") 'load the layout file of each page
	TabHost1.AddTab("Color", "page2") 
	TabHost1.AddTab("Animal", "page3")
	
	Dim ColorDrawable1 As ColorDrawable
	ColorDrawable1.Initialize(Colors.Blue, 0)
	
	Dim ColorDrawable2 As ColorDrawable
	ColorDrawable2.Initialize(Colors.Red, 0)
	
	Dim StateListDrawable1 As StateListDrawable
	StateListDrawable1.Initialize
	StateListDrawable1.AddState(StateListDrawable1.State_Selected, ColorDrawable1)
	StateListDrawable1.AddCatchAllState(ColorDrawable2)
	
	Dim TabHostExtras1 As TabHostExtras
	Dim TabIndicator As Panel=TabHostExtras1.GetTagWidget(TabHost1).GetChildTabViewAt(0)
	TabIndicator.Background=StateListDrawable1
End Sub

The TabWidget GetChildTabViewAt method returns an android ViewGroup type of object which can be assigned to a b4a Panel.
Now we can set the Panel Background property to a Drawable.
I've used a StateListDrawable which will appear red when selected and blue at all other times.

The example works BUT i see a problem on my Moto G (kitkat).
When the Panel Background is initially set it overflows the area which it originally occupied.
Click any other tab and the TabHost redraws itself and the Panel background appears as expected.

So try this.
Create a Drawable for the 'selected' state and a Drawable for the 'not selected' state then create a StateListDrawable from these two Drawables.
Now iterate through the tab indicators, setting their Background property to the StateListDrawable.

Martin.
 

Attachments

  • tabindicator-background.zip
    11.1 KB · Views: 302

RjCl

Member
Licensed User
Longtime User
Ah yes that'll only modify the tab indicator text color not background color.

I've modified the TabHost tutorial example:

B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("main")
    Dim bmp1, bmp2 As Bitmap
    bmp1 = LoadBitmap(File.DirAssets, "ic.png")
    bmp2 = LoadBitmap(File.DirAssets, "ic_selected.png")
   
    TabHost1.AddTabWithIcon ("Name", bmp1, bmp2, "page1") 'load the layout file of each page
    TabHost1.AddTab("Color", "page2")
    TabHost1.AddTab("Animal", "page3")
   
    Dim ColorDrawable1 As ColorDrawable
    ColorDrawable1.Initialize(Colors.Blue, 0)
   
    Dim ColorDrawable2 As ColorDrawable
    ColorDrawable2.Initialize(Colors.Red, 0)
   
    Dim StateListDrawable1 As StateListDrawable
    StateListDrawable1.Initialize
    StateListDrawable1.AddState(StateListDrawable1.State_Selected, ColorDrawable1)
    StateListDrawable1.AddCatchAllState(ColorDrawable2)
   
    Dim TabHostExtras1 As TabHostExtras
    Dim TabIndicator As Panel=TabHostExtras1.GetTagWidget(TabHost1).GetChildTabViewAt(0)
    TabIndicator.Background=StateListDrawable1
End Sub

The TabWidget GetChildTabViewAt method returns an android ViewGroup type of object which can be assigned to a b4a Panel.
Now we can set the Panel Background property to a Drawable.
I've used a StateListDrawable which will appear red when selected and blue at all other times.

The example works BUT i see a problem on my Moto G (kitkat).
When the Panel Background is initially set it overflows the area which it originally occupied.
Click any other tab and the TabHost redraws itself and the Panel background appears as expected.

So try this.
Create a Drawable for the 'selected' state and a Drawable for the 'not selected' state then create a StateListDrawable from these two Drawables.
Now iterate through the tab indicators, setting their Background property to the StateListDrawable.

Martin.

Thank you so much! It working alright so far :)

Just one other thing, if you could help me..........How to choose another color by say hex value ? Is it ColorDrawable1.Initialize(#2F4F4F, 0) ?
 

warwound

Expert
Licensed User
Longtime User
No, it didn't work....Logging DecimalInteger was 3100495 ?

That may well be a valid integer for a color value - try setting it and see what you get.
Remember that color hex values range from 0 to FFFFFFFF so a large integer value isn't necessarily incorrect.

Martin.
 

RjCl

Member
Licensed User
Longtime User
That may well be a valid integer for a color value - try setting it and see what you get.
Remember that color hex values range from 0 to FFFFFFFF so a large integer value isn't necessarily incorrect.

Martin.

It comes up blank, not showing the color its supposed to :confused:
 

warwound

Expert
Licensed User
Longtime User
http://stackoverflow.com/questions/6935057/convert-hex-color-value-ffffff-to-integer-value

Simply parsing an HTML hex color code to an integer won't work for android's color property.
This is a long winded approach but does work:

B4X:
Sub Activity_Create(FirstTime As Boolean)
	Activity.LoadLayout("main")
	Dim bmp1, bmp2 As Bitmap
	bmp1 = LoadBitmap(File.DirAssets, "ic.png")
	bmp2 = LoadBitmap(File.DirAssets, "ic_selected.png")
	
	TabHost1.AddTabWithIcon ("Name", bmp1, bmp2, "page1") 'load the layout file of each page
	TabHost1.AddTab("Color", "page2") 
	TabHost1.AddTab("Animal", "page3")
	
	Dim ColorDrawable1 As ColorDrawable
	'	parse the HTML hex color code as red, green and blue elements
	ColorDrawable1.Initialize(Colors.RGB(Bit.ParseInt("2f", 16), Bit.ParseInt("4f", 16), Bit.ParseInt("4f", 16)), 0)
	
	Dim ColorDrawable2 As ColorDrawable
	ColorDrawable2.Initialize(Colors.Magenta, 0)
	
	Dim StateListDrawable1 As StateListDrawable
	StateListDrawable1.Initialize
	StateListDrawable1.AddState(StateListDrawable1.State_Selected, ColorDrawable1)
	StateListDrawable1.AddCatchAllState(ColorDrawable2)
	
	Dim TabHostExtras1 As TabHostExtras
	Dim TabIndicator As Panel=TabHostExtras1.GetTagWidget(TabHost1).GetChildTabViewAt(0)
	TabIndicator.Background=StateListDrawable1
End Sub

Martin.
 

RjCl

Member
Licensed User
Longtime User
http://stackoverflow.com/questions/6935057/convert-hex-color-value-ffffff-to-integer-value

Simply parsing an HTML hex color code to an integer won't work for android's color property.
This is a long winded approach but does work:

B4X:
Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("main")
    Dim bmp1, bmp2 As Bitmap
    bmp1 = LoadBitmap(File.DirAssets, "ic.png")
    bmp2 = LoadBitmap(File.DirAssets, "ic_selected.png")
   
    TabHost1.AddTabWithIcon ("Name", bmp1, bmp2, "page1") 'load the layout file of each page
    TabHost1.AddTab("Color", "page2")
    TabHost1.AddTab("Animal", "page3")
   
    Dim ColorDrawable1 As ColorDrawable
    '    parse the HTML hex color code as red, green and blue elements
    ColorDrawable1.Initialize(Colors.RGB(Bit.ParseInt("2f", 16), Bit.ParseInt("4f", 16), Bit.ParseInt("4f", 16)), 0)
   
    Dim ColorDrawable2 As ColorDrawable
    ColorDrawable2.Initialize(Colors.Magenta, 0)
   
    Dim StateListDrawable1 As StateListDrawable
    StateListDrawable1.Initialize
    StateListDrawable1.AddState(StateListDrawable1.State_Selected, ColorDrawable1)
    StateListDrawable1.AddCatchAllState(ColorDrawable2)
   
    Dim TabHostExtras1 As TabHostExtras
    Dim TabIndicator As Panel=TabHostExtras1.GetTagWidget(TabHost1).GetChildTabViewAt(0)
    TabIndicator.Background=StateListDrawable1
End Sub

Martin.

Thank you :)
 

RjCl

Member
Licensed User
Longtime User
Hello Martin,

Is there a setting for the tab text position ? Currently letters like g, y are getting cut off from the bottom.....There seems to be more space above the text than below.

Also, I can't get the dividers showing back between the tabs like in your demo.
 

warwound

Expert
Licensed User
Longtime User
May I ask how I can just remove the outer padding of the tabhost, using reflection?

Here's the source code for the setTabHostPadding method:

B4X:
/**
 * Set the layout padding (in dip) of tabHost1 container View
 */
public static void setTabHostPadding(TabHost tabHost1, int left, int top, int right, int bottom){
	tabHost1.getChildAt(0).setPadding(Common.DipToCurrent(left), Common.DipToCurrent(top), Common.DipToCurrent(right), Common.DipToCurrent(bottom));
}

Martin.
 

RjCl

Member
Licensed User
Longtime User
Hello Martin,

Is there a setting for the tab text position ? Currently letters like g, y are getting cut off from the bottom.....There seems to be more space above the text than below.

Also, I can't get the dividers showing back between the tabs like in your demo.

Martin,

Any ideas about why the text is positioned as it is?
 

warwound

Expert
Licensed User
Longtime User
Martin,

Any ideas about why the text is positioned as it is?

Not really :(

If i had this problem i'd probably start my app, then open the folder containing the android sdk and run the Hierarchy Viewer.
The Hierarchy Viewer shows a tree based view and you can navigate to whichever view you want to inspect.

I'd navigate to the TextView (Label) that is a tab indicator and look at it's properties.
See if a padding or margin property has been set and this is not leaving enough space for the text to properly display.
Look at other properties too - once you find the cause i can look at some code to fix the problem.

Martin.
 

RjCl

Member
Licensed User
Longtime User
Not really :(

If i had this problem i'd probably start my app, then open the folder containing the android sdk and run the Hierarchy Viewer.
The Hierarchy Viewer shows a tree based view and you can navigate to whichever view you want to inspect.

I'd navigate to the TextView (Label) that is a tab indicator and look at it's properties.
See if a padding or margin property has been set and this is not leaving enough space for the text to properly display.
Look at other properties too - once you find the cause i can look at some code to fix the problem.

Martin.

How do you mean start app? On the device while running B4A ?
 

warwound

Expert
Licensed User
Longtime User
Run your b4a project so your device is running your project and is connected to your computer via USB.
Now you should be able to run the Hierarchy Viewer, select your device and select your running project by identifying it's packagename in the list of running packages.
Finally you get a graphical tree showing all Views currently displayed in your app and should be able to navigate to the TextView which is the tab indicator displaying your squashed text.

Martin.
 

RjCl

Member
Licensed User
Longtime User
I tried as you said, connected USB, its in debug mode, run in debug and started app and then hierarchy viewer but i get the following errors:-

B4X:
05:26:46 E/hierarchyviewer: Missing forwarded port for 42583930325834465A58
05:26:46 E/hierarchyviewer: Unable to load window data for window <Focused Windo
w> on device 42583930325834465A58
05:26:46 E/hierarchyviewer: null

Hierarchy Viewer is showing device but nothing else when clicking, just get that error in command prompt :confused:
 

Controller

Member
Licensed User
Longtime User
B4X:
Sub TabHost_SetTabPadding(xTabHost As TabHost, Left As Int, Top As Int, Right As Int, Bottom As Int)
    Dim xRef1 As Reflector
    Dim xRef2 As Reflector
    Dim xInt1 As Int
    Dim xInt2 As Int
   
    xRef1.Target = xTabHost
    xRef1.Target = xRef1.RunMethod("getTabWidget")
    xInt2 = xRef1.RunMethod("getChildCount")
    For xInt1 = 0 To xInt2 - 1
        xRef2.Target = xRef1.RunMethod2("getChildTabViewAt", xInt1, "java.lang.int")
      xRef2.RunMethod4("setPadding", Array As Object(Left, Top, Right, Bottom), Array As String("java.lang.int", "java.lang.int", "java.lang.int", "java.lang.int"))
        Next
  End Sub
Might help adjusting. Just call after adding your tabs.
 

RjCl

Member
Licensed User
Longtime User
B4X:
Sub TabHost_SetTabPadding(xTabHost As TabHost, Left As Int, Top As Int, Right As Int, Bottom As Int)
    Dim xRef1 As Reflector
    Dim xRef2 As Reflector
    Dim xInt1 As Int
    Dim xInt2 As Int
  
    xRef1.Target = xTabHost
    xRef1.Target = xRef1.RunMethod("getTabWidget")
    xInt2 = xRef1.RunMethod("getChildCount")
    For xInt1 = 0 To xInt2 - 1
        xRef2.Target = xRef1.RunMethod2("getChildTabViewAt", xInt1, "java.lang.int")
      xRef2.RunMethod4("setPadding", Array As Object(Left, Top, Right, Bottom), Array As String("java.lang.int", "java.lang.int", "java.lang.int", "java.lang.int"))
        Next
  End Sub
Might help adjusting. Just call after adding your tabs.

Wow, thanks so much ! It worked ! :)
 
Top