Android Tutorial ScrollView example

The ScrollView is a very useful container which allows you to show many other views on a small screen.
The ScrollView holds an inner panel view which actually contains the other views.
The user vertically scrolls the inner panel as needed.

In this example we will load images from the sdcard and add those to a ScrollView.

scrollview1.png


Adding views to a ScrollView is done by calling:
B4X:
ScrollView.Panel.AddView(...)
In order to avoid "out of memory" errors we use LoadBitmapSample instead of LoadBitmap. LoadBitmapSample accepts two additional parameters: MaxWidth and MaxHeight. When it loads a bitmap it will first get the bitmap original dimensions and then if the bitmap width or height are larger than MaxWidth or MaxHeight the bitmap will be subsampled accordingly. This means that the loaded bitmaps will have lower resolution than the original bitmap. The width / height ratio is preserved.

The following code sets the inner panel height and adds an ImageView for each loaded bitmap:
B4X:
    ScrollView1.Panel.Height = 200dip * Bitmaps.Size 'Set the inner panel height according to the number of images.
    For i = 0 To Bitmaps.Size - 1
        Dim iv As ImageView 'create an ImageView for each bitmap
        iv.Initialize("") 'not interested in any events so we pass empty string.
        Dim bd As BitmapDrawable
        bd.Initialize(Bitmaps.Get(i))
        iv.Background = bd 'set the background of the image view.
        'add the image view to the scroll bar internal panel.
        ScrollView1.Panel.AddView(iv, 5dip, 5dip + i * 200dip, ScrollView1.Width - 10dip, 190dip)
    Next
The code that is loading the bitmaps looks for jpg files under /sdcard/Images

If you want to run this program on the emulator you will first need to create this folder and copy some images to it.
This is done with the "adb" command, that comes with Android SDK.
Open a shell console (Windows Start - Run - Cmd).
Go to the sdk tools folder and issue:
B4X:
c:\android-sdk-windows\tools> adb -e shell mkdir /sdcard/Images
The -e parameter tells adb to send the command to the only connected emulator.
The command is mkdir with the name of the folder.
Note that Android file system is case sensitive.

Now we need to copy some images to this folder.
This is done with the push command:
B4X:
adb -e push "C:\temp" /sdcard/Images
This will copy all files under c:\temp to the Images folder.

The emulator is very slow compared to a real device. While on a real device 50 large images load in 2-3 seconds. It can take a long time for the emulator to load a few small images. I recommend you to copy 2 - 3 small images at most.

Also the experience of the ScrollView on the emulator cannot be compared to a real device (with capacitive screen).

The program is available here: http://www.basic4ppc.com/android/files/tutorials/ScrollView.zip
 

gkumar

Active Member
Licensed User
How to remove the views from Panel in Scrollview

Added Scrollview. Added 4 views (Lables) as table format to its default panel, 2 rows and 4 columns, labels as values. But while removing the views gives Null reference exception after removing first 2 counts.

Dim nCount As Int
nCount = Table.NumberOfViews
If(nCount > 0) Then
For i = 0 To nCount
Table.RemoveViewAt(i)
Next
End If

Here after first 2 counts, throws exception. whats wrong here. Is there any other way to clear all the views from Panel in scrollview.
 
Last edited:

gkumar

Active Member
Licensed User
Structure equivalent of C#

Thanks Erel. Its working now.
Another general question I have is, how can I declare structure ("struct") as in C# in B4A?
 

gkumar

Active Member
Licensed User
Line between the rows

How I can add the line between each row in the panel inside scrollview?
 

sterlingy

Active Member
Licensed User
I was able to get this sample to work perfectly on my device. I added a RadioButton to the ScrollView panel, which worked.

Then I merged this code with my app, and I don't get any errors, but I don't see the RadioButton.

The ScrollView Panel is not on the "main" view, but an another view called pnlPage2.

If, I attach the view to Activity, then I see it on all pages.

