B4A Library [Lib] ScrollView2D

Hello,

Does it need a long explanation ? You can scroll in the two directions.

This lib does not work with Android versions < 2.

v1.01:
I fixed a big bug in the original code.
I added the function SmoothScrollTo.

v1.02:
I restarted from a fresh basis because of the many bugs in the original code. I fixed most of them (now, multitouch events and hardware keys are correctly handled) but a few are left and need more work (sometimes the wrong object get the focus and resizing is not perfectly handled). I noticed by the way that the stock scrollview is bugged and does not set the focus correctly if you move your finger very slowly.
Thanks to Erel, I solved the problem with the B4A documentation.
I added a new function: FullScroll.

v1.03:
I fixed all known bugs (including bugs found in ScrollView & HorizontalScrollView);
I added the scrollbars;
I added three new functions:
- ScrollbarsVisibility
- FadingEdges
- GiveFocusToFirstVisible.

v1.1:
I fixed a problem with events that were not fired if declared in a class;
I added two functions: ScrollingIsFinished and DisableTouchEventInterception.

v1.2:
SV2D appears now as a custom view in the designer with all its properties.

v1.3:
I fixed a bug (SV_2 = SV_1 did not work because the inner panel was declared in the wrong class).

Enjoy,
Fred
 

Attachments

  • ScrollView2D v1.3.zip
    120.5 KB · Views: 5,512
  • Java source - ScrollView2D.zip
    17.6 KB · Views: 1,571
Last edited:

johnaaronrose

Active Member
Licensed User
Longtime User
CallSubDelayed

You should not call MsgBox directly in the touch event. Place it in another sub and call this sub will CallSubDelayed.

I seem to have my app stuck in an infinite loop in the following:
B4X:
Sub acsfMap_Draw (AC As AS_Canvas)
Dim xc, yc, x1, y1, x2, y2, x3, y3 As Int
Msgbox("ScreenX="&MapPoint.ScreenX&",ScreenY="&MapPoint.ScreenY, "acsf_Draw")
If Not(acsfMap.Enabled) Then
  Msgbox("Not drawing "&"acsfMap.Enabled="&acsfMap.Enabled, "acsfMap_Draw")
  Return
End If

Could the cause be due to not using callSubDelayed? I've been trying to find an example of this without success. i did not understand the posts about CallSubDelayed in the Dev Forum. Could you point me to an explanation and/or an example of it?
 

johnaaronrose

Active Member
Licensed User
Longtime User
You should start a new thread because that doesn't concern SV2D.
I'm trying to load a Bitmap (loaded from a .jpg file) into a ScrollView2D with it centralized. It displays OK but is aligned to the top left. The cause looks like the calculated values of sv2dMap.HorizontalScrollPosition & sv2dMap.VerticalScrollPosition are 0 & 0 (rather than expected 590 & 364): see code below:

B4X:
    sv2dMap.Initialize(0, 0, "sv2dMap")
    Activity.AddView(sv2dMap, pnlMap.Left, pnlMap.Top, pnlMap.Width, pnlMap.Height)
    Log("sv2dMap: L,T,W,H = "&sv2dMap.Left&", "&sv2dMap.Top&", "&sv2dMap.Width&", "&sv2dMap.Height)                                  -> 0, 0, 420 & 471
    MapBitmap.Initialize(MapDir, MapFilename)
    sv2dMap.Panel.Width = MapBitmap.Width
    sv2dMap.Panel.Height = MapBitmap.Height
    Log("sv2dMap.Panel: W,H = "&sv2dMap.Panel.Width&", "&sv2dMap.Panel.Height)                                                                            -> 1600 & 1200
    sv2dMap.Panel.SetBackgroundImage(MapBitmap)
    sv2dMap.HorizontalScrollPosition = (sv2dMap.Panel.Width - sv2dMap.Width) / 2
    sv2dMap.VerticalScrollPosition = (sv2dMap.Panel.Height - sv2dMap.Height) / 2
    Log("sv2dMap Initial Scrolling: ScrollH,ScrollV = " & sv2dMap.HorizontalScrollPosition & ", " & sv2dMap.VerticalScrollPosition)  -> 0 & 0
    MapHorizontalScrollPosition = sv2dMap.HorizontalScrollPosition
    MapVerticalScrollPosition = sv2dMap.VerticalScrollPosition
    sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)

