Android Tutorial How they do... #1

How do they... ? #1

(French version/Version française)

This tutorial is the first in a series that will show you how to reproduce some user interfaces (UI) with the classes and libraries available with B4A. The main goal is to show you how to best use the tools at your disposal. I do not claim to teach you how to design user interfaces and I will spare you any theory on the subject. I am not a professional designer. There are probably other methods as good as the ones here.

I chose the interfaces to be reproduced for various reasons. Their common point is to be of professional quality. I voluntarily discarded the interfaces of games, which are a special case, and I shall not discuss the quality, efficiency or visual appeal of these interfaces.

Note that I have not done reverse engineering; I thus ignore the exact method used by the authors of these UI, and I have reproduced the graphics from screenshots (thanks to the inventor of copy/paste). For copyright reasons, there will be nothing to download in this article.

We'll start with Microsoft's Hotmail, and more specifically, its message list. This is a sober and classic interface:
attachment.php


We see that the interface is composed of a list that is not a Listview (Listview does not allow us to add a Checkbox and is limited to two Labels) and, above this list, there is a tab bar. We are therefore faced with a TabHost hosting a ScrollView. Just below the tab bar, there is a header indicating what messaging folder is currently displayed. It is a label that is not part of the ScrollView. By cons, in other pages, it is embedded. I will show this variant at the end. For now, I open the B4A designer and I create my TabHost. Next, I create a panel consisting of a Label and a ScrollView:
attachment.php


In the script editor, I type the code placing and sizing views. The TabHost fills the screen. The Label and the ScrollView share the Panel. The Label has a fixed height.
B4X:
'All variants script
TabHost1.Height = 100%y
TabHost1.Width = 100%x

'Panel1 Views
Label1.Top = 0
Label1.Left = 0
Label1.Width = 100%x
Label1.Height = 30dip
ScrollView1.Top = Label1.Height
ScrollView1.Height = 100%y - ScrollView1.Top
ScrollView1.Width = 100%x

I save this layout with the name TabMsg.bal.

In the sub Activity_Create, I load the layout to the Activity and I add the Panel to the TabHost:
B4X:
Sub Globals
   Dim TabHost1 As TabHost
   Dim Panel1 As Panel
   Dim Label1 As Label
   Dim ScrollView1 As ScrollView
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("TabMsg.bal")

   Dim bmpDefault, bmpSelected As Bitmap
   bmpDefault = LoadBitmap(File.DirAssets, "envel_orange.png")
   bmpSelected = bmpDefault
   Panel1.RemoveView
   TabHost1.AddTabWithIcon2("Hotmail", bmpDefault,  bmpSelected, Panel1)
End Sub
As I created the panel in the designer, it is attached to the activity at start. I have to detach it from its parent to be able to add it to the TabHost, hence the RemoveView. Once in the TabHost, the Panel is automatically placed and sized.
To add an icon in the tab, I use the function AddTabWithIcon2. As the icon is the same regardless of the state of the tab, I load it only once in memory (bmpSelected points to bmpDefault).

Now I add the other tabs. As I have not created content for these tabs, I create a white colored panel to fill them: DummyPanel. In this example, I do not care about icons.
B4X:
Dim DummyPanel As Panel
DummyPanel.Initialize("")
DummyPanel.Color = Colors.White
TabHost1.AddTab2("Home", DummyPanel)
TabHost1.AddTab2("All emails", DummyPanel)

Dim bmpDefault, bmpSelected As Bitmap
bmpDefault = LoadBitmap(File.DirAssets, "envel_orange.png")
bmpSelected = bmpDefault
Panel1.RemoveView
TabHost1.AddTabWithIcon2("Hotmail", bmpDefault,  bmpSelected, Panel1)

TabHost1.AddTab2("School", DummyPanel)
TabHost1.AddTab2("Search", DummyPanel)
I set the text in bold in the Label and I color the background with a bluish gray.
B4X:
Label1.Text = " Hotmail - Inbox"
Label1.Typeface = Typeface.DEFAULT_BOLD
Label1.Color = Colors.RGB(124, 133, 148) 'Bluish gray
Let's see the result. We realize that there is an unwanted margin around the Panel and around the TabHost. My tab bar is too tall and the tab colors do not match the model.
attachment.php


