Android Tutorial How they do... #2

How do they... ? #2

How do they... ? #2

(French version/Version française)

I decided to dedicate my second and third tutorials to PlayerPro of BlastOn LLC. I'll show you how to reproduce the album list, the artist list and the equalizer. The interface of these lists is fairly standard for a media player. The equalizer shows a little more graphic design, with custom views.

This article assumes that you know how to use the B4A designer and install a library or class.

First part: the album list

attachment.php


In this screenshot, there are three different areas: a tab bar at the top, a list of images below and an information area at the bottom. At first glance, one might think that the first two areas can be achieved with a TabHost except that the tab bar can scroll horizontally, which is not the case with a TabHost. Moreover, in applications displaying multiple lists filled with images, it is necessary to limit the memory consumption and avoid multiple lists loaded simultaneously. So, I will use a simple panel that I will fill depending on the selected tab. For the tab bar, I will use the ActionBar class and for the list of images, I will use the CustomGallery class, a class that can display a gallery with several vertical columns. At the very bottom, I'll put a Panel to group information about the song currently playing.

I open the designer. I create a HorizontalScrollview at the top, a panel in the middle and another panel at the bottom. In the bottom panel (pnlInfo), I place all the elements necessary to display my information: three ImageViews and three Labels. The first Label has a green text, the second a white text and the third a gray text.

attachment.php


The pnlInfo background is an image with a dark shade of gray. In the designer, I load the image with Add Images and I specify that this drawable is a BitmapDrawable in the Panel properties. Then, I select the image in the Image file list.

Script:
B4X:
'All variants script
hsvTabBar.Height = 65dip
hsvTabBar.Width = 100%x
pnlInfo.Top = 100%y - 65dip
pnlInfo.Width = 100%x
pnlContent.Height = 100%y - hsvTabBar.Height - pnlInfo.Height
pnlContent.Width = 100%x

'Views in pnlInfo
ivNextBtn.Right = 100%x
lblTitle.Width = ivNextBtn.Left - lblTitle.Left - 5dip
lblArtist.Width = lblTitle.Width

I declare all the created views in Globals:
B4X:
Sub Globals
   Dim hsvTabBar As HorizontalScrollView
   Dim pnlContent As Panel
   Dim pnlInfo As Panel

   'Views in pnlInfo
   Dim ivPlayState As ImageView
   Dim lblPlayState As Label
   Dim ivArtwork As ImageView
   Dim lblTitle As Label
   Dim lblArtist As Label
   Dim ivNextBtn As ImageView
 End Sub

I also declare the two classes that I'm going to use: :
B4X:
Dim abTabs As ClsActionBar
Dim lstAlbums As ClsCustomGallery
The CustomGallery class requires the installation of two libraries: BitmapPlus and ScrollView2D.

I load my layout to the activity and I initialize the action bar:
B4X:
Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main.bal")
   
   abTabs.Initialize(hsvTabBar.Panel, True, False, hsvTabBar.Height, Me)
End Sub
The parent of the abTabs bar is the HorizontalScrollView Panel and this bar is as high as its container.

I know I'm going to put six buttons with a width of 65dip in my action bar. So I can already calculate the width of my HorizontalScrollView Panel and, therefore, the width of the action bar. I take into account the case where my screen is wide enough to display all the buttons.
B4X:
hsvTabBar.Panel.Width = Max(65dip * 6, 100%x)
abTabs.AsPanel.Width = hsvTabBar.Panel.Width

Why don't I use the FillParent function of the ActionBar class? Because HorizontalScrollViews (like TabHosts) set the height of their content to the constant -1 (FILL_PARENT). Thus hsvTabBar.Panel.Height would return -1 and cause errors.

My action bar has buttons separated by a very thin divider (1dip) and these buttons share the available width equally:
B4X:
abTabs.SetDividerWidth(1dip)
abTabs.SameWidthForAll(True)

In Globals, I declare my six buttons :
B4X:
Dim aBtn(6) As View

I add also their default color (light gray), the ColorDrawable (green) that I will use for the pressed state and the BitmapDrawable that will fill the bar background:
B4X:
Dim btnDefaultTextColor As Int
btnDefaultTextColor = Colors.RGB(150, 150, 150) 'Gray
Dim cdPressed As ColorDrawable
cdPressed.Initialize(Colors.ARGB(128, 0, 250, 0), 0) 'Green
Dim btnBackground As BitmapDrawable

