Android Tutorial How they do... #3

How do they... ? #3​

<Version française/french version>

This third tutorial returns to the interface of PlayerPro of BlastOn LLC. We are going to reproduce the artists list and the operation of the customized views of the equalizer. We are also going to add the drag & drop functionality to the tabs bar and deport the ScrollPanel display.

This article assumes that you already know the bases of the development with B4A and that you have read the first two tutorials.

Second part: the artists list

attachment.php


We saw in the previous tutorial how to reproduce the general layout of the application. I'm going to focus this time on the creation of the artists list. It has a noticeable feature: by clicking on the artist, you can expand a list of albums below. In the Android API, this view is a ExpandableListView that we can reproduce easily with the class CheckList.

In the first tutorial, I showed you how to make a mask for an item in the designer and how to load it into the application. I am not going to use this method here because I need maximum speed. You should know that loading a .bal file with LoadLayout is very slow. I timed both methods (loading views created in the designer vs. creation in the code) in several projects and the difference varies from 5 to 7 depending on the project. In this case, my device (a Huawei Honor with a single core 1.4 Ghz) takes 35 seconds to build 1000 entries in the artists list with the first method (loading views created in the designer) and only 5 with the second (creation in the code). So, I advise to avoid using LoadLayout in a loop. That said, you can still use the designer to make models (the visual model helps you to choose the right values for the view properties, in particular Left, Top, Width and Height).

Model for an item of the artists list:
attachment.php


Before creating the views, I have to modify the events handler (cf. previous tutorial) to call my new function CreateArtistList:
B4X:
Sub Artists_Click(ActionBar As ClsActionBar, Btn As View)
   If LastBtn <> Btn Then
      TabChange(Btn, "tab_artists_defaut.png", "tab_artists_select.png")
      CreateArtistList
   End If
End Sub

In CreateArtistList, I start by loading the static images in memory, that is to say, the images that are not specific to an item (the background image, the icon indicating that an extension is possible and the icon after extension). This will avoid having to read files for each created item. This ensures also there is only one copy of these images in memory:
B4X:
bdDefaultBackground.Initialize(LoadBitmap(File.DirAssets, "bg_liste.png"))
bdCanExpandArrow.Initialize(LoadBitmap(File.DirAssets, "can_expand.png"))
bdExpandedArrow.Initialize(LoadBitmap(File.DirAssets, "expanded.png"))
These three BitmapDrawables have been declared in Globals.

Next, I create the ScrollView holding the list and allowing to scroll it:
B4X:
svArtist.Initialize2(0, "svArtist")
pnlContent.AddView(svArtist, 0, 0, pnlContent.Width, pnlContent.Height)
Then I create the list itself:
B4X:
lstArtist.Initialize(Me, svArtist, "", "lstArtist_Click", "", 1dip)
The ScrollView and the list have been declared in Globals:
B4X:
Dim svArtist As ScrollView
Dim lstArtist As ClsCheckList

To reproduce the custom appearance of the PlayerPro list, I cannot leave the appearance management to the class (by default, it does not support the extension indicators). I let it know that I take control by setting the OnPaint listener:
B4X:
lstArtist.SetOnPaintListener("PaintArtist")
Then I create the event handler:
B4X:
Sub PaintArtist(pnlItem As Panel, State As Int)
   Select State
      Case 1   ' Extended
         pnlItem.Color = Colors.RGB(120, 190, 30) 'Green
         Dim ivExpandState As ImageView
         ivExpandState = pnlItem.GetView(3)
         ivExpandState.Background = bdExpandedArrow
      Case 4   ' Pressed
         pnlItem.Color = Colors.RGB(120, 190, 30) 'Green
      Case Else
         pnlItem.Background = bdDefaultBackground
         Dim ivExpandState As ImageView
         ivExpandState = pnlItem.GetView(3)
         ivExpandState.Background = bdCanExpandArrow
   End Select
End Sub

From now on, whenever an item in the list will change state, I will be informed and I can change its appearance accordingly. I have to manage at least three states:
0 (default): the item is not pressed or extended
attachment.php
4 (pressed): the user just pressed the item
attachment.php
1 (extended): the item has been extended (we will see later how to fill the extension)
attachment.php