I use the library TabHostExtras to change all that. I select it in B4A Libs and I add it in Globals:
B4X:
Dim THExtras As TabHostExtras
Then in Activity_Create:
B4X:
THExtras.setTabHeight(TabHost1, 62dip)
THExtras.setTabHostPadding(TabHost1, 0, 0, 0, 0)
THExtras.setTabContentViewPadding(TabHost1, 0, 0, 0, 0)
THExtras.setTabTextColorStateList(TabHost1, "tab_widget_text_colors")
The SetTabTextColorStateList function refers to an XML file that I placed in the folder Objects/res/drawable of my project. I protected this file against writing to prevent B4A from deleting it. Here's the content:
B4X:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
   <item android:state_selected="true"
      android:color="#FFFFFF" />
   <item android:state_selected="false"
      android:color="#606060" />
</selector>
I differentiate colors (android:color) according to the selected state or not (android:state_selected) of the tab.

It remains to color the tab background. Here, things get complicated because TabHostExtras v2.0 does not have the needed function. So I have to either write a library to fill the gap, or use the Android API with the Reflection library. I choose the second option. I add in Activity_Create:
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
In this code, I call the function LoadDrawable. It is used to get a system drawable by name. The Android SDK contains a copy of all drawables in the folder C:\Android\android-sdk\platforms\android-n\data\res\. They may differ slightly from those installed on your device.
B4X:
Sub LoadDrawable(Name As String) As Object
   Dim r As Reflector
   r.Target = r.GetContext
   r.Target = r.RunMethod("getResources")
   r.Target = r.RunMethod("getSystem")
   Dim ID_Drawable As Int
   ID_Drawable = r.RunMethod4("getIdentifier", Array As Object(Name, "drawable", "android"), _
                                               Array As String("java.lang.String", "java.lang.String", "java.lang.String"))
   r.Target = r.GetContext
   r.Target = r.RunMethod("getResources")
   Return r.RunMethod2("getDrawable", ID_Drawable, "java.lang.int")
End Sub
Let's add the small shaded bar beneath the tabs:
B4X:
Dim ivShadow As ImageView
ivShadow.Initialize("")
ivShadow.Background = LoadDrawable("code_lock_bottom")
Panel1.AddView(ivShadow, 0, 0, 100%x, 5dip)
The result is now very close to the model:
attachment.php


It's time to take care of the ScrollView. To create the list, I'm going to use the class CheckList. I copy its bas file in the project folder and I declare it in Globals:
B4X:
Dim MsgList As ClsCheckList
Then I initialize it in Activity_Create:
B4X:
MsgList.Initialize(Me, ScrollView1, "", "Msg_Click", "", 2dip)
Each item in the list consists of a checkbox, a small optional picture (paper clip) and three labels (sender, subject, date sent). I open the designer and I create the mask item.bal:
attachment.php

Mask script:
B4X:
'All variants script
Label1.Width = 100%x - 85dip
Label2.Width = 100%x - 130dip
Label3.Left = 100%x - Label3.Width - 5dip
ImageView1.Left = 100%x - ImageView1.Width – 5dip
I create a sub that will fill the mask:
B4X:
Sub FillItem(MsgSender As String, MsgSubject As String, MsgDate As String, MsgRead As Boolean, MsgAttached As Boolean) As Panel
   Dim pnl As Panel
   pnl.Initialize("")
   pnl.LoadLayout("item.bal")

   If MsgRead Then
      pnl.Color = Colors.Black
   Else
      pnl.Color = Colors.DarkGray 'Unread messages have a gray background
   End If

   Dim lblSender, lblSubject, lblDate As Label
   lblSender = pnl.GetView(1)
   lblSender.Text = MsgSender

   lblSubject = pnl.GetView(2)
   lblSubject.Text = MsgSubject

   lblDate = pnl.GetView(3)
   lblDate.Text = MsgDate

   Dim ivAttached As ImageView
   ivAttached = pnl.GetView(4)
   ivAttached.Visible = MsgAttached

   Return pnl