In Activity_Create, I load my background (a gradient dark gray with a green border at the bottom) and set the pressed state color:
B4X:
btnBackground.Initialize(LoadBitmap(File.DirAssets, "bg_defaut.png"))
abTabs.SetBackground(btnBackground)
abTabs.ReplacePressedDrawable(cdPressed)

Next I add the buttons :
B4X:
aBtn(0) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_artists_defaut.png"), "", 2, 1, "Artists_Click", "")
abTabs.SetText(aBtn(0), "Artists", btnDefaultTextColor, 14)
aBtn(1) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_albums_defaut.png"), "", 2, 2, "Albums_Click", "")
abTabs.SetText(aBtn(1), "Albums", btnDefaultTextColor, 14)
aBtn(2) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_genres_defaut.png"), "", 2, 3, "Genres_Click", "")
abTabs.SetText(aBtn(2), "Genres", btnDefaultTextColor, 14)
aBtn(3) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_playlists_defaut.png"), "", 2, 4, "Playlists_Click", "")
abTabs.SetText(aBtn(3), "Playlists", btnDefaultTextColor, 14)
aBtn(4) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_folders_defaut.png"), "", 2, 5, "Folders_Click", "")
abTabs.SetText(aBtn(4), "Folders", btnDefaultTextColor, 14)
aBtn(5) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_songs_defaut.png"), "", 2, 6, "Songs_Click", "")
abTabs.SetText(aBtn(5), "Songs", btnDefaultTextColor, 14)
I chose the style 2 (small icon with a text below) and I call SetText to change the text color and text size.

Let's see the result:
attachment.php


There are three notable differences with the model: the scrollbar is visible when one moves horizontally, the text is far too low under the icon and the shadow effect on the text is missing. For the scrollbar, I'm going to use the Reflection library to access the method setHorizontalScrollBarEnabled of HorizontalScrollView:
B4X:
Dim r As Reflector
r.Target = hsvTabBar
r.RunMethod2("setHorizontalScrollBarEnabled", False, "java.lang.boolean")

For the text position and the shadow effect, I could modify the class code, but that would complicate a possible update. I prefer to write two small functions to change the Label position in the button (which is not a real button, but a Panel) and add a shadow with the method setShadowLayer:
B4X:
Sub ModifyButton(Btn As Panel, TextColor As Int, ShadowColor As Int)
   Dim lbl As Label
   lbl = Btn.GetView(1) 'The button label is the second view in panel
   lbl.Top = lbl.Top  - 9dip
   lbl.TextColor = TextColor
   AddShadow(lbl, ShadowColor)
End Sub
Sub AddShadow(lblTxtVw As Label, Color As Int)
   Dim r As Reflector
   r.Target = lblTxtVw
   Dim Args(4) As Object
   Args(0) = 1 'radius
   Args(1) = 0 'dx
   Args(2) = 1 'dy
   Args(3) = Color
   r.RunMethod4("setShadowLayer", Args, Array As String("java.lang.float", "java.lang.float", "java.lang.float", "java.lang.int"))
End Sub
I call my function ModifyButton in Activity_Create for each button:
B4X:
For i = 0 To 5
   ModifyButton(aBtn(i), btnDefaultTextColor, Colors.Black)
Next
Result :
attachment.php


Now I have to manage the change of tab. I specified when adding buttons that I wanted to listen the OnClick event. It's time to create the six event handlers:
B4X:
Sub Artists_Click(ActionBar As ClsActionBar, Btn As View)
End Sub
Sub Albums_Click(ActionBar As ClsActionBar, Btn As View)
End Sub
Sub Genres_Click(ActionBar As ClsActionBar, Btn As View)
End Sub
Sub Playlists_Click(ActionBar As ClsActionBar, Btn As View)
End Sub
Sub Folders_Click(ActionBar As ClsActionBar, Btn As View)
End Sub
Sub Songs_Click(ActionBar As ClsActionBar, Btn As View)
End Sub