I don't forget in CreateArtistList to set the color of the divider separating items:
B4X:
lstArtist.DividerColor = Colors.Black

To fill my list, I need a function creating items:
B4X:
Sub CreateArtistItem(Artist As String, PicFile As String, NbOfAlbums As Int) As Panel
   Dim pnl As Panel
   pnl.Initialize("")
   pnl.Background = bdDefaultBackground

   Dim ivPhoto As ImageView
   ivPhoto.Initialize("")
   ivPhoto.Gravity = Gravity.FILL
   ivPhoto.Bitmap = LoadBitmap(strPicDir, PicFile)
   pnl.AddView(ivPhoto, 0, 0, 65dip, 65dip)

   Dim lblArtistName As Label
   lblArtistName.Initialize("")
   lblArtistName.TextColor = Colors.White
   lblArtistName.TextSize = 18
   lblArtistName.Typeface = Typeface.DEFAULT_BOLD
   lblArtistName.Gravity = Gravity.TOP
   lblArtistName.Text = Artist
   Ellipsize(lblArtistName)
   AddShadow(lblArtistName, Colors.Black)
   pnl.AddView(lblArtistName, 75dip, 10dip, 100%x - 110dip, 25dip)

   Dim lblNbOfAlbums As Label
   lblNbOfAlbums.Initialize("")
   lblNbOfAlbums.TextColor = Colors.RGB(220, 220, 220)
   lblNbOfAlbums.TextSize = 14
   lblNbOfAlbums.Gravity = Gravity.BOTTOM
   If NbOfAlbums > 1 Then
      lblNbOfAlbums.Text = NbOfAlbums & " albums"
   Else
      lblNbOfAlbums.Text = "1 album"
   End If
   AddShadow(lblNbOfAlbums, Colors.Black)
   pnl.AddView(lblNbOfAlbums, 75dip, 34dip, 100%x - 110dip, 20dip)

   Dim ivExpandState As ImageView
   ivExpandState.Initialize("")
   ivExpandState.Gravity = Gravity.FILL
   ivExpandState.Background = bdCanExpandArrow
   pnl.AddView(ivExpandState, 100%x - 36dip, 10dip, 32dip, 32dip)

   Return pnl
End Sub
It is in this function that I avoid to use LoadLayout. All views are created and set in the code.

My function calls another function, Ellipsize, which adds an ellipsis if the artist name is too long:
B4X:
Sub Ellipsize(lbl As Label)
   Dim r As Reflector
   r.Target = lbl
   r.RunMethod2("setLines", 1, "java.lang.int")
   r.RunMethod2("setHorizontallyScrolling", True, "java.lang.boolean") 
   r.RunMethod2("setEllipsize", "END", "android.text.TextUtils$TruncateAt")
End Sub

I fill my list using CreateArtistItem. Example:
B4X:
Dim pnlArtist As Panel
pnlArtist = CreateArtistItem("Big Country", "big_country_square.jpg", 1)
lstArtist.AddCustomItem("BigCountry1", pnlArtist, 65dip)
I finish my filling with:
B4X:
lstArtist.ResizePanel

What remains is the case of extensions. I indicated while I initialized my list that I wanted to handle the OnClick event. I write the handler:
B4X:
Sub lstArtist_Click(pnlItem As Panel, ID As Object)
   If lstArtist.HasExtraContent AND lstArtist.ExtendedItemID = ID Then
      lstArtist.CollapseItem
   Else
      Dim pnlAlbums As Panel
      pnlAlbums.Initialize("")
      Dim NbOfAlbums As Int
      NbOfAlbums = AddAlbumItems(pnlAlbums, ID)
      lstArtist.ExtendItem(ID, pnlAlbums, 65dip * NbOfAlbums)
   End If
End Sub
This handler creates an extension when the item has been clicked and removes the extension if the item already has one.
The extension is filled with AddAlbumItems which will not be detailed here. This function uses a CreateAlbumItem function which is very similar to CreateArtistItem.

Result:
attachment.php


Third part: the little extras

