The code below is an example of one method for creating a virtual listview. The maximum memory used for the largest dataset which is 5000 strings is < 3mb
Use all the paging keys to experiment with the scrolling. Use the rw (that circle-arrow key) to change data sets. It is ROKU 3 HI-def so if you are using anything else
your on your own. I double display the data horizontally so that you can see how fast it is even with large strings. The l_scrollSpeed value puts some brakes on. Please
let me know its weaknesses, mistakes, don't think that you are offending me. I'm of average intelligence , I makes mistakes, and I don't always have the best
solution. But I try hard and I always get it right eventually (esp when someone helps) Just copy the code into a file an run it. No server or resources are used. Add
or change the testing values and make sure the scrolling is correct.
Function Main() As Void
' Create the Global channel object and initialize. This must always be done first
m.APP = NewChannel()
m.APP.Initialize()
' Open the ReadBook module.
' Ideally, ReadBook should itself be an instantiated object
' Create, Implemented, and Destroyed
ReadBook()
' Good habit to get into needed or not (at least not here)
m.APP.Clear()
m.APP = Invalid
End Function
' ASSERT is used only to short-circuit my own exception handler
' It is only meant for development testing and example code, NOT production
' It would be impossible to bring in my entire framework for one example
' Exception handling is your framework's job. The listview expects data
' to be in order. It does some minor checking for you but that is it
Function ASSERT(a_boolean As Boolean, l_object = "" As String ) As Void
if not a_boolean
print "=========================="
print l_object+" Failed ASSERT"
print "=========================="
stop
end if
End Function
' ************** APPLICATION OBJECT ***************************
' I use this to emulate my own environment. To make is easier for me to
' Bring in code from my own production application. It is not meant
' As a template. My own channel object controls theme (color, fonts, overhang composite)
' exception handling, loading screens with various wait cursors and progress bars
' the log viewer and message boxes
Function NewChannel() As Object
nc = CreateObject("roAssociativeArray")
' I use one roScreen swapping in/out composites to emulate stacked screens
' and one port as I employ the roSystemLog component.
nc.Screen = Invalid
nc.Port = Invalid
nc.Device = Invalid
nc.FontRegistry = Invalid
nc.Font20 = Invalid
nc.Font28 = Invalid
nc.DisplayWidth = 0
nc.DisplayHeight = 0
' Default Colors
nc.Transparent = &h00000000
nc.ScrBkgClr = &hE0DFDFFF
nc.DlgBkgClr = &hCECECEFF
nc.ItemTxtClr = &h383838FF
' Remote (you can use bslCore for these if you like)
nc.kp_BK = 0
nc.kp_UP = 2
nc.kp_DN = 3
nc.kp_LT = 4
nc.kp_RT = 5
nc.kp_OK = 6
nc.kp_RW = 7
nc.kp_REV = 8
nc.kp_FWD = 9
nc.kp_INFO = 10
nc.Initialize = channel_initialize
' can include interface to create the default
' roCompositor with your overhang at the top
return nc
End Function
Function channel_initialize() As Void
m.Port = CreateObject("roMessagePort")
m.Device = CreateObject("roDeviceInfo")
m.FontRegistry = CreateObject("roFontRegistry")
m.Font20 = m.FontRegistry.GetDefaultFont(20, False, False)
ASSERT(type(m.Font20) = "roFont", "m.Font20")
m.Font28 = m.FontRegistry.GetDefaultFont(28, False, False)
ASSERT(type(m.Font28) = "roFont", "m.Font28")
m.Screen = CreateObject("roScreen", True)
ASSERT(type(m.Screen) = "roScreen", "m.Screen")
l_wh = m.Device.GetDisplaySize()
m.DisplayWidth = l_wh.w
m.DisplayHeight = l_wh.h
m.Screen.SetPort(m.Port)
m.Screen.Clear(m.ScrBkgClr)
m.Screen.SwapBuffers()
End Function
'****************** END PPLICATION OBJECT ************************************
'****************** BEGIN READER MODULE ************************************
' Testing module for the example. My own modules are actually objects that contain
' an roCompositior. They are created, implemented and destroyed as the user navigates
' the main menu. For me this is the most efficient way to manage memory using the API
Function ReadBook() As Void
' Create the composite for this module. Set it to the applications screen
' and default color. Ideally your application object should create a compositor
' for you with your overhang already set at zorder 0 eg Compositor = m.APP.NewCompositor()
Compositor = CreateObject("roCompositor")
Compositor.SetDrawTo(m.APP.Screen, m.APP.ScrBkgClr)
' Create the listview passing it the compositor, an ideal page size
' and the zorder to place the listview sprite
Viewer = NewVListView(Compositor, 2)
l_index = 0
l_lastKey = -1
l_scrollSpeed = 120
l_kp_BK = m.APP.kp_BK
l_kp_UP = m.APP.kp_UP
l_kp_DN = m.APP.kp_DN
l_kp_LT = m.APP.kp_LT
l_kp_RT = m.APP.kp_RT
l_kp_OK = m.APP.kp_OK
l_kp_RW = m.APP.kp_RW
l_kp_REV = m.APP.kp_REV
l_kp_FWD = m.APP.kp_FWD
l_running = True
l_port = m.APP.Port
' Create some test data
l_test_values = [
[305, 10],
[1000, -10],
[105, 2],
[45, 167],
[3000, 20],
[68, 27],
[143, 27],
[5000,1027],
]
l_test_count = l_test_values.Count()
l_test_index = 0
l_test = l_test_values[l_test_index]
' Lets start with 3000 data items, at a 20 row page size
Viewer.SetList( GetData(l_test[0]), l_test[1] )
l_x = int(m.APP.DisplayWidth / 2 - Viewer.GetWidth() / 2)
l_y = int(m.APP.DisplayHeight / 2 - Viewer.GetHeight() / 2)
Viewer.MoveTo(l_x, l_y, False)
Viewer.Show()
while(l_running)
l_msg = wait(l_scrollSpeed, l_port)
if type(l_msg) = "roUniversalControlEvent"
l_index = l_msg.GetInt()
l_lastKey = l_index
print l_index
if l_index = l_kp_RW
l_test_index = l_test_index + 1
if l_test_index >= l_test_count then l_test_index = 0
l_test = l_test_values[l_test_index]
Viewer.SetList( GetData(l_test[0]), l_test[1] )
l_x = int(m.APP.DisplayWidth / 2 - Viewer.GetWidth() / 2)
l_y = int(m.APP.DisplayHeight / 2 - Viewer.GetHeight() / 2)
Viewer.MoveTo(l_x, l_y, False)
Viewer.Show()
else if l_index = l_kp_UP or l_index = l_kp_DN
Viewer.ScrollRow(l_index = l_kp_DN)
else if l_index = l_kp_LT or l_index = l_kp_RT
l_scrollSpeed = 300
Viewer.ScrollRow(l_index = l_kp_RT, 0)
else if l_index = l_kp_FWD or l_index = l_kp_REV
l_scrollSpeed = 220
Viewer.ScrollPage(l_index = l_kp_FWD)
else if l_index > 100 ' All key ups are value + 100
l_lastKey = -1
l_scrollSpeed = 120
else if l_index = l_kp_BK
l_running = False
end if
else
if l_lastKey = l_kp_UP or l_lastKey = l_kp_DN
l_scrollSpeed = 5
Viewer.ScrollRow(l_lastKey = l_kp_DN, 6)
else if l_lastKey = l_kp_LT or l_lastKey = l_kp_RT
l_scrollSpeed = 5
Viewer.ScrollRow(l_lastKey = l_kp_RT, 0)
else if l_lastKey = l_kp_FWD or l_lastKey = l_kp_REV
l_scrollSpeed = 150
Viewer.ScrollPage(l_index = l_kp_FWD)
end if
end if
end while
End Function
'****************** END READER MODULE ************************************
' ************** BEGIN LISTVIEW OBJECT ***************************
' Virtual listview is an abbreviated, object extracted in part from my production code
' The engine is a more simplifed form of virtual management that I am experimenting with.
' This one uses index offsetting
' This example expects a one-dimensional array of strings. You can override or modify it to present
' data in a much richer format. My devlopment channel is called New Man Living and is dedicated to
' my Lord and Savior Jesus Christ whom I love with all my heart, soul and mind. He is the very breath of
' my life. It consists of all custom audio, video and e-book modules. The e-book module employs a form
' of the listview that contains rich-text features. I would like to bring back some of the out-of-print
' writings of what I consider 'true' men of God. It is quite doable to create a very rich, very fast reader
' using a virtual type of viewer.
' -- To God the Eternal Father be Glory and Honor and Power through Jesus Christ forever - John
' Hopefully I have extracted everything here. If you can make it better please do so, I'm not the brightest
' cog in the wheel. If there are any problems
' that you come across let me know so I could fix them. I'm a very busy guy so I quickly put together things
' If something works great in my environment for the purpose it was intended I leave it alone. This does not
' mean it is totally bug proof. You may use it for something different and discover other things I did not
' anticipate. Please share your improvements with the rest of us who use the API - all four of us
Function NewVListView(a_compositor As Object, a_zOrder As Integer) As Object
lv = CreateObject("roAssociativeArray")
' Holds a reference to the application object
' If you don't use one just replace all ref members with your own
' stuff - fonts, colors just look at it above
lv.APP = m.APP
' A reference to the one and only roScreen
lv.Screen = m.APP.Screen
' A reference to the implementor's compositor
lv.Compositor = a_compositor
' The Zorder to place the listview
lv.ZOrder = a_zOrder
' Make sure they are valid - see the ASSERT method
ASSERT(type(lv.Compositor) = "roCompositor", "lv.Compositor")
ASSERT(type(lv.Screen) = "roScreen", "lv.Screen")
' Holds a reference to your data container. In this example it must be
' an roArray of Strings. If you change this, obviously you need to change other interface
lv.Data = Invalid
lv.DataCount = 0
lv.DataIndex = 0
' The paging information. I don't believe I implement all members in this example
' I'm mostly trying to express the virtual aspect rather than anything else
lv.PageSize = 0 ' used
lv.PageHeight = 0 ' used
lv.PageNumber = 0
lv.TotalPages = 0
' datacount mod pagesize > 0
lv.IsPartialPage = False
' Flag to alert me that the user has changed scrolling directions
lv.IsPagingDown = True
' Total row height includes the linespace + onelineHeight of the font
' These are fixed I already have over 700 lines to copy into the code post
lv.RowHeight = 0
lv.LineSpace = 0
' The bitmap that will buffer the data. The bufferbitmap must not exceed the size of a bitmap
' However, your datalists can be as long as the limits imposed by the framework. You will see
' that this is more than you will ever need
lv.BufferBitmap = Invalid
lv.BufferRegion = Invalid
lv.BufferSprite = Invalid
' Always dynamically calculated
lv.BufferWidth = 0
lv.BufferHeight = 0
' Holds the virtual row index which can change because of how setwrap works. Picture a magnifying glass
' Going up and down over a stationary piece of paper. The paper stays still but what is in focus changes
' And so does the virtual row, which makes this implementation far more difficult
lv.VirtualRowIndex = 0
' Default Fonts are fixed for this example.
lv.TextFont = m.APP.Font20
' Default Colors
lv.BkgClr = m.APP.ScrBkgClr
lv.TextClr = m.APP.ItemTxtClr
' PUBLIC
lv.SetList = list_view_setlist
lv.Show = list_view_show
lv.Hide = list_view_hide
lv.MoveTo = list_view_moveto
lv.GetWidth = list_view_getwidth
lv.GetHeight = list_view_getheight
lv.ScrollRow = list_view_scrollrow
lv.ScrollPage = list_view_scrollpage
' PROTECTED
lv.DrawRow = list_view_drawrow
lv.DrawPage = list_view_drawpage
lv.DrawAll = list_view_drawall
lv.Reset = list_view_reset
return lv
End Function
' Releases any sprite memory and resets all members to default
Function list_view_reset() As Void
if type(m.BufferSprite) = "roSprite" then m.BufferSprite.Remove()
m.Data = Invalid
m.DataCount = 0
m.DataIndex = 0
m.PageSize = 0
m.PageHeight = 0
m.PageNumber = 0
m.TotalPages = 0
m.IsPagingDown = True
m.IsPartialPage = False
m.RowHeight = 0
m.LineSpace = 0
m.BufferBitmap = Invalid
m.BufferRegion = Invalid
m.BufferSprite = Invalid
m.BufferWidth = 0
m.BufferHeight = 0
m.VirtualRowIndex = 0
return
End Function
' See notes on ASSERT above. Generally components should be insulated from checking
' your data for you. The implementor should guarantee that the data is acceptable for use
' In this example an roArray of type String is expected. The caller sends a data list
' and a desired page size
Function list_view_SetList(a_data As Object, a_pageSize As Integer) As Boolean
' Release resources and re-initialize
m.Reset()
' Assign the a_data reference to the m.Data member
ASSERT(type(a_data) = "roArray", "a_data")
m.Data = a_Data
' Get the data count and save it, must be at least one
m.DataCount = m.Data.Count()
ASSERT(m.DataCount > 0, "m.DataCount")
' Determine actual page size. It cannot be 0, or greater than the data count,
' nor can it be the same size as the datacount (not in this example too much code, all stripped out)
' Though I do allow for (1) single row/item for a scrolling vertical banner. My version has a non-virtual
' Interface as well and permits the same page size as the datacount.
' Guaranteed to have at least one data item and a one-row page
m.PageSize = a_pageSize
if m.PageSize >= m.DataCount then m.PageSize = m.DataCount - 1
if m.PageSize <= 0 then m.PageSize = 1
' Get row height = onelneheight + linespace
' The font and linespacing are fixed, private members in this example. This is only
' to keep the example short. I am demonstrating one way to implement a virtual engine
' not how to create a rich-text reader. I will share the code to my own reader
' when I have perfected it.
m.LineSpace = 6
m.RowHeight = m.TextFont.GetOneLineHeight() + m.LineSpace
' Finally, determine the page height, I do not allow it to exceed the display height
' not only because it's not necessary, it also protects the bitmapbuffer from exceeding its own limits
m.PageHeight = m.PageSize * m.RowHeight
l_maxHeight = m.APP.DisplayHeight
' Make adjustments if needed. Calculate max num rows and resize
if m.PageHeight > l_maxHeight
m.PageSize = int(l_maxHeight / m.RowHeight) - 2
m.PageHeight = m.PageSize * m.RowHeight
end if
m.PageNumber = 0
' Total number of pages - I dont use them in this example
m.TotalPages = int(m.DataCount / m.PageSize)
l_diff = m.DataCount MOD m.PageSize
m.IsPartialPage = l_diff > 0
if m.IsPartialPage then m.TotalPages = m.TotalPages + 1
' From the above calculations we arrive at the buffers base height, and to this add the virtual row
m.BufferHeight = m.PageHeight + m.RowHeight
' Now for the width
' Determine the buffer width, limited to the display width
l_maxWidth = m.APP.DisplayWidth
m.BufferWidth = 0
for each l_str in m.Data
ASSERT(type(l_str) = "String", "String")
l_w = m.TextFont.GetOneLineWidth(l_str, l_maxWidth)
if m.BufferWidth < l_w then m.BufferWidth = l_w
end for
' Can ASSERT the buffer side if you wish. But the above code pretty much resolves an over-buffer
' I have had failures with slightly < 2048 so 2000 is a safer bet
ASSERT(m.BufferHeight < 2000, "m.BufferHeight = "+m.BufferHeight.ToStr())
ASSERT(m.BufferWidth < 2000, "m.BufferWidth = "+m.BufferWidth.ToStr())
' Finally, create the bitmap buffer
m.BufferBitmap = CreateObject("roBitmap", {width: m.BufferWidth, height: m.BufferHeight , AlphaEnable: False})
ASSERT(type(m.BufferBitmap) = "roBitmap", "m.BufferBitmap")
' Create the viewable page region, I remove the end linespace
' for a more even bottom border when scrolling. The width of the region
' is the same as the bitmap.
l_height = m.PageHeight - m.LineSpace
m.BufferRegion = CreateObject("roRegion", m.BufferBitmap, 0, 0, m.BufferWidth, l_height)
ASSERT(type(m.BufferRegion) = "roRegion", "m.BufferRegion")
' Set the regions wrap to true for scrolling. This is where the difficulty comes in
m.BufferRegion.SetWrap(True)
' Create the listview components sprite at 0,0 pos on the screen and hide it for now (zOrder < 0)
' The Interface members show, moveto, will handle this later
m.BufferSprite = m.Compositor.NewSprite(0, 0, m.BufferRegion, -1)
ASSERT(type(m.BufferSprite) = "roSprite", "m.BufferSprite")
' Draw the first page, setting the flag to true as you are always going down when
' drawing a page
m.IsPagingDown = True
m.DrawPage()
m.DrawAll()
return True
End Function
' Instructs the compositor to draw all sprites and then the screen swaps out the back buffer
' Dont need to call Flush it is called for you in SwapBuffers
Function list_view_drawall() As Void
m.Compositor.DrawAll()
m.Screen.SwapBuffers()
End Function
' Move the sprite to where you like. The drawall arg allows you to stack drawing before dumping
Function list_view_Moveto(a_x = 0 As Integer, a_y = 0 As Integer, a_isDrawAll = True As Boolean ) As Void
ASSERT(type(m.BufferSprite) = "roSprite", "m.BufferSprite")
m.BufferSprite.MoveTo(a_x, a_y)
if a_isDrawAll then m.DrawAll()
End Function
' Show or hide it
Function list_view_show(a_isDrawAll = True As Boolean) As Void
ASSERT(type(m.BufferSprite) = "roSprite", "m.BufferSprite")
m.BufferSprite.SetZ(m.ZOrder)
if a_isDrawAll then m.DrawAll()
End Function
Function list_view_hide(a_isDrawAll = True As Boolean) As Void
ASSERT(type(m.BufferSprite) = "roSprite", "m.BufferSprite")
m.BufferSprite.SetZ(-1)
if a_isDrawAll then m.DrawAll()
End Function
Function list_view_getwidth() As Integer
ASSERT(type(m.BufferRegion) = "roRegion", "m.BufferRegion")
return m.BufferRegion.GetWidth()
End Function
Function list_view_getheight() As Integer
ASSERT(type(m.BufferRegion) = "roRegion", "m.BufferRegion")
return m.BufferRegion.GetHeight()
End Function
' Clears the virtual row with background color and writes the data to the virtual row. DataIndex and VirtualRowIndex
' are controlled in the list_view_scrollrow member. You do not want to be bounds-checking in the drawing
' members. If the indexes are calculated correctly in the callers then you dont have to worry about it
Function list_view_drawrow() As Void
m.BufferBitmap.DrawRect(0, m.VirtualRowIndex, m.BufferWidth, m.RowHeight, m.BkgClr)
m.BufferBitmap.DrawText( m.Data[m.DataIndex], 0, m.VirtualRowIndex, m.TextClr, m.TextFont )
End Function
' Draws the complete page starting at y pos 0 in the bitmap buffer. DataIndex is calculated in the
' list_view_scrollpage handler. To insure that no calculations or conditionals are performed in the drawing
' functions I set the start index at 2 to m.PageSize. Then once I exit the loop I write the final line.
' which has already been indexd by the final iteration in the loop
' This member does not wirte to the the virtual row, but it always resets the virtual index pointer
' To the last row in the buffer.
Function list_view_drawpage() As Void
l_y = 0
m.BufferBitmap.Clear(m.BkgClr)
for l_i = 2 to m.PageSize
m.BufferBitmap.DrawText( m.Data[m.DataIndex], 0, l_y, m.TextClr, m.TextFont )
l_y = l_y + m.RowHeight
' Wrap around if needed
m.DataIndex = m.DataIndex + 1
if m.DataIndex >= m.DataCount then m.DataIndex = 0
end for
' Draw the final row and set the virtual index
m.BufferBitmap.DrawText( m.Data[m.DataIndex], 0, l_y, m.TextClr, m.TextFont )
m.VirtualRowIndex = l_y
End Function
' Here is where it gets messy, much easier if you don't employ region wrapping
Function list_view_scrollrow(a_isDown = True As Boolean, a_frames = 16 As Integer) As Void
' If user transitions from scrolling up/down you must adjust the dataindex and virtualindex
' by a page size since the indexes are either at the top or bottom of the visible region
' depending on the direction the user is moving. You can then resume row scrolling in the
' new direction
' In this case, the user wants to move downward
if a_isDown
' Transition adjustment made proceed with row-based indexint
if m.IsPagingDown
m.DataIndex = m.DataIndex + 1
if m.DataIndex >= m.DataCount then m.DataIndex = 0
m.VirtualRowIndex = m.VirtualRowIndex + m.RowHeight
if m.VirtualRowIndex > m.PageHeight then m.VirtualRowIndex = 0
else
' User just changed directions
' So both indexes have to be bumped up a page. There are other ways to do this
' in my past experience I have seen indexes which keep track of the top and bottom rows
' I just offset and correct
' Data Index
m.DataIndex = m.DataIndex + m.PageSize
if m.DataIndex >= m.DataCount then m.DataIndex = m.DataIndex - m.DataCount
' Virtual row index. Remember that the virtual row index is always one row beyond the
' page height
m.VirtualRowIndex = m.VirtualRowIndex + m.PageHeight
if m.VirtualRowIndex > m.PageHeight
m.VirtualRowIndex = m.VirtualRowIndex - m.PageHeight - m.RowHeight
end if
' Adjustment made, set the flag to true
m.IsPagingDown = True
end if
else
' The user is now scrolling down and the transition has been made proceed row-based indexing
if not m.IsPagingDown
m.DataIndex = m.DataIndex - 1
if m.DataIndex < 0 then m.DataIndex = m.DataCount - 1
m.VirtualRowIndex = m.VirtualRowIndex - m.RowHeight
if m.VirtualRowIndex < 0 then m.VirtualRowIndex = m.PageHeight
else
' Need to transition and turn the flag off - note that you are adding a negative number here
' if < 0
m.DataIndex = m.DataIndex - m.PageSize
if m.DataIndex < 0 then m.DataIndex = m.DataCount + m.DataIndex
m.VirtualRowIndex = m.VirtualRowIndex - m.PageHeight
if m.VirtualRowIndex < 0
m.VirtualRowIndex = m.VirtualRowIndex + m.PageHeight + m.RowHeight
end if
m.IsPagingDown = False
end if
end if
' You may safely remove the ASSERTS once your code has been fully tested
l_isState = m.DataIndex >= 0 and m.DataIndex < m.DataCount
ASSERT(l_isState, "dataindex = "+m.DataIndex.ToStr())
l_isState = m.VirtualRowIndex >= 0 and m.VirtualRowIndex <= m.PageHeight
ASSERT(l_isState, "virtualrowindex = "+m.VirtualRowIndex.ToStr())
' Draw the new virtual row/data
m.DrawRow()
l_offset = 0
l_prevset = 0
l_offdiff = 0
' Some amount of sliding: slide in the new row and return
if a_frames > 1
if a_frames > 200 then a_frames = 16
for l_i = 1 to a_frames
l_offset = int(m.RowHeight * l_i / a_frames)
l_offdiff = l_offset - l_prevset
if l_offdiff > 0
if not a_isDown l_offdiff = -l_offdiff
m.BufferRegion.Offset(0, l_offdiff, 0, 0)
m.DrawAll()
l_prevset = l_offset
end if
end for
return
end if
' If the frame value < 2 just offset by m.RowHeight
l_offset = m.RowHeight
if not a_isDown l_offset = -l_offset
m.BufferRegion.Offset(0, l_offset, 0, 0)
m.DrawAll()
return
End Function
Function list_view_scrollpage(a_isDown = True As Boolean) As Void
' Paging does not involve use of the VirtualRow. This method calls the drawpage function
' which always resets the virtual index to be one rowheight beyond the pagesize
' So, if the region's offset > 0 by the user previously row-scrolling, you need to
' reset it to 0 so that not only is the virtual row hidden, but the correct data is displayed
l_getY = m.BufferRegion.GetY()
if l_getY > 0 then m.BufferRegion.Offset(0, -l_getY, 0, 0)
' This is exactly the same as above, but instead of moving the indexes by row, we are
' moving by page
if a_isDown
if m.IsPagingDown
m.DataIndex = m.DataIndex + 1
if m.DataIndex >= m.DataCount then m.DataIndex = m.DataIndex - m.DataCount
else
m.DataIndex = m.DataIndex + m.PageSize
if m.DataIndex >= m.DataCount then m.DataIndex = m.DataIndex - m.DataCount
m.IsPagingDown = True
end if
else
' Once transitioning has taken place indexing is at
' he bottom of the page so to get back a page it would be pagesize * 2 - 1
if m.IsPagingDown
m.DataIndex = m.DataIndex - (m.PageSize * 2 - 1)
else
' This will only occur once between transitions since the indexing is at the top
m.DataIndex = m.DataIndex - m.PageSize
m.IsPagingDown = True
end if
' if you have overstepped bounds then correct. Keep in mind that you are adding a
' negative number. Also a page size can be as large as m.DataCount - 1 so your
' correction has to account for the * 2 multiplier. You could use the pagecount
' or is partialpageflag and do something brilliant but here I just add the negative offset and
' check the index again, if it is still negative I repeat the addition.
if m.DataIndex < 0
m.DataIndex = m.DataCount + m.DataIndex
if m.DataIndex < 0 then m.DataIndex = m.DataCount + m.DataIndex
end if
end if
' You may safely remove the ASSERTS once your code has been thouroughly tested
' You do not need to test the virtualrowindex since it is not changed here
l_isState = m.DataIndex >= 0 and m.DataIndex < m.DataCount
ASSERT(l_isState, "dataindex = "+m.DataIndex.ToStr())
m.DrawPage()
m.DrawAll()
End Function
' ************** END LISTVIEW OBJECT ***************************
' Generate some dummy data
Function GetData(a_number As Integer) As Object
' Emulate an empty buffer
if a_number <= 0 then return CreateObject("roArray", 1, False)
l_data = CreateObject("roArray", a_number, False)
l_text = " Down Row Number Up Row Number "
for l_i = 0 to a_number - 1
l_dn = l_i + 1
l_up = a_number - l_i
l_str = l_dn.ToStr()+l_text+l_up.ToStr()
l_str = l_str+" "+l_str
l_data.Push(l_str)
end for
return l_data
End Function
My Channels: 2D API Framework Presentation: https://owner.roku.com/add/2M9LCVC
Updated: 11-11-2015 - Completed Keyboard interface
The Joel Channel ( Final Beta )