Any thoughts?

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

    Dim sp As SoundPool
    Dim waterDrop, bellSound, buzzerSound As Int
   
   Dim hc As HttpClient
   Dim Question_LIST, user_LIST As Int

End Sub

Sub Globals
   Dim pnlPage1, pnlPage2, pnlPage3 As Panel      ' Declares the three panels

   Dim pnlToolbox, pnlAnswerGroup, pnlAnsResult As Panel
   Dim btnResult, btnSubmitAns As Button
   Dim lblAnsResult, lblQuestion As Label
   
   Dim CurrentQuestionNumber As Int: CurrentQuestionNumber = 0
   Dim chosenAnsBtn As Int: chosenAnsBtn = 0
   
   Dim radioBtnAnswer1, radioBtnAnswer2, radioBtnAnswer3, radioBtnAnswer4 As RadioButton
   
   Dim ScrollView1 As ScrollView
   
Dim RadioButton1 As RadioButton
   
   Dim anim1 As Animation
   
   Type typeQuestion( _
           Question As String, correctAnswer As String, _
           Answer1 As String, Answer2 As String, Answer3 As String, Answer4 As String)
           
   Dim currentQuestion As typeQuestion
   
   Dim listQuestions As List
   listQuestions.Initialize
   
   
   Dim correctAnsBMP As Bitmap
   correctAnsBMP.Initialize(File.DirAssets,"greenResultBKG.png")
   Dim wrongAnsBMP As Bitmap
   wrongAnsBMP.Initialize(File.DirAssets,"wronganswer.png")
   
End Sub

Sub Activity_Create(FirstTime As Boolean)

    If FirstTime Then
      hc.Initialize("hc")
      
      pnlAnswerGroup.Initialize(pnlAnswerGroup)
      RadioButton1.Initialize(RadioButton1)
      
        sp.Initialize(4)
        waterDrop = sp.Load(File.DirAssets, "WaterDrop.mp3")
      bellSound = sp.Load(File.DirAssets, "bell.mp3")
      buzzerSound = sp.Load(File.DirAssets, "buzzer.mp3")
    End If
   
RadioButton1.Text = "And now for a message from our sponsors. Buy Coke, you'll rot your teeth out in less than two months. Just think of the advantages to your life. You will never have to brush your teeth again, and that will save you four minutes a day!"   
   
   Activity.LoadLayout("Main")                           ' Loads "Main" layout file
   FetchQuestionsList      
                                                                     
   pnlPage1.Initialize("")                              ' Initializes pnlPage1
   pnlPage1.LoadLayout("Page1")                        ' Loads "Page1" layout file
   Activity.AddView(pnlPage1,0,0,100%x,100%y)               ' Adds pnlPage1
   pnlPage1.Visible=True                              ' Sets pnlPage1 to Visible
   
   pnlPage2.Initialize("")                              ' Initializes pnlPage2
   pnlPage2.LoadLayout("Page2")                        ' Loads "Page2" layout file
   Activity.AddView(pnlPage2,0,0,100%x,100%y)               ' Adds pnlPage2
   pnlPage2.Visible=False                              ' Sets pnlPage1 to invisible
   
   pnlPage3.Initialize("")                              ' Initializes pnlPage3
   pnlPage3.LoadLayout("Page3")                        ' Loads "Page3" layout file
   Activity.AddView(pnlPage3,0,0,100%x,100%y)               ' Adds pnlPage3
   pnlPage3.Visible=False                              ' Sets pnlPage1 to Visible
   
   Activity.AddMenuItem("Login","Page1")                  ' Adds menu item mnuPage1
   Activity.AddMenuItem("Exercises","Page2")               ' Adds menu item mnuPage2
   Activity.AddMenuItem("Lessons","Page3")                  ' Adds menu item mnuPage3

   ScrollView1.Panel.AddView(RadioButton1,0,0,-2,-2)
   RadioButton1.BringToFront
   