The PlayerPro interface has two characteristics that we are going to reproduce: the ability to change the order of buttons on the tab bar by a long click and the display of the first letter of the artist name when using the fast scroll handle.

The ActionBar class that I used for the tabs has a drag & drop feature. I just have to implement it.
I start by changing the initialization of my buttons to indicate that I want to handle long clicks (no need in this case to have custom handlers, all buttons can have the same). Example:
B4X:
aBtn(0) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_artists_defaut.png"), "", 2, 1, "Artists_Click", "Btn_LongClick")
aBtn(1) = abTabs.AddButton(LoadBitmap(File.DirAssets, "tab_albums_defaut.png"), "", 2, 2, "Albums_Click", "Btn_LongClick")

I write the long click handler:
B4X:
Sub Btn_LongClick(ActionBar As ClsActionBar, Btn As View)
   If LastBtn <> Btn Then
      'Replaces the transparent background by a solid one
      Btn.Background = btnBackground
   End If
   abTabs.StartDragAndDrop(Btn, hsvTabBar, "Btn_AfterDrop")
End Sub
And the function called after the button is dropped:
B4X:
Sub Btn_AfterDrop(ActionBar As ClsActionBar, Btn As View)
   If LastBtn = Btn Then
      'Selected btn: redraws the nine-patch erased after the drag&drop
      Btn.Background = LoadNinePatchDrawable("bg_select")
   End If
End Sub
The class redraws the button background when the operation ends. So I have to reload the 9-patch with bright color (cf. the second tutorial) if the button is the selected tab.

Drag & drop in action:
attachment.php


Now, let's see the fast scroll handle. We want it to display the first letter of the artist name in a separate label. That is quite easy with the ScrollPanel class. I declare this class and the Label in Globals:
B4X:
Dim spFastScrollArtist As ClsScrollPanel
Dim lblFirstLetter As Label
I initialize my class in CreateArtistList:
B4X:
spFastScrollArtist.Initialize(svArtist, 60dip, 52dip, False) 'No cache
spFastScrollArtist.ReplaceBackground(spFastScrollArtist.LoadDrawable("scrollbar_handle_accelerated_anim2"))
I initialize the Label and I place it in the center of the screen:
B4X:
lblFirstLetter.Initialize("")
lblFirstLetter.Background = spFastScrollArtist.LoadDrawable("toast_frame")
lblFirstLetter.Gravity = Gravity.CENTER_HORIZONTAL + Gravity.CENTER_VERTICAL
lblFirstLetter.TextSize = 20
lblFirstLetter.TextColor = Colors.White
lblFirstLetter.Typeface = Typeface.DEFAULT_BOLD
pnlContent.AddView(lblFirstLetter, (lstArtist.getWidth - 70dip) / 2, (lstArtist.getHeight - 70dip) / 2, 70dip, 70dip)
Then I request the display of the first letter for each item:
B4X:
Sub svArtist_ScrollChanged(Position As Int)
   spFastScrollArtist.DisplayFirstChar(Position)
End Sub

Problem: the first letter is displayed on the ScrollPanel instead of the Label and the Label is displayed permanently. To remedy that, I add in CreateArtistList:
B4X:
spFastScrollArtist.GetLabel.Visible = False
lblFirstLetter.Visible = False 'Initially, the Label is hidden
spFastScrollArtist.SetOnShowHideEvent(Me, "spArtist_ShowHide")
spFastScrollArtist.SetOnTextUpdatedEvent(Me, "spArtist_TextUpdated")
Then I create two event handlers that change the visibility and content of the Label:
B4X:
Sub spArtist_ShowHide(SenderSP As Object, Visible As Boolean)
   If Visible = False Then
      lblFirstLetter.Visible = False
   Else
      lblFirstLetter.Visible = spFastScrollArtist.ScrollPanelMovedByUser
   End If
End Sub
Sub spArtist_TextUpdated(SenderSP As Object, Text As String)
   lblFirstLetter.Text = Text
End Sub

Et voilà:
attachment.php


Note: I initialized the ScrollPanel without activating its cache. This is not a problem here because the list is small, but if your list contains more than a hundred of items, I urge you to turn it on. Disadvantage: you will have to call RefreshCache each time you change the content or order of the list (which in this case should not happen).

