Перейти к содержанию
Fire Monkey от А до Я
  • 0

ListItemBackground


xenon54

Вопрос

Здравствуйте! Подскажите пожалуйста способ поменять  цвет фона у ListViewItem'а. С ListBox'om все просто, можно через стили все сделать, добавить например RectAngle в стили, и использовать его в RunTime, а как же быть с ListViewItem, ведь это не контрол? Использую модуль из примеров MultiDetailAppearanceU прям то что мне нужно. Мб как-то туда можно добавить что-то? у меня ничего не получается :( в StyleBook (использую iOSBlack.fsf)  в "listviewstyle.itembackground" есть свойство Color, но естественно оно меняет сразу у всех итемов стиль, и в рантайме не получается кстати поменять почему-то этот цвет.

unit MultiDetailAppearanceU;

interface

uses FMX.ListView, FMX.ListView.Types, System.Classes, System.SysUtils,
FMX.Types, System.UITypes, FMX.MobilePreview;

type

  TMultiDetailAppearanceNames = class
  public const
    ListItem = 'MultiDetailItem';
    ListItemCheck = ListItem + 'ShowCheck';
    ListItemDelete = ListItem + 'Delete';
    Detail1 = 'det1';  // Name of MultiDetail object/data
    Detail2 = 'det2';
    Detail3 = 'det3';
  end;

implementation

uses System.Math, System.Rtti;

type

  TMultiDetailItemAppearance = class(TPresetItemObjects)
  public const
    cTextMarginAccessory = 8;
    cDefaultImagePlaceOffsetX = -3;
    cDefaultImageTextPlaceOffsetX = 4;
    cDefaultHeight = 80;
    cDefaultImageWidth = 50;
    cDefaultImageHeight = 70;
  private
    FMultiDetail1: TTextObjectAppearance;
    FMultiDetail2: TTextObjectAppearance;
    FMultiDetail3: TTextObjectAppearance;
    procedure SetMultiDetail1(const Value: TTextObjectAppearance);
    procedure SetMultiDetail2(const Value: TTextObjectAppearance);
    procedure SetMultiDetail3(const Value: TTextObjectAppearance);
  protected
    function DefaultHeight: Integer; override;
    procedure UpdateSizes; override;
    function GetGroupClass: TPresetItemObjects.TGroupClass; override;
    procedure SetObjectData(const AListViewItem: TListViewItem; const AIndex: string; const AValue: TValue; var AHandled: Boolean); override;
  public
    constructor Create; override;
    destructor Destroy; override;
  published
    property Image;
    property MultiDetail1: TTextObjectAppearance read FMultiDetail1 write SetMultiDetail1;
    property MultiDetail2: TTextObjectAppearance read FMultiDetail2 write SetMultiDetail2;
    property MultiDetail3: TTextObjectAppearance read FMultiDetail3 write SetMultiDetail3;
    property Accessory;
  end;

  TMultiDetailDeleteAppearance = class(TMultiDetailItemAppearance)
  private const
    cDefaultGlyph = TGlyphButtonType.Delete;
  public
    constructor Create; override;
  published
    property GlyphButton;
  end;

  TMultiDetailShowCheckAppearance = class(TMultiDetailItemAppearance)
  private const
    cDefaultGlyph = TGlyphButtonType.Checkbox;
  public
    constructor Create; override;
  published
    property GlyphButton;
  end;


const
  cMultiDetail1Member = 'Detail1';
  cMultiDetail2Member = 'Detail2';
  cMultiDetail3Member = 'Detail3';


constructor TMultiDetailItemAppearance.Create;
begin
  inherited;
  Accessory.DefaultValues.AccessoryType := TAccessoryType.More;
  Accessory.DefaultValues.Visible := True;
  Accessory.RestoreDefaults;
  Text.DefaultValues.VertAlign := TListItemAlign.Trailing;
  Text.DefaultValues.TextVertAlign := TTextAlign.Leading;
  Text.DefaultValues.Height := 76;  // Item will be bottom aligned, with text top aligned
  Text.DefaultValues.Visible := True;
  Text.RestoreDefaults;

  FMultiDetail1 := TTextObjectAppearance.Create;
  FMultiDetail1.Name := TMultiDetailAppearanceNames.Detail1;
  FMultiDetail1.DefaultValues.Assign(Text.DefaultValues);  // Start with same defaults as Text object
  FMultiDetail1.DefaultValues.Height := 56;  // Move text down
  FMultiDetail1.DefaultValues.IsDetailText := True; // Use detail font
  FMultiDetail1.RestoreDefaults;
  FMultiDetail1.OnChange := Self.ItemPropertyChange;
  FMultiDetail1.Owner := Self;

  FMultiDetail2 := TTextObjectAppearance.Create;
  FMultiDetail2.Name := TMultiDetailAppearanceNames.Detail2;
  FMultiDetail2.DefaultValues.Assign(FMultiDetail1.DefaultValues);  // Start with same defaults as Text object
  FMultiDetail2.DefaultValues.Height := 38; // Move text down
  FMultiDetail2.RestoreDefaults;
  FMultiDetail2.OnChange := Self.ItemPropertyChange;
  FMultiDetail2.Owner := Self;

  FMultiDetail3 := TTextObjectAppearance.Create;
  FMultiDetail3.Name := TMultiDetailAppearanceNames.Detail3;
  FMultiDetail3.DefaultValues.Assign(FMultiDetail2.DefaultValues);  // Start with same defaults as Text object
  FMultiDetail3.DefaultValues.Height := 20; // Move text down
  FMultiDetail3.RestoreDefaults;
  FMultiDetail3.OnChange := Self.ItemPropertyChange;
  FMultiDetail3.Owner := Self;

  // Define livebindings members that make up MultiDetail
  FMultiDetail1.DataMembers :=
    TObjectAppearance.TDataMembers.Create(
      TObjectAppearance.TDataMember.Create(
        cMultiDetail1Member, // Displayed by LiveBindings
        Format('Data["%s"]', [TMultiDetailAppearanceNames.Detail1])));   // Expression to access value from TListViewItem
  FMultiDetail2.DataMembers :=
    TObjectAppearance.TDataMembers.Create(
      TObjectAppearance.TDataMember.Create(
        cMultiDetail2Member, // Displayed by LiveBindings
        Format('Data["%s"]', [TMultiDetailAppearanceNames.Detail2])));   // Expression to access value from TListViewItem
  FMultiDetail3.DataMembers :=
    TObjectAppearance.TDataMembers.Create(
      TObjectAppearance.TDataMember.Create(
        cMultiDetail3Member, // Displayed by LiveBindings
        Format('Data["%s"]', [TMultiDetailAppearanceNames.Detail3])));   // Expression to access value from TListViewItem

  Image.DefaultValues.Width := cDefaultImageWidth;
  Image.DefaultValues.Height := cDefaultImageHeight;
  Image.RestoreDefaults;

  GlyphButton.DefaultValues.VertAlign := TListItemAlign.Center;
  GlyphButton.RestoreDefaults;

  // Define the appearance objects
  AddObject(Text, True);
  AddObject(MultiDetail1, True);
  AddObject(MultiDetail2, True);
  AddObject(MultiDetail3, True);
  AddObject(Image, True);
  AddObject(Accessory, True);
  AddObject(GlyphButton, IsItemEdit);  // GlyphButton is only visible when in edit mode
end;

constructor TMultiDetailDeleteAppearance.Create;
begin
  inherited;
  GlyphButton.DefaultValues.ButtonType := cDefaultGlyph;
  GlyphButton.DefaultValues.Visible := True;
  GlyphButton.RestoreDefaults;
end;

constructor TMultiDetailShowCheckAppearance.Create;
begin
  inherited;
  GlyphButton.DefaultValues.ButtonType := cDefaultGlyph;
  GlyphButton.DefaultValues.Visible := True;
  GlyphButton.RestoreDefaults;
end;

function TMultiDetailItemAppearance.DefaultHeight: Integer;
begin
  Result := cDefaultHeight;
end;

destructor TMultiDetailItemAppearance.Destroy;
begin
  FMultiDetail1.Free;
  FMultiDetail2.Free;
  FMultiDetail3.Free;
  inherited;
end;

procedure TMultiDetailItemAppearance.SetMultiDetail1(
  const Value: TTextObjectAppearance);
begin
  FMultiDetail1.Assign(Value);
end;

procedure TMultiDetailItemAppearance.SetMultiDetail2(
  const Value: TTextObjectAppearance);
begin
  FMultiDetail2.Assign(Value);
end;


procedure TMultiDetailItemAppearance.SetMultiDetail3(
  const Value: TTextObjectAppearance);
begin
  FMultiDetail3.Assign(Value);
end;


procedure TMultiDetailItemAppearance.SetObjectData(
  const AListViewItem: TListViewItem; const AIndex: string;
  const AValue: TValue; var AHandled: Boolean);
begin
  inherited;

end;

function TMultiDetailItemAppearance.GetGroupClass: TPresetItemObjects.TGroupClass;
begin
  Result := TMultiDetailItemAppearance;
end;

procedure TMultiDetailItemAppearance.UpdateSizes;
var
  LOuterHeight: Single;
  LOuterWidth: Single;
  LInternalWidth: Single;
  LImagePlaceOffset: Single;
  LImageTextPlaceOffset: Single;
begin
  BeginUpdate;
  try
    inherited;

    // Update the widths and positions of renderening objects within a TListViewItem
    LOuterHeight := Height - Owner.ItemSpaces.Top - Owner.ItemSpaces.Bottom;
    LOuterWidth := Owner.Width - Owner.ItemSpaces.Left - Owner.ItemSpaces.Right;
    if Image.ActualWidth = 0 then
    begin
      LImagePlaceOffset := 0;
      LImageTextPlaceOffset := 0;
    end
    else
    begin
      LImagePlaceOffset := cDefaultImagePlaceOffsetX;
      LImageTextPlaceOffset := cDefaultImageTextPlaceOffsetX;
    end;
    Image.InternalPlaceOffset.X := GlyphButton.ActualWidth + LImagePlaceOffset;
    if Image.ActualWidth > 0 then
      Text.InternalPlaceOffset.X :=
        Image.ActualPlaceOffset.X +  Image.ActualWidth + LImageTextPlaceOffset
    else
      Text.InternalPlaceOffset.X :=
        0 + GlyphButton.ActualWidth;
    MultiDetail1.InternalPlaceOffset.X := Text.InternalPlaceOffset.X;
    MultiDetail2.InternalPlaceOffset.X := Text.InternalPlaceOffset.X;
    MultiDetail3.InternalPlaceOffset.X := Text.InternalPlaceOffset.X;
    LInternalWidth := LOuterWidth - Text.ActualPlaceOffset.X - Accessory.ActualWidth;
    if Accessory.ActualWidth > 0 then
      LInternalWidth := LInternalWidth - cTextMarginAccessory;
    Text.InternalWidth := Max(1, LInternalWidth);
    MultiDetail1.InternalWidth := Text.InternalWidth;
    MultiDetail2.InternalWidth := Text.InternalWidth;
    MultiDetail3.InternalWidth := Text.InternalWidth;
  finally
    EndUpdate;
  end;

end;

type
  TOption = TCustomListView.TRegisterAppearanceOption;
const
  sThisUnit = 'MultiDetailAppearanceU';     // Will be added to the uses list when appearance is used
initialization
  // MultiDetailItem group
  TCustomListView.RegisterAppearance(
    TMultiDetailItemAppearance, TMultiDetailAppearanceNames.ListItem,
    [TOption.Item], sThisUnit);
  TCustomListView.RegisterAppearance(
    TMultiDetailDeleteAppearance, TMultiDetailAppearanceNames.ListItemDelete,
    [TOption.ItemEdit], sThisUnit);
  TCustomListView.RegisterAppearance(
    TMultiDetailShowCheckAppearance, TMultiDetailAppearanceNames.ListItemCheck,
    [TOption.ItemEdit], sThisUnit);
finalization
  TCustomListView.UnregisterAppearances(
    TArray<TCustomListView.TItemAppearanceObjectsClass>.Create(
      TMultiDetailItemAppearance, TMultiDetailDeleteAppearance,
      TMultiDetailShowCheckAppearance));
end.
Ссылка на комментарий

Рекомендуемые сообщения

  • 0

Ярослав писал статью по отрисовке Listbox:

http://blogs.embarcadero.com/yaroslavbrovin/

Правда, это было полтотра года назад, FMX поменялась, и пример не будет работать под XE7

Но общий принцип стилизаци должен был остаться тем же.

Ссылка на комментарий
  • 0

Проблема в том что ListBox и ListView очень сильно отличаются. У ListBoxa итемы это контролы, и там все понятно как сделать. А вот у ListView итемы не контролы, т.е. стилизация для них чужда.

Ссылка на комментарий
  • 0

Есть ListView заполненный итемами. Фон у всех итемов одного цвета, по дефолту белого. Есть, допустим, кнопка на форме. По нажатии на эту кнопку нужно чтобы поменялся фон у 3-го итема в ListView. Это задача.

В своем проекте использую кастомизированные итемы для ListView из примеров "MultiDetailAppearance". Я думаю что пути решения 2: 

1) Заного перерисовать отдельный итем;