I haven't been able to find an example of 'centering' a Bitmap onto a ScrollView2D. Help please!
 

Informatix

Expert
Licensed User
Longtime User
The scroller (in all views) runs in a separate thread, so changing the scroll position is never instant and needs that scrolling events are processed. Thus, when you want to change the scroll position, add a loop with DoEvents just after that checks ScrollingIsFinished. In your example, you set the position but doesn't wait. That cannot work. Second solution: use SmoothScrollTo if you want to scroll smoothly but pass as parameters the computations made, not the value of HorizontalScrollPosition and VerticalScrollPosition.
 

johnaaronrose

Active Member
Licensed User
Longtime User
The scroller (in all views) runs in a separate thread, so changing the scroll position is never instant and needs that scrolling events are processed. Thus, when you want to change the scroll position, add a loop with DoEvents just after that checks ScrollingIsFinished. In your example, you set the position but doesn't wait. That cannot work. Second solution: use SmoothScrollTo if you want to scroll smoothly but pass as parameters the computations made, not the value of HorizontalScrollPosition and VerticalScrollPosition.

Thanks for those solutions. I've included both in my app. Now I get successful centering for Horizontal Scrolling & Vertical Scrolling using coding below:
B4X:
        sv2dMap.Initialize(0, 0, "sv2dMap")
        Activity.AddView(sv2dMap, pnlMap.Left, pnlMap.Top, pnlMap.Width, pnlMap.Height)
        Log("sv2dMap: L,T,W,H = "&sv2dMap.Left&", "&sv2dMap.Top&", "&sv2dMap.Width&", "&sv2dMap.Height)
        MapBitmap.Initialize(MapDir, MapFilename)
        sv2dMap.Panel.Width = MapBitmap.Width
        sv2dMap.Panel.Height = MapBitmap.Height
        Log("sv2dMap.Panel: W,H = "&sv2dMap.Panel.Width&", "&sv2dMap.Panel.Height)
        sv2dMap.Panel.SetBackgroundImage(MapBitmap)
        MapHorizontalScrollPosition = Round2((sv2dMap.Panel.Width - sv2dMap.Width) / 2, 0)
        MapVerticalScrollPosition = Round2((sv2dMap.Panel.Height - sv2dMap.Height) / 2, 0)
        Log("sv2dMap Initial Scrolling: ScrollH,ScrollV = " & MapHorizontalScrollPosition & ", " & MapVerticalScrollPosition)
        sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
        sv2dMap.HorizontalScrollPosition = MapHorizontalScrollPosition
        sv2dMap.VerticalScrollPosition = MapVerticalScrollPosition
        Do While sv2dMap.ScrollingIsFinished = False
            DoEvents
        Loop

In Activity_Pause, I have:
B4X:
    MapHorizontalScrollPosition = sv2dMap.HorizontalScrollPosition
    MapVerticalScrollPosition = sv2dMap.VerticalScrollPosition

In Activity_Resume, I have:
B4X:
    sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
    sv2dMap.HorizontalScrollPosition = MapHorizontalScrollPosition
    sv2dMap.VerticalScrollPosition = MapVerticalScrollPosition
    Do While sv2dMap.ScrollingIsFinished = False
        DoEvents
    Loop
 

Informatix

Expert
Licensed User
Longtime User
In your code:
B4X:
sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
sv2dMap.HorizontalScrollPosition = MapHorizontalScrollPosition
sv2dMap.VerticalScrollPosition = MapVerticalScrollPosition
Do While sv2dMap.ScrollingIsFinished = False
      DoEvents
Loop
...you have to do either:
B4X:
sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
Do While sv2dMap.ScrollingIsFinished = False
      DoEvents
Loop
...or
B4X:
sv2dMap.HorizontalScrollPosition = MapHorizontalScrollPosition
sv2dMap.VerticalScrollPosition = MapVerticalScrollPosition
Do While sv2dMap.ScrollingIsFinished = False
      DoEvents
Loop
...but not both.
 

TheMightySwe

Active Member
Licensed User
Longtime User
Hi Informatix,

I have a "problem" with ScrollView2D, i place some buttons on a ScrollView. And the first and only the first time i click on them, the event get consumed. Next time i click on the button everything works fine.

It's a bit annoying but no biggie.

Do you know if this is a bug or maybe it has noting to do with the ScrollView at all.

/ TMS
 

Informatix

Expert
Licensed User
Longtime User
Hi Informatix,