In each handler, I'm going to call a function that will change the appearance of the button depending on its state and will clean pnlContent. This function, TabChange, must remember the last clicked button in order to restore it to its default appearance when another tab is selected. For this, I declare two variables in Globals:
B4X:
Dim LastBtn As View
Dim LastIcon As String
Then I write the function :
B4X:
Sub TabChange(Btn As Panel, DefaultIcon As String, SelectedIcon As String)
   If LastBtn.IsInitialized Then
      ' ReplaceIcon restores also the default background (transparent)
      abTabs.ReplaceIcon(LastBtn, LoadBitmap(File.DirAssets, LastIcon))
      ModifyButton(LastBtn, btnDefaultTextColor, Colors.Black)
   End If
   abTabs.ReplaceIcon(Btn, LoadBitmap(File.DirAssets, SelectedIcon))
   Btn.Background = LoadNinePatchDrawable("bg_select")
   ModifyButton(Btn, Colors.DarkGray, Colors.White)
   LastBtn = Btn
   LastIcon = DefaultIcon

   ' Scrolls the HSV if the button is partially hidden
   If Btn.Left + Btn.Width > hsvTabBar.ScrollPosition + hsvTabBar.Width Then
      'The margin of 10dip moves the button away from the fading edge
      hsvTabBar.ScrollPosition = Btn.Left + Btn.Width - hsvTabBar.Width + 10dip
   Else If Btn.Left < hsvTabBar.ScrollPosition Then
      hsvTabBar.ScrollPosition = Btn.Left - 10dip
   End If

   ' Cleans the panel
   For i = pnlContent.NumberOfViews - 1 To 0 Step -1
      pnlContent.RemoveViewAt(i)
   Next
End Sub
Since the function ReplaceIcon recalculates the position of the text under the icon, I have to call my function ModifyButton again to raise the Label. It is a good opportunity to change the text and shadow colors depending on the state.

I added a few lines of code to make sure the button is fully displayed when I click it. If this is not the case, I scroll hsvTabBar.

Example of call of the function TabChange in an event handler:
B4X:
TabChange(Btn, "tab_albums_defaut.png", "tab_albums_select.png")

When a button is clicked, the background becomes bright and displays a green bar with an arrow. It is an image. I could create it with the exact dimensions of the button, but it would have two disadvantages: I would have to redo the image if I change my mind about the button size and my button should have the same size regardless of the orientation or the screen width. Not really convenient. So that my button can extend without glitches, I have to use a 9-patch drawable. A 9-patch drawable is an image that contains stretchable zones and content areas. You can read his description here.
Look at the 9-patch that I drew from the screenshot:

attachment.php


The black lines above indicate which parts of the image can be stretched. Same thing with the line on the left. The lines on the right and below define the content area (the other views can draw only in this area).
I copied the 9-patch file into the folder Objects/res/drawable and I protected the file against being overwritten. To load in into the application, I use the following function:
B4X:
'Gets a 9-patch drawable from the application resources
Sub LoadNinePatchDrawable(ImageName As String) As Object
   Dim r As Reflector
   Dim package As String
   package = r.GetStaticField("anywheresoftware.b4a.BA", "packageName")
   Dim ID_Drawable As Int
   ID_Drawable = r.GetStaticField(package & ".R$drawable", ImageName)
   r.Target = r.GetContext
   r.Target = r.RunMethod("getResources")
   Return r.RunMethod2("getDrawable", ID_Drawable, "java.lang.int")
End Sub
Result:
attachment.php


I can fill now my album list. I create a function CreateAlbumList that I call from the event handler. Henceforth, the handler for albums looks like this:
B4X:
Sub Albums_Click(ActionBar As ClsActionBar, Btn As View)
   If LastBtn <> Btn Then
      TabChange(Btn, "tab_albums_defaut.png", "tab_albums_select.png")
      CreateAlbumList
   End If
End Sub

In CreateAlbumList, I initialize the image gallery (the style 7 is a vertical grid style with rescaled images):
B4X:
lstAlbums.Initialize(pnlAlbums, 0, 0, thContent.Width, thContent.Height, 7, Me, "", "lstAlbums_Click", "", "lstAlbums_Scroll")

Images will be spaced 4dip and distributed on a variable number of columns depending on the width of the screen. Their ideal size will be 150dip x 150dip.
B4X:
lstAlbums.SpaceBetweenThumbnails = 4dip
Dim ColWidth, NbCols As Int
ColWidth = Min(Min(150dip, pnlContent.Height), pnlContent.Width)
NbCols = Round(lstAlbums.SV2D.Width / ColWidth)
lstAlbums.SizeInGrid = ((lstAlbums.SV2D.Width - (lstAlbums.SpaceBetweenThumbnails * (NbCols - 1))) / NbCols) - 1

Smaller images will be enlarged to fill up their location in the grid and clicked images will be colored green (I reuse the ColorDrawable of pressed buttons):
B4X:
lstAlbums.RescaleOnlyIfBigger = False
lstAlbums.PressedDrawable = cdPressed