2) Добавить в класс "TMultiDetailItemAppearance" какой-нибудь "TIMage" и зарисовать его нужным цветом когда мне это понадобится, или "TRectAngel".

Как реализовать эти оба варианта, я не знаю.

Ссылка на комментарий
  • 0

Я согласен на потомков, но что там делать, чего добавлять. Можешь накидать? Хотя это наверное дело не 5 минут... В любом случае спасибо за попытку помочь :)

И не станет ли ListView после этих изменений таким же тормозным как ListBox...

Я потому и выбрал ListView что он не тормозит при скроле когда он состоит из 100-200 итемов на телефоне, чего нельзя сказать о ListBox.

 

Так что ListBox не вариант. 

Ссылка на комментарий
  • 0

См. исходники, конкретно часть

    TListViewItemObjects = class(TListItemObjects)
    private
      FAppearance: TItemAppearanceObjects;
      function GetDetailObject: TListItemText;
      function GetGlyphButton: TListItemGlyphButton;
      function GetImageObject: TListItemImage;
      function GetTextButton: TListItemTextButton;
      function GetTextObject: TListItemText;
      function GetAccessoryObject: TListItemAccessory;
      function FindObjectT<T: TListItemObject>(const AName: string): T;
    public
      procedure Clear; override;
      property TextObject: TListItemText read GetTextObject;
      property DetailObject: TListItemText read GetDetailObject;
      property ImageObject: TListItemImage read GetImageObject;
      property TextButton: TListItemTextButton read GetTextButton;
      property GlyphButton: TListItemGlyphButton read GetGlyphButton;
      property AccessoryObject: TListItemAccessory read GetAccessoryObject;
    end;