End Sub
To check the final result, I fill my list with some content:
B4X:
Dim pnl As Panel
pnl = FillItem("John Smith", "Hello Fred", "22:26", True, False)
MsgList.AddCustomItem("ID#100", pnl, 60dip)
pnl = FillItem("Lisa Gray", "I'm still loving you", "22:26", False, False)
MsgList.AddCustomItem("ID#101", pnl, 60dip)
pnl = FillItem("Darth Vador", "I'm your father", "09/08/2011", False, False)
MsgList.AddCustomItem("ID#102", pnl, 60dip)
pnl = FillItem("Pamela Rose", "FW: Class ChkList", "09/08/2011", True, True)
MsgList.AddCustomItem("ID#103", pnl, 60dip)

MsgList.ResizePanel
Et voilà:
attachment.php


Variant:

In the page "All E
mails", the header label is not static. It is part of the ScrollView and scrolls with him. To do this, remove the Label from the Panel in the designer and give the space freed to the ScrollView. Then, before filling the list, create a Panel, put the Label into it and add it to the list with AddCustomItem (height=30dip).

How do they... ? #2 >>>

About the author: Frédéric Leneuf-Magaud. I'm a professional developper since the early 90's and I have designed or contributed to hundreds of applications. I currently work for the French administration in a supervisory team of servers. Android development is one of my hobbies.

PS: Don't hesitate to help me to improve my english.
 
Last edited:

Penko

Active Member
Licensed User
How they do... #1


PS: Don't hesitate to help me to improve my english.

Please fix "Darth Vader", he is the ex-chosen one(he is no more Anakin).

Just kidding, very informative tutorial and it will be nice for beginners to get some understanding on how things are done.

Looking forward to seeing what else you've prepared.
 

barx

Well-Known Member
Licensed User
Brilliant work.
 

moster67

Expert
Licensed User
:sign0098:

Look forward to reading other tutorials.
 

Jost aus Soest

Active Member
Licensed User
+1

Great tutorial - a must read (not only) for beginners!

A little suggestion:
It would be even better to use talking variables names (e. g. "lblSender" instead of "Label1" etc.).
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Good tutorial. A few comments:

1. "Listview does not allow us to add a checkbox or the small paper clip indicating an attachment" This line should probably read "Listview does not allow us to add a checkbox and the small paper clip indicating an attachment". A listview item that is two lines with an image would be very close to this with customizing the items and handling touch for the image to behave as a check. It would not have the needed additional image for the clip or the other label for the date. I'm sure with some item height and item number calculations and other tricks that it could be drawn, but still would only be a close approximation.

2. The Subject and Date also dynamically size depending on Date/Time shown which your code and a list view don't do. It needs some code to get text width of Date/Time and adjust both labels for the size needed. There is some code here to auto ellipse the subject label too.

3. I've found in tabs with images and text I never can get it to look right. Either the image is too small, too big, too far to the top, or under the text depending on the device. It is usually best to make an image with both the icon and the text in the image that way it looks exactly how you need. Test it on a high DPI device too to make sure it is large enough to show the text crisp.
 

Informatix

Expert
Licensed User
Good tutorial. A few comments:

1. "Listview does not allow us to add a checkbox or the small paper clip indicating an attachment" This line should probably read "Listview does not allow us to add a checkbox and the small paper clip indicating an attachment". A listview item that is two lines with an image would be very close to this with customizing the items and handling touch for the image to behave as a check. It would not have the needed additional image for the clip or the other label for the date. I'm sure with some item height and item number calculations and other tricks that it could be drawn, but still would only be a close approximation.

EDIT: I changed my sentence:
"Listview does not allow us to add a checkbox and is limited to two labels"

You can try to simulate a checkbox with bitmaps and use a canvas to draw the paper clip. All right. But what's the interest? To my eyes, it's like trying to use a screwdriver as a hammer. Not the right tool, not the right method. And a waste of time.

2. The Subject and Date also dynamically size depending on Date/Time shown which your code and a list view don't do. It needs some code to get text width of Date/Time and adjust both labels for the size needed. There is some code here to auto ellipse the subject label too.

You're right. I didn't notice the dynamic sizing. That's not complicated. Use MeasureStringWidth to calculate the width of the date label and adjust the width of the other labels accordingly.
I will show Ellipsize in the next article.