End Sub

:BangHead: Sterling
 

Erel

B4X founder
Staff member
Licensed User
First you should move this code out of the "If FirstTime Then" condition:
B4X:
pnlAnswerGroup.Initialize(pnlAnswerGroup)
        RadioButton1.Initialize(RadioButton1)
        sp.Initialize(4)
Otherwise your UI will not appear correct when the activity is recreated (when the user rotates the device for example).

About the RadioButton. Try to explicitly set the width and height and see whether is makes any difference.
 

sterlingy

Active Member
Licensed User
I moved the lines, as you suggested, but did not get a different result. I am attaching a stripped down version of my code. Most of the functionality is missing.

You will notice at the end of the Activity_Create routine, two lines:
B4X:
'   Activity.AddView(RadioButton1,0,0,-2,-2)
   ScrollView1.AddView(RadioButton1,0,0,-2,-2)

If you uncomment the first line, and comment the second, you will see that the RadioButton will attach to the activity. The goal is to get it to attach to the panel of the ScrollView.

B4X:
'Activity module
Sub Process_Globals
   'These global variables will be declared once when the application starts.
   'These variables can be accessed from all modules.

    Dim sp As SoundPool
    Dim waterDrop, bellSound, buzzerSound As Int
   
   Dim hc As HttpClient
   Dim Question_LIST, user_LIST As Int
   Question_LIST = 1
   user_LIST = 2

End Sub

Sub Globals
   Dim pnlPage1, pnlPage2, pnlPage3, pnlProfile As Panel      ' Declares the three panels

   Dim pnlToolbox, pnlAnswerGroup, pnlAnsResult As Panel
   Dim btnResult, btnSubmitAns As Button
   Dim lblAnsResult, lblQuestion As Label
   
   Dim lblProfileUsername, lblProfileName, lblProfileSchool, lblProfileHomeroom, lblProfileGrade, lblProfileMSOLevel As Label
   
   Dim CurrentQuestionNumber As Int: CurrentQuestionNumber = 0
   Dim chosenAnsBtn As Int: chosenAnsBtn = 0
   
   Dim radioBtnAnswer1, radioBtnAnswer2, radioBtnAnswer3, radioBtnAnswer4 As RadioButton
   
   Dim ScrollView1 As ScrollView
   
   Dim edtxtUserName As EditText
   Dim edtxtPassword As EditText
   
Dim RadioButton1 As RadioButton
   
   Dim anim1 As Animation
   
   Type typeQuestion( _
           Question As String, correctAnswer As String, _
           Answer1 As String, Answer2 As String, Answer3 As String, Answer4 As String)
           
   Type typeProfile( _
           userID As Int, userName As String, firstName As String, _
           lastName As String, school As String, schoolGrade As String, msoLevel As String, homeroom As String)
           
   Dim currentQuestion As typeQuestion
   Dim userProfile As typeProfile 
   
   Dim listQuestions As List
   listQuestions.Initialize
   
   
   
   Dim correctAnsBMP As Bitmap
   correctAnsBMP.Initialize(File.DirAssets,"greenResultBKG.png")
   Dim wrongAnsBMP As Bitmap
   wrongAnsBMP.Initialize(File.DirAssets,"wronganswer.png")
   
   Dim btnLoginTryAgain As Button
   Dim pnlLoginFailed As Panel
End Sub

Sub Activity_Create(FirstTime As Boolean)

    If FirstTime Then
      hc.Initialize("hc")
      
        sp.Initialize(4)
        waterDrop = sp.Load(File.DirAssets, "WaterDrop.mp3")
      bellSound = sp.Load(File.DirAssets, "bell.mp3")
      buzzerSound = sp.Load(File.DirAssets, "buzzer.mp3")
    End If
   
         pnlAnswerGroup.Initialize(pnlAnswerGroup)
      RadioButton1.Initialize(RadioButton1)
   