и думай, где и что тебе придется поменять, чтобы добавить в него еще один элемент - фон, с цветом/градиентом/картинкой/чем-нибудь-ещё

Ссылка на комментарий
  • 0

ну тебе же написали где копать. в FMX.ListView есть все. тебе нужно всего лишь добавить нужные свойства и переписать метод Render, который рисует итемобъект

Ссылка на комментарий
  • 0

Нашел вот это:

procedure TCustomListView.DrawItemsFill(const StartItem, EndItem: Integer; const LocRect: TRectF; const Opacity: Single;
  const HeaderIndex: Integer);
var
  I, Sep, AltIndex: Integer;
  DrawRect, DrawSubRect, SepRect: TRectF;
  ItemObjects: TListViewItem;
  SepHeight: Single;
begin
  SepHeight := GetSeparatorLineHeight;

  for I := StartItem to EndItem do
    if I <> HeaderIndex then
    begin
      ItemObjects := Items[I];

      if (ItemObjects <> nil) and ((ItemObjects.Count > 0) or (Items[Index - 1].Purpose <> TListItemPurpose.None)) then
      begin
        DrawRect := GetItemRelRect(I, LocRect);
        if ItemObjects.Purpose = TListItemPurpose.None then
        begin
          FBrush.Color := FItemStyleFillColor;

