delphi ScrollBox Для большого кол-ва элементов
Мне нужно придумать алгоритм определения индекса элемента в списке scrollbox по позиции. Основная проблема это оптимизация из-за большого кол-ва элементов в scrollbox. Если использовать все стандартное то лаги дикие при прокрутке.
Суть.
1.scrollbox мой собственный поэтому все переменные кастомные и с комментами.
в scrollbox может быть очень много элементом. 1 млм к примеру.
в scrollbox есть 2 типа элементов
то что видно далее SHOW
то что не видно (их как правило большинство) далее HIDE это просто TObject с своими полями
Достаточно создать 4 - 10 SHOW что бы отобразить 1 млм HIDE
У каждого HIDE есть своя ширина высота которая не одинакова
(если бы она была одинакова то этого вопроса бы не было,
т.к год назад написал похожий алгоритм для const Высоты элемента
он прекрасно работает)
Проблема При прокрутке списка нужно определить Index HIDE в списке LISTHIDE по новой позиции C Наименьшим кол-во итераций. Проблема позанного алгоритма это перебор всех значений при каждом движении скрола и чем дальше итем в списке тем дольше цикл будет работать
function TPtoBox.HideIndexAtPositionGet(ANewPosition:integer;var OffsetShowScroll:integer):integer;
var i,ARange,h:integer;
begin
OffsetShowScroll:=0;
ARange:=0;
Result:=-1;
for I := 0 to FListHide.Count-1 do
begin
h:=FListHide.ItemsBaza[i].HeightSave;
if (ARange <= ANewPosition) and (ARange + h >= ANewPosition) then
begin
Result:= i;
OffsetShowScroll:= -(ANewPosition - ARange);
break;
end;
inc(ARange,h);
end;
Бинарный поиск не подойдет т.к мы не знаем позицию относительно начала LISTHIDE каждого HIDE а считать ее обратно цикл перебора вызывать нужно.
==================================================================== Я пришел пока к тому что нужно сохранять в переменную позицию HIDE тогда меньше проблем и итераций но как эту позицию для каждого HIDE потом обновлять - обратно цикл перебора вызывать - (отчего ушли к тому же и пришли)
Теперь алгоритм поиска такой. Но проблема как обновить TOPMUST для каждого HIDE если скажем высота одного SHOW поменялась
function TPtoBox.BazaIndexAtPositionGet(var APosition:integer;var Offset:integer):integer;
var i,r,h,bt,bb,APositionScroll:integer;
function LocGetIndex(IndexBegin,IndexEnd:integer):integer;
var Bool:Boolean;
AOffset:integer;
begin
AOffset:= Offset;
Bool:=AmSerchBase.BinaryOf(Result,IndexBegin,IndexEnd,
function(ind:integer):real
var p,p2:real;
PosTop,PosBottom:Int64;
var ABazaItem:TPtoBaza;
begin
ABazaItem:= FListBaza.ItemsBaza[ind];
PosTop:=ABazaItem.FTopMost;
PosBottom:= PosTop + ABazaItem.Height;
if (PosTop <= APositionScroll) and (PosBottom >= APositionScroll) then
begin
Result:=0;
AOffset:= -(APositionScroll - PosTop);
end
else if PosTop > APositionScroll then Result:=1
else Result:=-1;
end);
Offset:= AOffset;
if not Bool then Result:=-1;
end;
var Now,Pred:TPtoBaza;
begin
Result:=-1;
if APosition<0 then
APosition:=0;
Offset:=0;
if APosition>BazaRangeClient then
APosition:= BazaRangeClient;
if APosition<0 then
APosition:=0;
r:=0;
APositionScroll:= APosition;
bt:=BazaIndexTop;
if bt>0 then
begin
Pred:= FListBaza.ItemsBaza[bt-1];
r:= Pred.FTopMost + Pred.Height ; //FTopMost не верные данные
end;
if ( APosition < r ) and (bt>0) then
begin
Result:=LocGetIndex(0,bt-1);
if Result>=0 then exit;
end
else
begin
bb:= BazaIndexBottom;
if bb<FListBaza.Count-1 then
begin
Pred:= FListBaza.ItemsBaza[bb+1];
if ( APosition >= self.ChildrenScrollRangeVert + Pred.FTopMost) then
begin
APositionScroll:= APositionScroll - self.ChildrenScrollRangeVert;
Result:=LocGetIndex(bb+1,FListBaza.Count-1);
if Result>=0 then exit;
end;
end;
end;
for I := 0 to self.ChildrenCount-1 do
if TPtoPanel(Childrens[i]).Baza<>nil then
begin
h:=TPtoPanel(Childrens[i]).Baza.Index;
h:= TPtoPanel(Childrens[i]).Height + AmScaleV(self.ChildrenAlign.BolderVert);
if (r <= APosition) and (r + h >= APosition) then
begin
Result:= TPtoPanel(Childrens[i]).Baza.Index;
Offset:= -(APosition - r);
break;
end;
inc(r,h);
end;
end;