3. I've found in tabs with images and text I never can get it to look right. Either the image is too small, too big, too far to the top, or under the text depending on the device. It is usually best to make an image with both the icon and the text in the image that way it looks exactly how you need. Test it on a high DPI device too to make sure it is large enough to show the text crisp.

An image has a perfect layout but it's static. If you want to translate your app, you'll have to draw again each tab. You'll have also to provide many image sizes to ensure the text readibility on all screens, as you said. That's why I don't like this solution. However, I will show in the next article that, sometimes, it's the best solution.
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Are we sure the original app didn't use an image as a checkbox? I know you said there wasn't reverse engineering, etc but it does look different than a true checkbox if both it and your final product were ran on same device. I agree using a listview wouldn't make sense. Many of the standard views won't work for a lot of situations and custom solutions work best like this. I don't even like the standard Spinner/Combobox and am almost done integrating a custom version into my View Manager Class that works properly.

I always forget about translation, which would make the full image tab difficult. I didn't spend much time trying to figure out how it scales the image, but it may be possible to have a large transparent area at the bottom of the image and prevent the text from overlapping the image or even drawing text on the image at runtime. I've found Android to resize/scale PNG images that are large down to small nicely, so I usually make an image crisp enough for my HD screen on my phone and it looks good on lower resolution devices. Below are samples from the lower end tablet device I test with.
 

Attachments

  • Sample1.png
    Sample1.png
    67.5 KB · Views: 649
  • Sample2.png
    Sample2.png
    65.9 KB · Views: 543
  • Sample3.png
    Sample3.png
    57.2 KB · Views: 524

Informatix

Expert
Licensed User
Are we sure the original app didn't use an image as a checkbox? I know you said there wasn't reverse engineering, etc but it does look different than a true checkbox if both it and your final product were ran on same device.

I'm quite sure because it's on my phone and the checkbox looks like a Gingerbread checkbox (on the screenshot, it has the Froyo look).
However, in the next app that I will show, I cannot say if some texts are bitmaps or labels.
Using bitmaps can be useful if you want that all users see exactly the same thing, whatever device and Android version they may have.

I've found Android to resize/scale PNG images that are large down to small nicely, so I usually make an image crisp enough for my HD screen on my phone and it looks good on lower resolution devices.

I do the same.
 

Harris

Expert
Licensed User
Dim pnl As Panel

pnl = FillItem("John Smith", "Hello Fred", "22:26", True, False)
MsgList.AddCustomItem("ID#100", pnl, 60dip)
pnl = FillItem("Lisa Gray", "I'm still loving you", "22:26", False, False)
MsgList.AddCustomItem("ID#101", pnl, 60dip)
pnl = FillItem("Darth Vador", "I'm your father", "09/08/2011", False, False)
MsgList.AddCustomItem("ID#102", pnl, 60dip)
pnl = FillItem("Pamela Rose", "FW: Class ChkList", "09/08/2011", True, True)
MsgList.AddCustomItem("ID#103", pnl, 60dip)


I added 4 more items - total of 8 to your example.


The problem is the sv will only scroll 7 items. I looked at the code to see if the svheight was being miscalculated... Seems fine to me.

if I add 60dip (ie. sv.Panel.Height = svHeight + 60dip) in the svMsgList.ResizePanel - all is well. Confounds me..

Thanks
 

Attachments

  • examp.zip
    19.6 KB · Views: 404
Last edited:

Informatix

Expert
Licensed User
1) The original screenshot comes from a 800x480 screen. My own screenshots come from a 854x480 screen. You can see seven items in the first case, eight items in the second case.

2) Add this line after TabHost1.AddTabWithIcon2:
ScrollView1.Height = -1
That resizes automatically the ScrollView to fit the available space in the TabHost panel.
 

Harris

Expert
Licensed User
That did it - Thanks.
I will have to remember these special features.
 

adrianstanescu85

Active Member
Licensed User
Hi there,

I just started working on your example, which should be absolutely great for my project, but I cannot find anywhere the ClsCheckList class or its code. How can I obtain that?

Thank you!
 
Top