RadioButton1.Text = "And now for a message from our sponsors. Buy Coke, you'll rot your teeth out in less than two months. Just think of the advantages to your life. You will never have to brush your teeth again, and that will save you four minutes a day!"   
   
   Activity.LoadLayout("Main")                           ' Loads "Main" layout file
'   FetchQuestionsList      
                                                                     
   pnlPage1.Initialize("")                              ' Initializes pnlPage1
   pnlPage1.LoadLayout("Page1")                        ' Loads "Page1" layout file
   Activity.AddView(pnlPage1,0,0,100%x,100%y)               ' Adds pnlPage1
   pnlPage1.Visible=True                              ' Sets pnlPage1 to Visible
   
   pnlPage2.Initialize("")                              ' Initializes pnlPage2
   pnlPage2.LoadLayout("Page2")                        ' Loads "Page2" layout file
   Activity.AddView(pnlPage2,0,0,100%x,100%y)               ' Adds pnlPage2
   pnlPage2.Visible=False                              ' Sets pnlPage1 to invisible
   
   pnlPage3.Initialize("")                              ' Initializes pnlPage3
   pnlPage3.LoadLayout("Page3")                        ' Loads "Page3" layout file
   Activity.AddView(pnlPage3,0,0,100%x,100%y)               ' Adds pnlPage3
   pnlPage3.Visible=False                              ' Sets pnlPage1 to invisible
   
   pnlProfile.Initialize("")                           ' Initializes pnlPage3
   pnlProfile.LoadLayout("profile")                     ' Loads "profile" layout file
   Activity.AddView(pnlProfile,0,0,100%x,100%y)            ' Adds pnlProfile
   pnlProfile.Visible=False                           ' Sets pnlPage1 to invisible
   
   Activity.AddMenuItem("Login","Page1")                  ' Adds menu item mnuPage1
   Activity.AddMenuItem("Exercises","Page2")               ' Adds menu item mnuPage2
   Activity.AddMenuItem("Lessons","Page3")                  ' Adds menu item mnuPage3
   Activity.AddMenuItem("Profile","profile")

'   Activity.AddView(RadioButton1,0,0,-2,-2)
   ScrollView1.AddView(RadioButton1,0,0,-2,-2)
   RadioButton1.BringToFront
   
End Sub

Sub Page1_Click
   pnlPage2.Visible = False                              ' Hides pnlPage2
   pnlPage3.Visible = False                              ' Hides pnlPage3
   pnlPage1.Visible = True                                 ' Sets pnlPage1 to Visible
   pnlProfile.Visible = False
End Sub

Sub Page2_Click
   pnlPage1.Visible = False                              ' Hides pnlPage1
   pnlPage3.Visible = False                              ' Hides pnlPage3
   pnlPage2.Visible = True                                 ' Sets pnlPage2 to Visible
   pnlProfile.Visible = False
End Sub

Sub Page3_Click
   pnlPage1.Visible = False                              ' Hides pnlPage1
   pnlPage2.Visible = False                              ' Hides pnlPage2
   pnlPage3.Visible = True                                 ' Sets pnlPage3 to Visible
   pnlProfile.Visible = False
End Sub

Sub profile_Click
   pnlPage1.Visible = False                              ' Hides pnlPage1
   pnlPage2.Visible = False                              ' Hides pnlPage2
   pnlPage3.Visible = False                              ' Sets pnlPage3 to Visible
   pnlProfile.Visible = True
End Sub


Sub btnEnterSchool_Click

   Log(sp.Play(waterDrop, 1, 1, 1, 0, 1))   
   pnlPage1.Visible = False                              ' Hides pnlPage1
   pnlPage2.Visible = True                              ' Shows pnlPage2
   pnlPage3.Visible = False                              ' Sets pnlPage3 to Visible
   pnlProfile.Visible = False
   
'   checkUser
   
End Sub