I have a "problem" with ScrollView2D, i place some buttons on a ScrollView. And the first and only the first time i click on them, the event get consumed. Next time i click on the button everything works fine.

It's a bit annoying but no biggie.

Do you know if this is a bug or maybe it has noting to do with the ScrollView at all.

/ TMS
No, it's not related to ScrollView2D. If you change the EditText by Buttons in the example, you can click on them and get the event without any issue.
 

TheMightySwe

Active Member
Licensed User
Longtime User
Maybe I should use CallSubDelayed on button clicks? And not execute code in the event? I'll try that.
 

johnaaronrose

Active Member
Licensed User
Longtime User
In your code:
B4X:
sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
sv2dMap.HorizontalScrollPosition = MapHorizontalScrollPosition
sv2dMap.VerticalScrollPosition = MapVerticalScrollPosition
Do While sv2dMap.ScrollingIsFinished = False
      DoEvents
Loop
...you have to do either:
B4X:
sv2dMap.SmoothScrollTo(MapHorizontalScrollPosition, MapVerticalScrollPosition)
Do While sv2dMap.ScrollingIsFinished = False
      DoEvents
Loop
...or
B4X:
sv2dMap.HorizontalScrollPosition = MapHorizontalScrollPosition
sv2dMap.VerticalScrollPosition = MapVerticalScrollPosition
Do While sv2dMap.ScrollingIsFinished = False
      DoEvents
Loop
...but not both.
Thanks for that. It worked.
 

DonManfred

Expert
Licensed User
Longtime User
I´m at the moment to implement this lib and i want to ask a small question.

Due to layout the sv2d should be placed on an panel with backgroud-image...

Can i do this without problem (panel.add(sv2d) or MUST the sv2d be added to activity.add(sv2d... ?
 

kongc

Member
Licensed User
Longtime User
Please ignore my last post. I don't know why after a number of attempts I can now finally open the zip file...
 

Brian Robinson

Active Member
Licensed User
Longtime User
Hi,

Just wanted to know if it is possible to find out when the scroll finishes. What I want to be able to do is scroll to a defined position when the user scrolls left or right. I am loading up to 5 panels each measuring the width of the screen and I want to scroll to the next panel when the use swipes across. The scrollview is only on a small part of the screen, so I did not want to use a pager.

I noticed that the IsScrollFinished property was being set to true before the scroll had actually finished.

Has anyone any ideas on how to work out when the scrolling is finished. I was going to use the SmoothScrollTo to do the rest.

Brian
 

Informatix

Expert
Licensed User
Longtime User
I noticed that the IsScrollFinished property was being set to true before the scroll had actually finished.
Do you have an example that shows that ScrollingIsFinished returns True too early? Because, on my devices, it seems to be 100% reliable. Note that your perception of what's the end of a scroll may be very different from the library perception. ScrollingIsFinished is set to True by default and its value will change only when you do a quick gesture, a "fling". In this case, the content may scroll even if you lift your finger, so ScrollingIsFinished is set to False and will be True again only when the "fling" animation has ended. On the contrary, when you move your finger slowly, the library cannot predict whether you'll continue to move your finger or not and, for this reason, as soon the scroll starts, it is considered as ended. There's no "fling" animation and, for your code, ScrollingIsFinished is always True.
The situation that is difficult to evaluate is when the scroll ends after a fling gesture, that's why ScrollingIsFinished is precious.
 

Brian Robinson

Active Member
Licensed User
Longtime User
Hi Informatix,

Thanks for the extra info about the ScrollingIsFinished property. Yes, I understand that while dragging the items in the scrollview would need to handle that property to be true as it would not know whether the user was still scrolling. This could be a problem for one of the things that I want to do, but not related to my original post. I have included an example that gives an idea of what I am trying to do, and where I have put a comment about showing the debugger is where I have put a breakpoint. When this breakpoint is reached, I can see that the scrollview is in fact still scrolling on the device. Maybe this is just related to the debugger as it is hard to tell, as the other scrollchanged sub (commented out) I have defined seems to partially work.

What I am trying to do is work out when the scrolling stops, which of the inside panels we are on, so that I can then make sure only that panel is visible. The next challenge will be allowing the drag. Is it possible to still capture the Touch events to see if the finger has been released?

Thanks for any assistance.
 

Attachments

  • ScrollViewExample.zip
    7.9 KB · Views: 243
Top