Fourth and last part in the next post...
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
Fourth part: the custom views of the equalizer

I will focus now on another screen of PlayerPro: the equalizer.

attachment.php


Here there are views that do not exist in the standard views of Android: the vertical SeekBars, the rotary knobs and the On/Off button that does not look like the usual ToggleButton. The rest (Label, Spinner, Button, SeekBar, ImageView) is very classic. I'm going to reproduce only the custom views because the layout of the whole is not of particular interest.

First, let's see the ToggleButton. PlayerPro offers several versions:
attachment.php


By using it, you realize it is a set of four images (two for the static states: on/off, and two for the dynamic states: off to on/on to off). At first glance, it seems possible to reproduce this behavior with OnDown, OnUp and OnClick events of Button, but this solution does not work very well because if OnUp can manage the case where the click event does not occur (the finger is outside the button), it conflicts with OnClick otherwise. We will therefore use another solution, very simple, using two StateListDrawables. I declare a Button and four images in Globals:
B4X:
Dim btnEQOnOff As Button
Dim bdEqOn As BitmapDrawable
Dim bdEqOnToOff As BitmapDrawable
Dim bdEqOff As BitmapDrawable
Dim bdEqOffToOn As BitmapDrawable
In the code, I load my bitmaps and I initialize my button in the OFF state:
B4X:
bdEqOn.Initialize(LoadBitmap(File.DirAssets, "eqon.png"))
bdEqOff.Initialize(LoadBitmap(File.DirAssets, "eqoff.png"))
bdEqOnToOff.Initialize(LoadBitmap(File.DirAssets, "eqontooff.png"))
bdEqOffToOn.Initialize(LoadBitmap(File.DirAssets, "eqofftoon.png"))

btnEQOnOff.Initialize("btnEQOnOff")
ChangeBtnState(False)
Activity.AddView(btnEQOnOff, 80dip, 5dip, 100dip, 50dip)

I just called the ChangeBtnState function. It is the one going to swap the two StateListDrawables (one for the ON state, the other for the OFF state). The Tag property of the button is used to store the current state:
B4X:
Sub ChangeBtnState(IsOn As Boolean)
   Dim sd As StateListDrawable
   sd.Initialize
   If IsOn Then
      sd.AddState(sd.State_Pressed, bdEqOnToOff)
      sd.AddCatchAllState(bdEqOn)
      btnEQOnOff.Tag = "ON"
   Else
      sd.AddState(sd.State_Pressed, bdEqOffToOn)
      sd.AddCatchAllState(bdEqOff)
      btnEQOnOff.Tag = "OFF"
   End If
   btnEQOnOff.Background = sd
End Sub

It remains only to trigger the change of StateListDrawable after a click:
B4X:
Sub btnEQOnOff_Click
   ChangeBtnState(btnEQOnOff.Tag = "OFF")
End Sub

Here is a slightly more complicated variant, but useful if you want to add animations or ignore the case where the finger leaves the button. The Button is replaced by a Panel:
B4X:
pnlEQOnOff.Initialize("pnlEQOnOff")
pnlEQOnOff.Background = bdEqOff
pnlEQOnOff.Tag = "OFF"
Activity.AddView(pnlEQOnOff, 80dip, 5dip, 100dip, 50dip)