Тут "FBrush.Color := FItemStyleFillColor;" можно поменять на свой цвет, допустим в зависимоcти от свойства tag. Получится что-то типа :

if ItemObjects.Tag <> 0 then FBrush.Color := ItemObjects.Tag else FBrush.Color := FItemStyleFillColor;

Вопрос как применить эти изменения не изменяя FMX.ListView? Если написать Helper к TCustomListView в своем юните, то естественно он не подхватывается, а писать его в FMX.ListView ИМХО тупо, уж проще сам метод в TCustomListView поправить. Сделать наследника TlistView как я понял тоже гиблое дело из-за того что метод DrawItemsFill не виртуальный и его не переопределить. Как быть?

Изменено пользователем xenon54
Ссылка на комментарий
  • 0

новую версию ставить параллельно. сравнивать оригинальные исходники и вносить свои изменения )

Beyond Compare тебе в помощь....

 

п.с. я раньше тоже как и ты рассуждал )

Ссылка на комментарий

Присоединяйтесь к обсуждению

Вы можете написать сейчас и зарегистрироваться позже. Если у вас есть аккаунт, авторизуйтесь, чтобы опубликовать от имени своего аккаунта.

Гость
Ответить на вопрос...

×   Вставлено с форматированием.   Вставить как обычный текст

  Разрешено использовать не более 75 эмодзи.

×   Ваша ссылка была автоматически встроена.   Отображать как обычную ссылку

×   Ваш предыдущий контент был восстановлен.   Очистить редактор

×   Вы не можете вставлять изображения напрямую. Загружайте или вставляйте изображения по ссылке.

  • Последние посетители   0 пользователей онлайн

    • Ни одного зарегистрированного пользователя не просматривает данную страницу
×
×
  • Создать...