To fill the gallery, I use the MediaBrowser library (the retrieval of album data, the loading in a separate thread and the memory management won't be discussed here). To add an image from a file, I can call the functions AddThumbnail or InsertFileAt. E.g.:
B4X:
lstAlbums.AddThumbnail(LoadBitmapSample(CoverDir, CoverFile, lstAlbums.SizeInGrid, lstAlbums.SizeInGrid), i)
or
B4X:
lstAlbums.InsertFileAt(CoverDir, CoverFile, lstAlbums.NumberOfThumbnails, i)

What remains is to create the label that displays the album title. It must be inserted in the thumbnail Panel.
B4X:
pnlThumbnail = lstAlbums.GetThumbnailAt(lstAlbums.NumberOfThumbnails - 1) 'Gets directly the added thumbnail
'pnlThumbnail = lstAlbums.GetThumbnailWithTag(i) 'Not recommended in a loop: this function iterates through all the thumbnails to find the right one
Dim lblAlbumTitle As Label
lblAlbumTitle.Initialize("")
lblAlbumTitle.Color = Colors.ARGB(128, 128, 128, 128) 'Semi-transparent gray
lblAlbumTitle.Gravity = Gravity.CENTER_HORIZONTAL + Gravity.CENTER_VERTICAL
lblAlbumTitle.TextColor = Colors.White
lblAlbumTitle.TextSize = 13
lblAlbumTitle.Text = strAlbumTitle
lblAlbumTitle.Typeface = Typeface.DEFAULT_BOLD
pnlThumbnail.AddView(lblAlbumTitle, 0, pnlThumbnail.Height * 0.7, pnlThumbnail.Width, pnlThumbnail.Height * 0.3)
AddShadow(lblAlbumTitle, Colors.Black)
I want this Label to display two lines maximum, and to end with an ellipsis if the text is too long, so I add:
B4X:
Dim r As Reflector
r.Target = lblAlbumTitle
r.RunMethod2("setMaxLines", 2, "java.lang.int")
r.RunMethod2("setHorizontallyScrolling", False, "java.lang.boolean") 
r.RunMethod2("setEllipsize", "END", "android.text.TextUtils$TruncateAt")

I finish by putting some demo content in the information Panel at the very bottom. Et voilà:

attachment.php


Scrolling through the list, one can notice that PlayerPro displays on the right a fast scroll handle, as in ListView when you set FastScrollEnabled to True. To replicate it, I'm going to use the class ScrollPanel2D (the ScrollPanel version for ScrollView2D). I initialize the class after having declared it in Globals:
B4X:
spFastScroll.Initialize(lstAlbums.SV2D, 60dip, 52dip, False) 'No cache
I load the drawable of the fast scroll handle:
B4X:
spFastScroll.ReplaceBackground(spFastScroll.LoadDrawable("scrollbar_handle_accelerated_anim2"))
So that ScrollPanel is informed about the scrolling in the gallery, I transfer the OnScroll event to it:
B4X:
Sub lstAlbums_Scroll(PositionX As Int, PositionY As Int)
   spFastScroll.DisplayCustomText(PositionY, "") 'No text is displayed
End Sub
I can now remove the vertical scrollbar of ScrollView2D, become useless:
B4X:
lstAlbums.SV2D.ScrollbarsVisibility(False, False)

attachment.php


<<< How do they... ? #1
How do they... ? #3 >>>

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.
 
Last edited:

moster67

Expert
Licensed User
Longtime User
Another great tutorial. :sign0098:

Thank you!
 

JogiDroid

Member
Licensed User
Longtime User
Great tutorial, I really appreciate your time and knowledge to make this tutorial.
 

AscySoft

Active Member
Licensed User
Longtime User
I am speechless. Another great tutorial. thank you so much. Right now I'm working on a simple mp3 player application(for my personal use), and this UI absolutely shock me, in the good sense.
Thank you.
 

Roger Garstang

Well-Known Member
Licensed User
Longtime User
This is great! And perfect timing too as I was running into some problems with my tabs. I think I will tear it apart and make some custom tabs using this method or a hybrid thereof. I've been wanting to make some tabs that look like true file folder tabs like I did on my web app version and this gets the gears turning for multiple ways to do it.
 

Stulish

Active Member
Licensed User
Longtime User
So you have the image files available to downloads or even a copy of the tutorial source code.


thanks
 

Informatix

Expert
Licensed User
Longtime User
So you have the image files available to downloads or even a copy of the tutorial source code.
thanks

About the image files, please re-read the disclaimer at the beginning of the first tutorial.

About the source code, I don't provide it because:
- the public parts are all here (I know it's less convenient than a zipped file);
- most of the undisclosed parts are of no use to anyone (like filling a list with fake data);
- the source code is full of test functions, different tries, timing code... and I don't want to spend time to clean it.

Regards,
Fred
 

Informatix

Expert
Licensed User
Longtime User
For educational purposes, you asked your readers to let you know if we saw any misused English words in your English translation. First, if you meant to start the tutorial with a question, the tutorial might be supposed to start with these words: “How do they…?”
In french, the title is a question. I can't remember why I changed my mind when I translated it in english. I agree that was a bad idea.

Second, one word should be changed in this sentence: OLD: "In the designer, I load the image with Add Images and I precise that this drawable is a BitmapDrawable in the Panel properties." BETTER: "In the designer, I load the image with Add Images and I specify that this drawable is a BitmapDrawable in the Panel properties."

Thank you for the correction. I'm going to change the word. In french, we can use "precise" as a verb. I thought it was the same in english.
If you see another mistake or something that could be written more clearly, let me know.
By the way, I have a question. I see regularly these two grammatical constructions:
"Use the function DrawBitmap"
"Use the DrawBitmap function"
Are they both right ? Is there a subtle difference between them?
 

Chr6373

New Member
Licensed User
Longtime User
Programmers can skip this post...

Some people might complain (to themselves, I hope) that "Chr6373 is wasting time with off-topic commentary." If I do not answer, others may think that "Chr6373 is rude for not answering the person who took the time to prepare this tutorial in perfectly acceptable American English form." Out of respect for the thread-creator's interest, I have tried to prepare an acceptable answer to his question.

If you are asking me to choose the most universal and "proper" use of American English from the options below, and those are:

A) "the DrawBitmap function" or
B) "the function DrawBitmap" or
C) Both

