HyperZen Опубликовано 6 июня, 2016 Поделиться Опубликовано 6 июня, 2016 (изменено) Добрый вечер! Все-таки, как корректно подсчитывать высоту итемов данного компонента, если учитывать, что текст может быть достаточно большим. Использую код с данного сайта, все работает корректно, пока не встречается текст, чуть больше одного абзаца: function TextHeight(const AText: string; aTextSettings: TTextSettings; const MaxWidth: Single): Single; // uses FMX.Graphics, FMX.TextLayout, FMX.Types, Math var Layout: TTextLayout; aRect: TRectF; aWW: Boolean; begin Result := 24; if AText.IsEmpty then Exit; aWW := Pos(#13#10, AText) > 0; if (aTextSettings.WordWrap) or (aWW) then aRect := RectF(0, 0, MaxWidth, MaxSingle) else aRect := RectF(0, 0, MaxSingle, MaxSingle); Layout := TTextLayoutManager.DefaultTextLayout.Create; try Layout.BeginUpdate; Layout.TopLeft := aRect.TopLeft; Layout.MaxSize := PointF(aRect.Width, aRect.Height); Layout.Text := AText; Layout.WordWrap := aTextSettings.WordWrap; Layout.HorizontalAlign := TTextAlign.Leading; Layout.VerticalAlign := TTextAlign.Leading; Layout.Font.Assign(aTextSettings.Font); Layout.Color := aTextSettings.FontColor; Layout.RightToLeft := false; Layout.EndUpdate; aRect := Layout.TextRect; finally FreeAndNil(Layout); end; Result := aRect.Bottom; end; И вызов в onUpdateObject: procedure TForm12.ListView1UpdateObjects(const Sender: TObject; const AItem: TListViewItem); var TS: TTextSettings; begin TS := TTextSettings.Create(nil); // задаем параметры текста для расчета "Text" TS.WordWrap:= True; TS.Font.Assign(ListView1.ItemAppearanceObjects.ItemObjects.Text.Font); AItem.Height := Trunc(TextHeight(AItem.Text, TS, ListView1.Width)); FreeAndNil(TS); end; Получаем: Изменено 6 июня, 2016 пользователем HyperZen Цитата Ссылка на комментарий
0 HyperZen Опубликовано 7 июня, 2016 Автор Поделиться Опубликовано 7 июня, 2016 1 час назад, Brovin Yaroslav сказал: Вот пример вычисления высоты итема. ListViewVariableHeightItems2.zip Я этот пример показывал на лонче в питере. Отличный пример!!! Начинаю его "копать"! Ярослав, почему после стольких чаяний пользователей EMB никак не введут AutoItemHeight в ListViewItem? Цитата Ссылка на комментарий
0 Администраторы Brovin Yaroslav Опубликовано 7 июня, 2016 Администраторы Поделиться Опубликовано 7 июня, 2016 Сразу не могу вам ответить. Но могу предположить, что итем умеет отображать не только текст и имеет возможность различных вариантов расположения отображаемых данных. Именно по этому и нельзя заранее знать, какой способ вы будите использовать. HyperZen 1 Цитата Ссылка на комментарий
0 HyperZen Опубликовано 7 июня, 2016 Автор Поделиться Опубликовано 7 июня, 2016 (изменено) С Вашим примером все работает как надо. Тестим... Изменено 7 июня, 2016 пользователем HyperZen Цитата Ссылка на комментарий
0 HyperZen Опубликовано 7 июня, 2016 Автор Поделиться Опубликовано 7 июня, 2016 (изменено) Ярослав! Все работает как надо, но при некоторых размерах шрифта (я подозреваю те размеры, про которые мы писали выше) все-таки вычисления некорректны - это касается платформы Windows. На конкретном устройстве - все работает корректно с любым размером шрифтов! Изменено 7 июня, 2016 пользователем HyperZen #WAMACO 1 Цитата Ссылка на комментарий
0 HyperZen Опубликовано 8 июня, 2016 Автор Поделиться Опубликовано 8 июня, 2016 Нашел-таки глюк/баг (не знаю как назвать правильно) - все работает корректно в Windows. А вот что касается Android, то размер шрифта необходимо указывать вручную обязательно (даже если используете в ListViewItem неизменный размер шрифта), иначе время от времени наблюдаются артефакты в виде наложения текста, или уползания его на следующий Item. Drawable := TListItemText(AItem.View.FindDrawable('txtMain')); Text := Drawable.Text; Drawable.Font.Size := ListView1.ItemAppearanceObjects.ItemObjects.Text.Font.Size; - эта строчка решила проблему. AngryOwl 1 Цитата Ссылка на комментарий
0 haword Опубликовано 8 июня, 2016 Поделиться Опубликовано 8 июня, 2016 function GetTextHeight(text: string; width: single; wordwrap: boolean; font: tfont; HAlign, VAlign: TTextAlign; Trimming: TTextTrimming): single; begin if FTextLyout = nil then FTextLyout := TTextLayoutManager.DefaultTextLayout.Create; FTextLyout.BeginUpdate; try // Инициализируем текстовый слой для корректного вычисления // размеров отображаемого текста FTextLyout.Text := text; FTextLyout.MaxSize := TPointF.Create(Width, 10000); FTextLyout.WordWrap := WordWrap; FTextLyout.Font := Font; FTextLyout.Trimming:= Trimming; FTextLyout.HorizontalAlign := HAlign; FTextLyout.VerticalAlign := VAlign; finally FTextLyout.EndUpdate; end; Result:=FTextLyout.Height; end; procedure TMainForm.GroupLVUpdateObjects(const Sender: TObject; const AItem: TListViewItem); var lvi: TListViewItem; t: Single; begin lvi:= TListView(Sender).Items[AItem.Index]; t:= GetTextHeight(lvi.Text, lvi.Objects.TextObject.Width, lvi.Objects.TextObject.WordWrap, lvi.Objects.TextObject.Font, TTExtAlign(lvi.Objects.TextObject.Align), TTExtAlign(lvi.Objects.TextObject.VertAlign) ); lvi.Height:= Round(t) + 30; end; у меня работает. Евгений Корепов и HyperZen 2 Цитата Ссылка на комментарий
0 Евгений Корепов Опубликовано 13 июня, 2016 Поделиться Опубликовано 13 июня, 2016 Почитал, посмотрел все варианты и сделал работу над ошибками. Переделал свой, показанный выше, код. Проверил на Windows и Android, с стилем по умолчанию и другими стилями - работает безупречно. Так же не нуждается в подгонках типа "Height:= Round(t) + 30;". Преимущество кода в том что на вход подаем ATextObject : TListItemText, т.е. можно легко использовать для вычисления Text или Detail. function TFormMain.CalculateListItemObjectHeight(ATextObject : TListItemText) : Single; begin FTextLayout.BeginUpdate; try FTextLayout.Text:=ATextObject.Text; FTextLayout.MaxSize:=TPointF.Create(ATextObject.Width, TTextLayout.MaxLayoutSize.Y); FTextLayout.Font.Assign(ATextObject.Font); FTextLayout.WordWrap:=ATextObject.WordWrap; FTextLayout.Trimming:=ATextObject.Trimming; FTextLayout.HorizontalAlign:=ATextObject.TextAlign; FTextLayout.VerticalAlign:=ATextObject.TextVertAlign; finally FTextLayout.EndUpdate; end; Result:=FTextLayout.Height; end; Пример использования: у меня Text непредсказуемой длины, вверху ListItem, по всей ширине ListItem. Под Text располагаются слева Image, справа Detail. Текст Detail может быть от пары слов, до нескольких предложений procedure TFormMain.ListViewActionsUpdateObjects(const Sender: TObject; const AItem: TListViewItem); Var AItemTextHeight, AItemDetailHeight, AItemImageHeight, AItemHeight : Single; AListView: TListView; begin AListView:=TListView(Sender); AItem.Objects.TextObject.Width:=AListView.Width - AListView.ItemSpaces.Left - // Вычисляем ширину Text AListView.ItemSpaces.Right - DefaultScrollBarWidth; AItemTextHeight:=CalculateListItemObjectHeight(AItem.Objects.TextObject); // Вычисляем высоту Text AItem.Objects.DetailObject.PlaceOffset.Y:=AItemTextHeight; // Устанавливаем смещение для Detail AItem.Objects.ImageObject.PlaceOffset.Y:=AItemTextHeight; // Устанавливаем смещение для Image AItemDetailHeight:=CalculateListItemObjectHeight(AItem.Objects.DetailObject); // Вычисляем высоту Text AItemImageHeight:=AItem.Objects.ImageObject.Height; AItem.Objects.DetailObject.Height:=AItemImageHeight; AItemHeight:=AItemTextHeight + Max(AItemDetailHeight, AItemImageHeight); // Вычисляем высоту всего Item AItem.Height:=Round(AItemHeight); end; HyperZen и Rusland 2 Цитата Ссылка на комментарий
0 krapotkin Опубликовано 13 июня, 2016 Поделиться Опубликовано 13 июня, 2016 у меня аналогичная функция содержит еще один важный момент иногда ATextObject не имеет установленной ширины, т.е. значение по умолчанию=0! тогда нужно либо всегда задавать ее при создании итема, либо передавать ее отдельным параметром думаю над созданием наследников TDrawable которые кроме Offset имеют свойства Margins OFF: по идее, все это уже пройдено в HTML когда настала эпоха DIV-верстки. Мы пытаемся наощупь идти по тем же граблям HyperZen 1 Цитата Ссылка на комментарий
0 Евгений Корепов Опубликовано 13 июня, 2016 Поделиться Опубликовано 13 июня, 2016 5 минут назад, krapotkin сказал: у меня аналогичная функция содержит еще один важный момент иногда ATextObject не имеет установленной ширины, т.е. значение по умолчанию=0! тогда нужно либо всегда задавать ее при создании итема, либо передавать ее отдельным параметром думаю над созданием наследников TDrawable которые кроме Offset имеют свойства Margins OFF: по идее, все это уже пройдено в HTML когда настала эпоха DIV-верстки. Мы пытаемся наощупь идти по тем же граблям Полностью согласен. Ширину я устанавливаю в ListViewActionsUpdateObjects, и только после этого вызываю функцию. HyperZen 1 Цитата Ссылка на комментарий
0 rareMax Опубликовано 13 июня, 2016 Поделиться Опубликовано 13 июня, 2016 От эмбр: http://community.embarcadero.com/blogs?view=entry&id=8542 procedure TVariableHeight.ListView1UpdateObjects(const Sender: TObject; const AItem: TListViewItem); var Drawable: TListItemText; SizeImg: TListItemImage; Text: string; AvailableWidth: Single; begin SizeImg := TListItemImage(AItem.View.FindDrawable('imgSize')); AvailableWidth := TListView(Sender).Width - TListView(Sender).ItemSpaces.Left - TListView(Sender).ItemSpaces.Right - SizeImg.Width; // Find the text drawable which is used to calcualte item size. // For dynamic appearance, use item name. // For classic appearances use TListViewItem.TObjectNames.Text // Drawable := TListItemText(AItem.View.FindDrawable(TListViewItem.TObjectNames.Text)); Drawable := TListItemText(AItem.View.FindDrawable('txtMain')); Text := Drawable.Text; // Randomize the font when updating for the first time if Drawable.TagFloat = 0 then begin Drawable.Font.Size := 1; // Ensure that default font sizes do not play against us Drawable.Font.Size := 10 + Random(4) * 4; Drawable.TagFloat := Drawable.Font.Size; if Text.Length < 100 then Drawable.Font.Style := [TFontStyle.fsBold]; end; // Calculate item height based on text in the drawable AItem.Height := GetTextHeight(Drawable, AvailableWidth, Text); Drawable.Height := AItem.Height; Drawable.Width := AvailableWidth; SizeImg.OwnsBitmap := False; SizeImg.Bitmap := GetDimensionBitmap(SizeImg.Width, AItem.Height); end; Rusland, krapotkin и HyperZen 3 Цитата Ссылка на комментарий
0 HyperZen Опубликовано 19 июня, 2016 Автор Поделиться Опубликовано 19 июня, 2016 (изменено) В 07.06.2016 в 21:44, Brovin Yaroslav сказал: Вот пример вычисления высоты итема. ListViewVariableHeightItems2.zip Ярослав, сегодня, вновь тестируя различные размеры шрифтов, обнаружил, что если по умолчанию выставить размер шрифта 14 (под Андроид), расчет все равно неверный - не хватает буквально 5% высоты, добавлять ее вручную не хочется, потому как при всех других размерах все корректно. Если изменить размер шрифта в рантайме - и вернуть на 14, то все опять корректно. Неправильный подсчет только при запуске приложения и в случае, если размер стоит = 14. Вышел из положения добавляя при расчете 0.1 к размеру шрифта: Drawable.Font.Size := spnBoxFontSize.Value + 0.1; // при старте программы размер выставляется относительно значения SpinBox'а Изменено 19 июня, 2016 пользователем HyperZen Цитата Ссылка на комментарий
0 Евгений Корепов Опубликовано 19 июня, 2016 Поделиться Опубликовано 19 июня, 2016 4 часа назад, HyperZen сказал: Ярослав, сегодня, вновь тестируя различные размеры шрифтов, обнаружил, что если по умолчанию выставить размер шрифта 14 (под Андроид), расчет все равно неверный - не хватает буквально 5% высоты, добавлять ее вручную не хочется, потому как при всех других размерах все корректно. Если изменить размер шрифта в рантайме - и вернуть на 14, то все опять корректно. Неправильный подсчет только при запуске приложения и в случае, если размер стоит = 14. Вышел из положения добавляя при расчете 0.1 к размеру шрифта: Drawable.Font.Size := spnBoxFontSize.Value + 0.1; // при старте программы размер выставляется относительно значения SpinBox'а Попробуйте проверить отладкой какой размер шрифта попадает в GetTextHeight. Потому что по умолчанию размер 12, а в стиле размер может быть 18. Так же вы не выполняете ListView.ApplyStyleLookup после заполнения ListView. Очень рекомендую перед заполнением ListView выполнять код AListView.BeginUpdate; AListView.OnUpdateObjects:=Nil; После заполнения: AListView.EndUpdate; ListViewUpdateAll(AListView); // В цикле применяет ListViewUpdateObjects ко всем элементам AListView.ApplyStyleLookup; AListView.OnUpdateObjects:= ListViewUpdateObjects; HyperZen 1 Цитата Ссылка на комментарий
0 HyperZen Опубликовано 20 июня, 2016 Автор Поделиться Опубликовано 20 июня, 2016 21 час назад, Евгений Корепов сказал: Попробуйте проверить отладкой какой размер шрифта попадает в GetTextHeight. Потому что по умолчанию размер 12, а в стиле размер может быть 18. Проверил, размер шрифта попадает тот, который у меня грузится в SpinEdit во время создания формы, и он же устанавливается сразу в момент все той же загрузки. Стилей не использую. Ваш пример выше хороший, но не могу его допилить для использования TListView в DynamicAppearance режиме (если его вообще надо допиливать - просто не получилось ). Цитата Ссылка на комментарий
0 krapotkin Опубликовано 20 июня, 2016 Поделиться Опубликовано 20 июня, 2016 я же выкладывал готовый рабочий проект под берлин. там же все вычисляется верно. в Dynamic Appearance что еще не так? ListViewTestBerlin.7z HyperZen 1 Цитата Ссылка на комментарий
0 dervish00 Опубликовано 14 февраля, 2019 Поделиться Опубликовано 14 февраля, 2019 Подниму тему. Вот совершенно элементарный код: procedure TForm1.Button2Click(Sender: TObject); var Item : TListViewItem; i, CountStringsInHeader : Integer; hHeader : single; begin CountStringsInHeader := 5; if CountStringsInHeader > 0 then begin hHeader := 0; ListView3.BeginUpdate; try if ListView3.Items.Count <> 0 then begin ListView3.ScrollViewPos := 0; while ListView3.Items.Count > 0 do ListView3.Items.Delete(0); end; for I := 0 to CountStringsInHeader-1 do begin Item := ListView3.Items.Add; Item.Text := i.ToString; Item.Detail := 'Detail: '+i.ToString; hHeader := hHeader + Item.Height; end; finally ListView3.EndUpdate; layListHeader.Height := hHeader + laySeparator.Height; layListHeader.Visible := True; end; end; ListView2.BeginUpdate; try if ListView2.Items.Count <> 0 then begin ListView2.ScrollViewPos := 0; while ListView2.Items.Count > 0 do ListView2.Items.Delete(0); end; for I := CountStringsInHeader to 4 do begin Item := ListView2.Items.Add; Item.Text := i.ToString; Item.Detail := 'Detail: '+i.ToString; end; finally ListView2.EndUpdate; end; end; Почему в строке hHeader := hHeader + Item.Height; Item.Height всегда равно 0? Техт маленький, мне не нужно расширять item, просто точно вычислить высоту Layout в который вписан ListView. Цитата Ссылка на комментарий
0 Евгений Корепов Опубликовано 14 февраля, 2019 Поделиться Опубликовано 14 февраля, 2019 25 минут назад, dervish00 сказал: hHeader := hHeader + Item.Height; Item.Height всегда равно 0? Техт маленький, мне не нужно расширять item, просто точно вычислить высоту Layout в который вписан ListView. Потому что перед этой строкой вы делаете Item := ListView3.Items.Add; Item.Text := i.ToString; Item.Detail := 'Detail: '+i.ToString; Вы добавили Item, но не дали возможность его отрисовать. Т.е. ListView3 добавил себе итем, но еще ничего о нем не знает. Параметры Item вам нужно выставлять в событии ListView3UpdatedObjects - тут уже будет известна высота. Цитата Ссылка на комментарий
0 dervish00 Опубликовано 15 февраля, 2019 Поделиться Опубликовано 15 февраля, 2019 11 часов назад, Евгений Корепов сказал: Вы добавили Item, но не дали возможность его отрисовать. Т.е. ListView3 добавил себе итем, но еще ничего о нем не знает. Параметры Item вам нужно выставлять в событии ListView3UpdatedObjects - тут уже будет известна высота. Ну все не так просто - OnUpdatINGObjects возникает при прорисовке конкретного Item. Мне же нужно посчитать сумму их высот. Да и вообще - я, честно говоря, думал, что данным событием нужно пользоваться если тип ItemAppearance будет Custom или DynamicAppearance, у меня же в примере вполне себе классический тип - ListItemRightDetail, не нужно ничего добавлять - ни рисунков, никакого сложного форматирования, просто Item и Detail Цитата Ссылка на комментарий
0 Hevard Опубликовано 20 апреля, 2021 Поделиться Опубликовано 20 апреля, 2021 Читаю, диву даюсь! Что мешало сразу сделать корректным расчёт высоты. Хитрости, уловки, танцы с бубном... Я использовал : function TMainForm.DoMeasureText(Canvas: TCanvas; AText: string; Width: single): single; var R: TRectF; begin R := RectF(0, 0, Width-9, 10000); // Width-54 = font.size x 3 Canvas.MeasureText(R, AText, True, [], TTextAlign.Leading, TTextAlign.Leading); Result := R.Bottom; end; Но так и не понял всей премудрости. При шрифте 18, в одну строчку , на один RadioButton она выдаёт 43, 23 , а на какой-то 46,47. Где-то считает одну строчку за две. Цитата Ссылка на комментарий
0 Hevard Опубликовано 30 апреля, 2021 Поделиться Опубликовано 30 апреля, 2021 В 20.06.2016 в 22:42, krapotkin сказал: я же выкладывал готовый рабочий проект под берлин. там же все вычисляется верно Вот что-то опять идёт не так Цитата Ссылка на комментарий
0 krapotkin Опубликовано 4 мая, 2021 Поделиться Опубликовано 4 мая, 2021 достану, открою на 10.4, как будет время Цитата Ссылка на комментарий
0 Hevard Опубликовано 16 мая, 2021 Поделиться Опубликовано 16 мая, 2021 В 04.05.2021 в 15:01, krapotkin сказал: достану, открою на 10.4, как будет время Прошу прощения за оффтоп, но не нашёл даже упоминаний - что такое Delphi 14.1 Цитата Ссылка на комментарий
0 Vitaldj Опубликовано 16 мая, 2021 Поделиться Опубликовано 16 мая, 2021 6 часов назад, Hevard сказал: Прошу прощения за оффтоп, но не нашёл даже упоминаний - что такое Delphi 14.1 Какое 14.1? Автор пишет 10.4 Цитата Ссылка на комментарий
0 Hevard Опубликовано 19 мая, 2021 Поделиться Опубликовано 19 мая, 2021 В 16.05.2021 в 11:32, Vitaldj сказал: Какое 14.1? Автор пишет 10.4 Ох уж эти суровые челябинские пользователи! А если внимательнее "подпись" автора посмотреть? Цитата Ссылка на комментарий
0 Vitaldj Опубликовано 19 мая, 2021 Поделиться Опубликовано 19 мая, 2021 54 минут назад, Hevard сказал: Ох уж эти суровые челябинские пользователи! А если внимательнее "подпись" автора посмотреть? Ну, то есть, циклопентанпергидрофенантрен тебя не смутил? Цитата Ссылка на комментарий
0 Hevard Опубликовано 20 мая, 2021 Поделиться Опубликовано 20 мая, 2021 14 часов назад, Vitaldj сказал: Ну, то есть, циклопентанпергидрофенантрен тебя не смутил? Нет, ибо к IDE это никак не относится. Мне достаточно знания, что такое ацетилсалициловая кислота. Цитата Ссылка на комментарий
Вопрос
HyperZen
Добрый вечер! Все-таки, как корректно подсчитывать высоту итемов данного компонента, если учитывать, что текст может быть достаточно большим.
Использую код с данного сайта, все работает корректно, пока не встречается текст, чуть больше одного абзаца:
function TextHeight(const AText: string; aTextSettings: TTextSettings; const MaxWidth: Single): Single; // uses FMX.Graphics, FMX.TextLayout, FMX.Types, Math var Layout: TTextLayout; aRect: TRectF; aWW: Boolean; begin Result := 24; if AText.IsEmpty then Exit; aWW := Pos(#13#10, AText) > 0; if (aTextSettings.WordWrap) or (aWW) then aRect := RectF(0, 0, MaxWidth, MaxSingle) else aRect := RectF(0, 0, MaxSingle, MaxSingle); Layout := TTextLayoutManager.DefaultTextLayout.Create; try Layout.BeginUpdate; Layout.TopLeft := aRect.TopLeft; Layout.MaxSize := PointF(aRect.Width, aRect.Height); Layout.Text := AText; Layout.WordWrap := aTextSettings.WordWrap; Layout.HorizontalAlign := TTextAlign.Leading; Layout.VerticalAlign := TTextAlign.Leading; Layout.Font.Assign(aTextSettings.Font); Layout.Color := aTextSettings.FontColor; Layout.RightToLeft := false; Layout.EndUpdate; aRect := Layout.TextRect; finally FreeAndNil(Layout); end; Result := aRect.Bottom; end;
И вызов в onUpdateObject:
procedure TForm12.ListView1UpdateObjects(const Sender: TObject; const AItem: TListViewItem); var TS: TTextSettings; begin TS := TTextSettings.Create(nil); // задаем параметры текста для расчета "Text" TS.WordWrap:= True; TS.Font.Assign(ListView1.ItemAppearanceObjects.ItemObjects.Text.Font); AItem.Height := Trunc(TextHeight(AItem.Text, TS, ListView1.Width)); FreeAndNil(TS); end;
Получаем:
Ссылка на комментарий
50 ответов на этот вопрос
Рекомендуемые сообщения
Присоединяйтесь к обсуждению
Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.