Sub radioBtnAnswer_Click
   Dim Send As Button
   
   Send = Sender
   
   Log(sp.Play(waterDrop, 1, 1, 1, 0, 1))
   
   radioBtnAnswer1.Checked = False
   radioBtnAnswer2.Checked = False
   radioBtnAnswer3.Checked = False
   radioBtnAnswer4.Checked = False
   
   Select Send.Tag
   Case "1"
      radioBtnAnswer1.Checked = True
      chosenAnsBtn = 1
   Case "2"
      radioBtnAnswer2.Checked = True
      chosenAnsBtn = 2
   Case "3"
      radioBtnAnswer3.Checked = True
      chosenAnsBtn = 3
   Case "4"
      radioBtnAnswer4.Checked = True
      chosenAnsBtn = 4
   End Select   
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub
 

sterlingy

Active Member
Licensed User
About the RadioButton. Try to explicitly set the width and height and see whether is makes any difference.

Erel,
When I tried to explicitly set the width and height, I got java.lang.NullPointerException

my code is something like this:

B4X:
RadioButton1.Text = "Blah, blah, blah"   

RadioButton1.Width = 200
RadioButton1.Height = 50

ScrollView1.Panel.AddView(RadioButton1,0,0,200,50)
RadioButton1.BringToFront

I've noticed some other odd things. changing parameters in code, such as color or the Z-Depth (sendToBack or BringToFront) doesn't have any affect on the ScollView1 when the app is running.

In the Designer, I set the ScrollView1 width to 300 and the Height to 260, but when I am in Debug mode, I can see that the width and height are 480x570.

Trying to change the width and height of the ScrollView1 in code results in the same error I get with the RadioButton1

-Sterling
 

sterlingy

Active Member
Licensed User
Erel,

I solved the button attachment to the scrollview problem. I'm not sure why it happens, but the solution was to NOT create the ScrollView in the Designer, as you did in your example.

Here's what I did:

B4X:
        ScrollView1.Panel.Height = 500dip
   ScrollView1.Panel.AddView(RadioButton1,5dip,10dip,-2,-2)
   ScrollView1.RemoveView
   pnlPage2.AddView(ScrollView1,10dip,110dip,300dip,250dip)
   ScrollView1.SendToBack

:icon_clap: -Sterling
 

johnaaronrose

Active Member
Licensed User
Scrolling Label

I have a Label that I want to be able to scroll on (vertically only). I have a some newbie questions. Can I create the necessary (?) Scrollview in the Designer and create the Label (that fills the Scrollview) in the Designer with parent Scrollview? IF not, what's the simplest way of going about this?

PS I've looked at the examples for LongText etc & they haven't given answers to my questions.
 

klaus

Expert
Licensed User
You can create the ScrollView in the Designer.
You can also create a separate layout in the Designer and load this layout file onto the ScrollView.Panel. If you need views positioned out of the screen you could define a Panel with dimensions higher than the screen and add views to this panel and load the layout file onto the ScrollView.Panel.
You cannot define a Label or any view in the Designer and set its parent view being the ScrollView.
In most cases it's easier to add the views to the ScrollView.Panel by code.

Best regards.
 

johnaaronrose

Active Member
Licensed User
Text does not fully scroll

Klaus,

Thank you for your helpful replies. I've now created (in the Designer) ScrollViewDescription. I have code to create LabelDescription in the Panel of ScrollViewDescription. The text is displayed in LabelDescription and it scrolls to reveal some of the text but not all and the last line of text displayed only shows its top half: see below for details. The appropriate code is shown below:

Dim LabelDescription As Label
Log("LabelDescription view added")
LabelDescription.Initialize("")
Log("LabelDescription view initialized")
ScrollViewDescription.Panel.AddView(LabelDescription, 5dip, 5dip, ScrollViewDescription.Width-10, ScrollViewDescription.Height-10)
LabelDescription.Text = CursorSite.GetString("description")