I would choose "A" in this multiple choice situation. Reversing the order suggests that the author is trying to point out that there is a function entitled DrawBitmap, without including the word "entitled" or "called" in the sentence. I would not use the word "called" because a function can "call" another function, so it would be confusing to use the "call" word. I do not know why my brain likes the phrase "DrawBitmap function." It might be the result of knowing that many programming languages require at least one DrawBitmap function somewhere in the program, so I am expecting to find that DrawBitmap function in your B4A source code. An American English-speaking person would probably agree that it is not proper to write: "most programs need at least one function DrawBitmap" -- and -- it is also improper to write: "I am looking for that function DrawBitmap in your source code." So, in the end, "DrawBitmap function" seems to be a better choice. Whew.

Your tutorial is excellent, please forgive the wordy response to your question... I have a compelling need to finish exploring your suggestions in my new project, while the flame from the creative spark you started is there, so I have converted your posts and images into to a MS Word document that I will be using for future reference, and I will be back to lurking-only mode for a while... (probably to the obvious relief of others)

Roger is correct (below). I would expect to see a tutorial for novices with an introduction to the function that says: "Use the function called DrawBitmap." Or, the novice would need some sort of punctuation or bold, or underlining on the DrawBitmap part. The phrase "use the function DrawBitmap" seems to need at least a literary pause. If the tutorial is not for novices to such a function, or that phrase ("function called DrawBitmap") has already been used, then I think it would be more common American English to subsequently refer to the function as the "DrawBitmap function" even when it is used at the end of a short imperative declaration, as in the options you originally presented.
 
Last edited:

Roger Garstang

Well-Known Member
Licensed User
Longtime User
I like A as well. I come from using Functions and Subs though. Most languages lately tend to just declare them with the word Sub...which to me means a Function that doesn't return a value. Many new programmers start out with experiencing Classes and Object Oriented programming only so may even call both a Method and use just one word to declare each type as well under the assumption that all Methods/Subs/Functions return a value even if it is Null, Void, or I've often seen B4A Subs that return no value indicate a String return in the tooltip.

This led me to believe about a week ago that I could do things like EditView1.Text= EditView2.Text= Editview3.Text= "String for all 3", but Erel reminded me that B4A sticks with the early BASIC syntax of = meaning two different things. A second = in a line tries to be considered like a boolean operation.

I could also go with the B definition, but to me using the "function" before creates a pause and would be more of a definition like: "Use the function- DrawBitmap" or "Use the function called DrawBitmap". I'd use B in a training manual or something maybe to put emphasis on it, but when describing to developers I'd us A.
 

mancio61

New Member
Licensed User
Longtime User
Error on text alignment in the top bat

How do they... ? #2
........
.........
Let's see the result:
attachment.php

.......
.......

