Jump to content
Fudley

FMX-Android-D12 Scroll TListbox selected item into view

Recommended Posts

FMX-Android-D12  Scroll TListbox selected item into view

 

What I've tried (where lb is a TListbox and newindex is the new itemindex):

 

1.

newindex := lb.selected.index;

lb.scrollby(0.0,lb.itemheight*newindex)

 

2.

oldindex := lb.ItemIndex;
rowheight := lb.Selected.Height;
lb.ViewportPosition :=  PointF(0,rowheight*oldindex) ;

 

3.

lb.ScrollToItem(lb.Selected);

 

 

Neither of these work. Please advise. Much thanks.

Edited by Fudley
added scrolltoitem

Share this post


Link to post

Here's what Claude (the a.i.) suggested when I asked this question (and #2 does work best on android):

 

------------------------------


// Solution 1: Using ScrollToItem with proper timing
 

procedure ScrollSelectedItemIntoView_Method1(lb: TListBox);
begin
  if (lb.Selected <> nil) then
  begin
    // Use TThread.Queue to ensure the UI is updated before scrolling
    TThread.Queue(nil, procedure
    begin
      lb.ScrollToItem(lb.Selected);
    end);
  end;
end;

// Solution 2: Manual viewport positioning (most reliable)


 

procedure ScrollSelectedItemIntoView_Method2(lb: TListBox);
var
  ItemTop, ItemBottom, ViewportTop, ViewportBottom: Single;
  NewViewportY: Single;
begin
  if (lb.Selected <> nil) and (lb.Count > 0) then
  begin
    // Get the selected item's position
    ItemTop := lb.Selected.Position.Y;
    ItemBottom := ItemTop + lb.Selected.Height;
    
    // Get current viewport bounds
    ViewportTop := lb.ViewportPosition.Y;
    ViewportBottom := ViewportTop + lb.Height;
    
    // Calculate new viewport position if needed
    NewViewportY := ViewportTop;
    
    // If item is above viewport, scroll up
    if ItemTop < ViewportTop then
      NewViewportY := ItemTop
    // If item is below viewport, scroll down
    else if ItemBottom > ViewportBottom then
      NewViewportY := ItemBottom - lb.Height;
    
    // Apply the new viewport position
    if NewViewportY <> ViewportTop then
      lb.ViewportPosition := PointF(0, NewViewportY);
  end;
end;

 

// Solution 3: Using item index with proper calculations


 

procedure ScrollSelectedItemIntoView_Method3(lb: TListBox);
var
  ItemIndex: Integer;
  ItemY: Single;
  ViewportHeight: Single;
begin
  if (lb.Selected <> nil) then
  begin
    ItemIndex := lb.Selected.Index;
    ViewportHeight := lb.Height;
    
    // Calculate the Y position of the item
    ItemY := ItemIndex * lb.ItemHeight;
    
    // Check if item is outside viewport
    if (ItemY < lb.ViewportPosition.Y) or 
       (ItemY + lb.ItemHeight > lb.ViewportPosition.Y + ViewportHeight) then
    begin
      // Center the item in the viewport
      lb.ViewportPosition := PointF(0, ItemY - (ViewportHeight / 2) + (lb.ItemHeight / 2));
    end;
  end;
end;

 

// Solution 4: Enhanced method with smooth scrolling option

 

procedure ScrollSelectedItemIntoView_Method4(lb: TListBox; SmoothScroll: Boolean = False);
var
  ItemRect: TRectF;
  ViewportRect: TRectF;
  TargetY: Single;
begin
  if (lb.Selected <> nil) then
  begin
    // Get item bounds
    ItemRect := lb.Selected.BoundsRect;
    
    // Get viewport bounds
    ViewportRect := RectF(0, lb.ViewportPosition.Y, lb.Width, lb.ViewportPosition.Y + lb.Height);
    
    // Check if item is fully visible
    if not ViewportRect.Contains(ItemRect) then
    begin
      // Calculate target scroll position
      if ItemRect.Top < ViewportRect.Top then
        TargetY := ItemRect.Top
      else if ItemRect.Bottom > ViewportRect.Bottom then
        TargetY := ItemRect.Bottom - lb.Height
      else
        TargetY := lb.ViewportPosition.Y;
      
      // Ensure we don't scroll beyond content bounds
      TargetY := Max(0, Min(TargetY, lb.ContentBounds.Height - lb.Height));
      
      if SmoothScroll then
      begin
        // Smooth scroll using animation
        lb.AniCalculations.Animation := True;
        lb.AniCalculations.TouchTracking := [];
        lb.ViewportPosition := PointF(0, TargetY);
      end
      else
      begin
        // Immediate scroll
        lb.ViewportPosition := PointF(0, TargetY);
      end;
    end;
  end;
end;

 

// Usage examples:


procedure TForm1.Button1Click(Sender: TObject);
begin
  // Select an item programmatically
  if ListBox1.Count > 5 then
  begin
    ListBox1.ItemIndex := 5;
    
    // Then scroll it into view using any of the methods above ScrollSelectedItemIntoView_Method2(ListBox1);
  end;
end;

 

// Alternative: Create a helper method in your form
procedure TForm1.SelectAndScrollToItem(lb: TListBox; ItemIndex: Integer);
begin
  if (ItemIndex >= 0) and (ItemIndex < lb.Count) then
  begin
    lb.ItemIndex := ItemIndex;
    
    // Use a small delay to ensure selection is processed
    TThread.CreateAnonymousThread(procedure
    begin
      TThread.Synchronize(nil, procedure
      begin
        ScrollSelectedItemIntoView_Method2(lb);
      end);
    end).Start;
  end;
end;

// For Android-specific optimizations, you might want to disable animations
// during scrolling to improve performance:
procedure OptimizeForAndroid(lb: TListBox);
begin
  lb.AniCalculations.Animation := False;
  lb.AniCalculations.TouchTracking := [];
end;

 

Key Points:

Method 2 is the most reliable - it manually calculates viewport positioning and works consistently across different Android devices.
Timing is crucial - Sometimes you need to use TThread.Queue or TThread.Synchronize to ensure the UI has updated before scrolling.
Your original attempts failed because:

ScrollBy doesn't work well with viewport positioning
Direct ViewportPosition assignment needs proper bounds checking
ScrollToItem can be unreliable without proper timing


Android-specific considerations:

Disable animations during programmatic scrolling for better performance
Consider the device's screen density and scaling factors
Test on different Android versions and screen sizes

 

Recommended approach:
Use Method 2 for most cases, or Method 4 if you want smooth scrolling. For Android deployment, consider disabling animations during programmatic scrolling to improve performance.
The key difference from your attempts is proper bounds checking and ensuring the viewport position is calculated relative to the actual item positions rather than just multiplying by item height.RetryClaude can make mistakes. Please double-check responses.

 

 

 

Share this post


Link to post

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×