The first paragraph of the text in LabelDescription is:
The Rue des Rosiers, which means "street of the rosebushes", is a street in the 4th arrondissement of Paris France. It begins at the Rue Mahler and proceeds west-northwestward across the Rue Pavée, the Rue Ferdinand Duval, the Rue des Écouffes, and the Rue des Hospitalières-Saint-Gervais, before it ends at the Rue Vieille du Temple. The Rue des Rosiers lies at the center of the Jewish quarter unofficially called "the Pletzl" (Yiddish for "little place"). Officially, this street is in the Marais district, which extends along the Rue de Rivoli a short distance away, and some refer to the area as "Saint Paul" because of the proximity of the Place saint-Paul.

The last words shown which are readable are "Rue Mahler".

PS CursorSite is a cursor defined against the site table. The description column in the site table is defined as type Text. All other columns from CursorSite display OK (e.g. site name taken from a Text column shown in a Label, image taken from a Blob column shown in an ImageView).
 

klaus

Expert
Licensed User
This line:
B4X:
ScrollViewDescription.Panel.AddView(LabelDescripti on, 5dip, 5dip,  ScrollViewDescription.Width-10, ScrollViewDescription.Height-10)
should be
B4X:
ScrollViewDescription.Panel.AddView(LabelDescripti on, 5dip, 5dip,     ScrollViewDescription.Width-10dip, ScrollViewDescription.Height-10dip)
You should use dip units instead of pur pixels.

Then you need to adapt the ScrollViewDescription.Panel.Height to the Labels height using MeasureMultilineTextHeight from the StringUtils library.
You can find an example here.

If you want I can also answer in french.

Best regards.
 

johnaaronrose

Active Member
Licensed User
StrUtil Compile error

Klaus,

Thank you for your very helpful reply. It was useful to know that the Label view's height & width could be the same as the ScrollView's width & height: I'd assumed that the 'scroll icon/bar' would take up room in the ScrollView. So I've done that.

When I try to compile, I get a problem with StrUtil as below:
Error description: Undeclared variable 'strutil' is used before it was assigned any value.
Occurred on line: 96
ht = StrUtil.MeasureMultilineTextHeight(LabelDescription, txt) ' measure Label height

I previously had StringUtils v1.02 ticked (it must have been because I was using another module e.g. httputils2, dbutils). I've tried reloading StringUtils but no change. I'm obviously missing something elementary (being a newbie). Any idea?
 

klaus

Expert
Licensed User
It was useful to know that the Label view's height & width could be the same as the ScrollView's width & height:
Not really.
The real Label.Height is defined by the Label.Width and the text displayed in it.
Then yo must set the ScrollView.Panel.Height equal to the Label.Top + Label.Height.
You need to include Label.Top if it's not equal to 0.
You could also add a small margin for the bottom.

You need to declare StrUtil in Globals:
B4X:
Dim StrUtil As StringUtils
Best regards.
 

johnaaronrose

Active Member
Licensed User
Scrolling text & photo

Klaus,

Another feature I would like is to display the text (i.e that which is currently vertically scrollable as per your LongTextSimple example) on a full screen. I have some questions on this:

1. I could use a button for it but I thought that it would better to associate an event with this: if so which one would not interfere with the scrolling?

2. Would you recommend using a panel in the existing layout file or creating a new activity & associated new layout file for this?

3. I also have an ImageView on the existing layout file: this Image View will contain one of a number of photos (of varying resolutions). Which example of yours would you recommend to include both vertical & horizontal scrolling for this?

4. Same questions as for numbers 1 & 2 for the photo?
 

klaus

Expert
Licensed User
Sorry but I don't really understand what exactly you want to do.

Do you have a screen where you display information and from there you want to display details, the long text or images on other screens?
Do you have an example ?

It would be better to start a new thread for this question because it's not really related to the title of this thread.

Best regards.
 
Top