Hi, fantastic tutorial. I'm following it step by step and it's very clear. However, when i build the bar with all the buttons and the underlying text, I obtain e not perfect alignement of the text in the bottom part of the buttons.

Here's my result. The icons are obviosly all identical, 52x42. Honestly I don't undestand why I obtain this result...
 

Attachments

  • ButtonBar.png
    ButtonBar.png
    18.2 KB · Views: 591

mancio61

New Member
Licensed User
Longtime User
managing images in the scroll panel..

...and can you tell me if there's some examples how to populate the panel with data (e.g. records with image, title, artist.,etc.)?
Is there an example on the MediaBrowser Library?
 

mancio61

New Member
Licensed User
Longtime User
...nine.patch drawable...

..and finally...
i've got an error trying to load a .9.png file as indicated in your tutorial.
I've tried to use the attached one, but the compiler tell me that this is a not valid 9.patch file....
Any suggestion?
 

Attachments

  • buttonbar_bg_focused.9.png
    buttonbar_bg_focused.9.png
    751 bytes · Views: 583

Informatix

Expert
Licensed User
Longtime User
..and finally...
i've got an error trying to load a .9.png file as indicated in your tutorial.
I've tried to use the attached one, but the compiler tell me that this is a not valid 9.patch file....
Any suggestion?

I suppose you're using the decompiled resources. I have different files, made by myself from the screenshots (that was a kind of challenge for me). My icons are squared, for example, with a size of 48x48. My 9-patch is sized 16x70. I can't distribute these files because they are copies of a copyrighted work. You should easily recreate the 9-patch from the screenshot in the tutorial.
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
...and can you tell me if there's some examples how to populate the panel with data (e.g. records with image, title, artist.,etc.)?
Is there an example on the MediaBrowser Library?

You can do that with the MediaBrowser library, indeed, and the ID3 class for MP3.
The method:
- get a list of your audio files (GetMediaAudioList)
- make a list of folders from this audio list
- get the list of images (GetMediaImageList)
- filter this list to keep only the images in your music folders
- search through the MP3 files if they have an artwork embedded (ID3 class)
 

Chr6373

New Member
Licensed User
Longtime User
Still Excellent B4A tutorial #2 and #3!

So far, I have been able to figure out and implement almost everything, which is really enjoyable. Again, #2 and #3 are great tutorials.

While I am on a break, here are some more *minor* American English grammar observations regarding TUTORIAL #2 and #3. The goal in submitting these is not to be a snob, troll or a pest. This appears to be "the one" tutorial that can motivate all the "Hello World" B4A beginners like myself... (vague reference to the Matrix or LOTR, take your pick). It is out of respect for the author's hard work, and his request for translation "tips" -- that I have a couple more suggestions:

Old sentence: I copied the 9-patch file in the folder Objects/res/drawable and I protected it against writing.
CORRECTED sentence: I copied the 9-patch file into the folder Objects/res/drawable and I protected the file against being overwritten. (Erel's nine patch image tutorial says to set the Read-Only file property, so the file is not deleted at run time.)
Comment: Copying something "in" a place is not the same as copying (or pasting) something "into" a place. Similarly, protecting a file "against writing" is not the same as protecting a file against "being overwritten."

Old sentence: I want that this Label displays two lines maximum and ends with an ellipsis if the text is too long. I add:
CORRECTED sentence: I want this Label to display two lines maximum, and to end with an ellipsis if the text is too long, so I add:
Long Comment: See http://lilt.ilstu.edu/jhreid/grammar/vouloir.htm: "Vouloir" is followed by "que" and a dependent clause when the subject of the main clause differs from the subject of the dependent clause. The verb in the dependent clause must be conjugated in the subjunctive. Note that English still uses the infinitive.
Pink Panther Dictionary comment: In American English, everbody just wants everything. Amercians say: I want to buy a hamburger. I want my friend to buy me a hamburger. Give me the *!(S* hamburger... In French, some things are the same, and some are slightly different: I want to buy a hamburger. I want that my friend will buy me a hamburger. Give me the *!(S* hamburger...

Old sentence: It remains to create the Label that displays the album title.
CORRECTED sentence: What remains is to create the label that displays the album title.

Old sentence in Tutorial #3: It remains the case of extensions.
CORRECTED sentence: What remains is the possibility of extensions.

The two tutorials really don't need to be changed at all. If your libraries/classes/tutorials were symphonies, I am sure Mozart would be impressed. I just hope you will keep writing, in French or in English!
 
Last edited:
Top