All is done in the OnTouch event handler:
B4X:
Sub pnlEQOnOff_Touch (Action As Int, X As Float, Y As Float)
    If Action = 0 Then ' DOWN
        If pnlEQOnOff.Tag = "OFF" Then
            pnlEQOnOff.Background = bdEqOffToOn
            pnlEQOnOff.Tag = "OFF to ON"
        Else
            pnlEQOnOff.Background = bdEqOnToOff
            pnlEQOnOff.Tag = "ON to OFF"
        End If
    Else If Action = 2 Then ' MOVE
        If X < 0 OR X > pnlEQOnOff.Width OR Y < 0 OR Y > pnlEQOnOff.Height Then
            ' Out of bounds: the user action is cancelled
            If pnlEQOnOff.Tag = "OFF to ON" Then
                pnlEQOnOff.Background = bdEqOff
                pnlEQOnOff.Tag = "OFF"
            Else If pnlEQOnOff.Tag = "ON to OFF" Then
                pnlEQOnOff.Background = bdEqOn
                pnlEQOnOff.Tag = "ON"
            End If
        End If
    Else If Action = 1 Then ' UP
        If pnlEQOnOff.Tag = "OFF to ON" Then
            pnlEQOnOff.Background = bdEqOn
            pnlEQOnOff.Tag = "ON"
        Else If pnlEQOnOff.Tag = "ON to OFF" Then
            pnlEQOnOff.Background = bdEqOff
            pnlEQOnOff.Tag = "OFF"
        End If
    End If
End Sub

Let's see the vertical SeekBar. PlayerPro offers several versions:
attachment.php


To reproduce this SeekBar, I need the InputSlider class and two images: the slider bar and the mobile cursor above. I declare the class in Globals:
B4X:
Dim isEQFrequency As InputSlider
In the code, I initialize the class and I load the images:
B4X:
isEQFrequency.Initialize(Me, "isEQFrequency", Activity, 20dip, 90dip, 40dip, 200dip)
isEQFrequency.SetBackGroundImage(LoadBitmap(File.DirAssets, "bg_eq.png"))
isEQFrequency.SetPointerImage(LoadBitmap(File.DirAssets, "eqthumb.png"))
I resize my pointer:
B4X:
isEQFrequency.SetPtrSize(0, 0, 40dip, 40dip)
That's it.

You can also do this SeekBar with the VSeekbar class.

Let's end this tour of the custom views by the rotary knob:
attachment.php


This is likely a panel with three images inside (the knob, the marks around and the name). To draw the curved name, one could use the Reflection library to get access to the drawTextOnPath function of the Android API but it is much easier to design an image in a drawing program and import it into the application. The downside is that it does not facilitate translation (for each language, you must create an image and include it in the application).

I decided this time to provide you with a source code to download. Of course, you will not get images or code from PlayerPro, but you will see that my algorithms (simple and complete) allow the same operation. I've included two other types of button: rotary switches and endless buttons. This code requires the BitmapPlus library (provided with the CustomGallery class).

In BitmapPlus, I use the Matrix class of the Android API to rotate the button. I could do it with the Canvas DrawBitmapRotated function, but this function does not have a filter and the result is not very good, even if it is clearer. Here is a comparison (BitmapPlus above, Canvas.DrawBitmapRotated below):
attachment.php


Good reading.

<<< 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.
 

Attachments

  • RotaryClub_fixed.zip
    89.3 KB · Views: 1,621
Last edited:

Informatix

Expert
Licensed User
Longtime User
Bad news for english speakers: this is probably the last tutorial I translate because translation takes me hours and it is more than I can afford.
I will post the next tutorials in the french section of this forum.
If someone wants to translate these tutorials, it is more than welcome.
 

Jost aus Soest

Active Member
Licensed User
Longtime User
Great tutorial! :)
And with nice "talking" variable names!:sign0060:

I will miss the english translations! :(
 
Last edited:

thedesolatesoul

Expert
Licensed User
Longtime User
Excellent work Informatix.
I am sure someone else can translate from French, maybe klaus?

btw...did you make PlayerPro? Your work seems to be very detailed.
There is also a pitfall to mention that memory use can become very high with scrollview lists like this.
Great and inspiring work.
EDIT: I forgot to ask my main question.
How do you deal with the OnPaintListener? For my own panels, I used the OnTouch event to change the color when selected, which is a bit clumsy and not the best way. The problem with that happens when you drag your finger away from the panel the color does not change back. How does OnPaintListener work, and how does it return with a state?
 
Last edited:

Informatix

Expert
Licensed User
Longtime User
I didn't see anywhere in the code for how the expansion is handled to bump down all the other items like the original example, or does the expansion just draw over the items below it until closed?

That's done by the CheckList class. The ExtendItem function pushes everything down to create the needed room. That's quick and you don't wait, even with a long list.
 

Informatix

Expert
Licensed User
Longtime User
Excellent work Informatix.
I am sure someone else can translate from French, maybe klaus?
Klaus spends so much time to help us that I cannot ask him to translate my work. There's still the Google Translate solution. Not perfect from french to english, but that's enough to understand the global meaning.

btw...did you make PlayerPro? Your work seems to be very detailed.
Not at all. As a civil servant, I cannot work for a company.
Trying to reproduce the work of someone else is a very good exercise to learn something about programming. That showed me the limits of my classes and libraries, and that's why there are a bunch of new versions since the first tutorial.
That may sound pretentious but my goal is to provide the B4A users with the tools and methods to create any kind of UI. Currently, I estimate that 95% of what you find on the Google Market can be reproduced (games excluded).

There is also a pitfall to mention that memory use can become very high with scrollview lists like this.
Not really. The item images are very small and on a mid-range device like mine, with 32 Mb of heap size, you can display more than 3000 items without hitting an Out of Memory exception (I know it because I'm working on a QuickLoader class to speed up the loading of very long lists).
On the other hand, you will be quickly short on memory if you try to fill the Albums list seen in the second tutorial with more than a hundred of images. The images are so big...

EDIT: I forgot to ask my main question.
How do you deal with the OnPaintListener? For my own panels, I used the OnTouch event to change the color when selected, which is a bit clumsy and not the best way. The problem with that happens when you drag your finger away from the panel the color does not change back. How does OnPaintListener work, and how does it return with a state?

In this tutorial, the ToggleButton is a good example of what you can do to revert to a former state when the finger moves. You should look at the two variants. One of them uses a Panel.

In the CheckList class, the internal functions use a function named PaintBackground to draw the background. They call it every time there's a state change (the user pressed the item, the user expanded it, the user checked the checkbox, etc.). When you set the listener, the event triggered by these functions is redirected to the listener and the class lets you do all the painting. This way, you can do more than painting backgrounds. That's what I show in this tutorial.

Whenever possible, it's better to use the StateListDrawables instead of handling the different states with the Touch listener, because the other Android views expect your view to function in a standard way (your view should give up its pressed state if asked to). But, for the CheckList class, that was too limited, hence the OnPaint and OnTouch management.
 

thedesolatesoul

Expert
Licensed User
Longtime User
Klaus spends so much time to help us that I cannot ask him to translate my work. There's still the Google Translate solution. Not perfect from french to english, but that's enough to understand the global meaning.
I can live with Google Translate. I just need to make sure I keep looking in the French forums.

Not at all. As a civil servant, I cannot work for a company.
In that case maybe we can interest you in providing your services for free! Dont want to waste such talent and skill. :)

That may sound pretentious but my goal is to provide the B4A users with the tools and methods to create any kind of UI. Currently, I estimate that 95% of what you find on the Google Market can be reproduced (games excluded).
In that case there are many many more resources out there for inspiration.
http://mycolorscreen.com/
Design patterns
Mobile UI Patterns | Recently Added
Inspired UI - mobile patterns
lovely ui
Android UI Patterns
(sorry couldnt stop myself)

Not really. The item images are very small and on a mid-range device like mine, with 32 Mb of heap size, you can display more than 3000 items without hitting an Out of Memory exception (I know it because I'm working on a QuickLoader class to speed up the loading of very long lists).
On the other hand, you will be quickly short on memory if you try to fill the Albums list seen in the second tutorial with more than a hundred of images. The images are so big...
Well it depends on the size and number of bitmaps and the heap size. Mine is 16 MB (this is the lowest I know of).

In this tutorial, the ToggleButton is a good example of what you can do to revert to a former state when the finger moves. You should look at the two variants. One of them uses a Panel.
Okay I fixed my issue using help from your code!

Thanks a lot.
 

Informatix

Expert
Licensed User
Longtime User
In that case there are many many more resources out there for inspiration.
http://mycolorscreen.com/
Design patterns
Mobile UI Patterns | Recently Added
Inspired UI - mobile patterns
lovely ui
Android UI Patterns
(sorry couldnt stop myself)

Many, if not all, of these superb design are just nice images put together with a bit of shadow here, a bit of round corners here and a bit of semi-transparency there. That's easy to reproduce. The things get complicated when an interface use a custom view or has an operation not easy to reproduce.
 

thedesolatesoul

Expert
Licensed User
Longtime User
Many, if not all, of these superb design are just nice images put together with a bit of shadow here, a bit of round corners here and a bit of semi-transparency there. That's easy to reproduce. The things get complicated when an interface use a custom view or has an operation not easy to reproduce.
Yes, I am not that great with images so I drool on other peoples designs. But I agree with you.
The UI for this app is pretty incredible: https://play.google.com/store/apps/details?id=com.threebanana.notes
 

Informatix

Expert
Licensed User
Longtime User
Yes, I am not that great with images so I drool on other peoples designs. But I agree with you.
The UI for this app is pretty incredible: https://play.google.com/store/apps/details?id=com.threebanana.notes

I installed Catch on my phone to try it. It asked me for an online account. Ok -> to the bin. What's impressive in it (apart its clean design and nice buttons) ? Does the button wheel turn? (if it turns, that's probably the same principle as the rotary switch I provided in the tutorial file).
 

thedesolatesoul

Expert
Licensed User
Longtime User
I installed Catch on my phone to try it. It asked me for an online account. Ok -> to the bin. What's impressive in it (apart its clean design and nice buttons) ? Does the button wheel turn? (if it turns, that's probably the same principle as the rotary switch I provided in the tutorial file).
I am using it without an account. At the very bottom there is the option to try without signing up. The button wheel is a simple selector. I think the more impressive things is in the animations. When you open a note how the panel expands into a window and the colors change. And going between notes, uses a twitter like 'Pull to go to next note' or something. Its quite nice.
 

Informatix

Expert
Licensed User
Longtime User
I am using it without an account. At the very bottom there is the option to try without signing up. The button wheel is a simple selector. I think the more impressive things is in the animations. When you open a note how the panel expands into a window and the colors change. And going between notes, uses a twitter like 'Pull to go to next note' or something. Its quite nice.

My next tutorial is all about animations. I'll try to include Catch inside.
 

AscySoft

Active Member
Licensed User
Longtime User
This is getting better every day

Thanks for this UI tutorial series. Your skills are impressive. You made earlier an estimation that 95% of G Market apps could be reproduced... wow... (for you maybe)! I have so much to learn. Nice job, including translation from French.
:wav:
 

Mrjoey

Active Member
Licensed User
Longtime User
Hello , tnx for this great library and appreciate ur hardwork , i would like to add the inputslider to a panel instead of activity , and how to call "ISName&"_ValueChanged" for an array of inputslider ? im using this loop for creating the array :
B4X:
    For i = 0 To 8
    isEQFrequency(i).Initialize(Me,"isEQFrequency",Activity,i*40dip, 390dip, 40dip, 200dip)
isEQFrequency(i).SetBackGroundImage(LoadBitmap(File.DirAssets, "slider-back.png"))
isEQFrequency(i).SetPointerImage(LoadBitmap(File.DirAssets, "slider.png"))
isEQFrequency(i).SetPtrSize(28, 0, 20dip, 40dip)
isEQFrequency(i).SetPos(0.5)
Next
 
Last edited:

Mrjoey

Active Member
Licensed User
Longtime User
hello , question about rotaryknob , is there a way to scale the original bitmaps , i mean u are using the images with their original size , but if i want to make the whole knob size to smaller , i used createscaledbitmap when initialising bitmaps but i lost quality and the size was wrong , thank u
 

Informatix

Expert
Licensed User
Longtime User
hello , question about rotaryknob , is there a way to scale the original bitmaps , i mean u are using the images with their original size , but if i want to make the whole knob size to smaller , i used createscaledbitmap when initialising bitmaps but i lost quality and the size was wrong , thank u
In this demo, the first thing is to replace LoadBitmap by LoadScaledBitmap of my Accelerated Surface library. Once done, you can remove the multiplication of bitmap dimensions by the density (the density of the loaded image is left unchanged by the LoadScaledBitmap function so this multiplication is useless).
 
Top