Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4425.html
 

WinAnimationManager: Grid Layout

Опубликовано 06.06.2010 г. 21:57

Вторая статья про изучение Windows Animation Manager, где мы рассмотрим вариант реализации примера MSDN: Grid Layout Sample с использованием Delphi. Суть данного демонстрационного приложения заключается в том, что оно создает превью изображений, располагающихся в библиотеке изображений, и располагает их на форме в виде таблицы, при изменении размеров формы таблица перестраивается, с использованием анимации.

Сразу приведем изображение конечного результата, что бы вы решили хотите читать дальше или нет (:
 
Когда трансляция примера с VC на Delphi была почти завершена, я столкнулся с проблемой использования импортированной библиотеки типов UIAnimation.dll. И по началу я задумал было переписать заголовочный файл, но потом вспомнил про гугл, и в результате нашел сайт www.bilsen.com, автор которого кроме того что качественно переписал хэдеры, но еще и выложил полнофункциональную демку использования Windows Animation Manager (с GDI+). Поэтому скачав заголовочный файл я начал его использовать. Наш пример будет как и прежде основан на использовании Direct2D. Итак, разобьем нашу программу на 3 составных части:
  1. Класс описания preview изображения TThumbnail
  2. Менеджер для управления и позиционирования превью - TLayoutManager
  3. и непосредственно форма на которой это все будет выводится.
Класс TThumbnail будет представлять миниатюру изображения, которую мы будем выводить на форме. Следовательно описание класса будет содержать непосредственно картинку - bitmap : ID2D1Bitmap. При изменении размеров формы, миниатюры будут менять свое местоположение, с использованием анимации, поэтому для анимирования каждая миниатюра должна содержать переменные координаты x,y : IUIAnimationVariable. Также мы будем отображать название файла, следовательно его также надо сохранить в filename : string; Для создания переменных нам потребуется указатель на IUIAnimationManager. Далее следуют методы класса: во первых конструктор/деструктор. В конструкторе мы получим картинку, указатель на менеджер, и начальные координаты. Нам также понадобится функция, возвращающая размер миниатюры getSize, и самое главное метод рисования - render(), параметрами которого будут канва, и кисть для рисования рамки. В конечном итоге описание класса выглядит следующим образом:
    TThumbnail = class (TObject)
      strict private
        animationManager : IUIAnimationManager;
        bitmap : ID2D1Bitmap;
      public
        varX, varY : IUIAnimationVariable;
        filename : string;

        destructor Destroy();
        constructor Create( pBitmap : ID2D1Bitmap;
                            aManager : IUIAnimationManager;
                            x,y : double);

        function getSize() : TD2D1SizeF;
        procedure Render(canvas : TDirect2DCanvas; outlineBrush : ID2D1Brush) ;
    end;
Реализация методов класса весьма проста, при создании объекта, мы также создаем переменные vaX,varY. При отрисовке мы получаем значения переменных,и в полученных координатах рисуем изображение, потом рамку вокруг него, и затем имя файла чуть ниже. Далее очередь класса TLayoutManager. Для чего он? Допустим изображения на нашей форме выводятся в 2 строки, по 4 штуки в ряд. Уменьшив размер формы, нам потребуется перестроить порядок, например получив три строки, в первых двух будет по 3 изображения, в третьей - два. Следовательно основная функция данного класса - при изменении размеров формы рассчитать новое местоположение для всех миниатюр, и соответствующим образом настроить анимацию. Следовательно класс будет хранить все требуемые ссылки на объекты анимации - IUIAnimationManager, IUIAnimationTimer, IUITransitionLibrary. (основные экземпляры хранятся в главной форме, ибо анимация может потребоваться и не только в данном случае, но в других местах программы). Также само собой потребуется хранить список наших миниатюр, в виде TList<TThumbnail>. Пара методов для выстраивания миниатюр: основной Arrange будет определять на какой строке расположится миниатюра (по заполняемости слева направо). Второй - ArrangeRow будет выстраивать миниатюры находящиеся в одной строке. Также нам потребуется метод для создания планировщика и настройки анимации - AddThumbnailTransitions. Итого, описание класса принимает следующий вид:
TLayoutManager = class(TObject)
      protected
        AnimationManager  : IUIAnimationManager ;
        AnimationTimer    : IUIAnimationTimer ;
        TransitionLibrary : IUIAnimationTransitionLibrary;

        thumbs : TList<TThumbnail>;

        procedure ArrangeRow( pStoryboard : IUIAnimationStoryboard;
                             iThumbMin,iThumbMax : integer;
                             xRow,yRow : double;
                             heightMax : double );

        procedure AddThumbnailTransitions(
                             pStoryboard : IUIAnimationStoryboard;
                             pVariablePrimary : IUIAnimationVariable ;
                             valuePrimary : double;
                             pVariableSecondary : IUIAnimationVariable;
                             valueSecondary : double
                            ) ;
      public
        destructor Destroy();
        constructor Create(
                        pAnimationManager : IUIAnimationManager;
                        pAnimationTimer : IUIAnimationTimer ;
                        pTransitionLibrary : IUIAnimationTransitionLibrary ;
                        uThumbCount : Cardinal;
                        thumbsList : TList<TThumbnail> );

        procedure Arrange(sizeClient : TD2D1SizeF) ;
    end;
Теперь перейдем к описанию главной формы приложения. Канва имеет тип TDirec2DCanvas, как и ранее изменяет размер при изменении размера формы. Также перекрыта обработка сообщения WM_ERASEBKGND. Сразу напомню, что при изменении размера формы необходимо вызывать метод Arrange() менеджера миниатюр. При создании формы сделаем следующие вещи:
  1. Создаем канву
  2. Создаем главные объекты анимации
  3. Создаем градиентную кисть для заливки фона
  4. Создаем сплошную кисть, для обводки миниатюр
  5. Создаем список миниатюр thubms : TList<TThumbnail> и запускаем процедуру его заполнения FindImages().
В процедуре рисования, нам необходимо заполнить фон, и вывести все миниатюры, используя метод TThumbnail.Render(), т.е вызвать его для всех объектов в списке thumbs. Для построения списка миниатюр, нам потребуется перебрать все элементы библиотеки "Изображения", получить для каждого элемента его миниатюру, и создать соответствующий объект ее описывающий. Данная часть реализации приложения немного отличается от оригинального примера на VC, т.к там использовался интерфейс IThumbnailCache, описание которого отсутствует в Delphi. Рекурсивный перебор всех элементов библиотеки проведен с использованием интерфейса INamespaceWalk. Алгоритм добавления миниатюр получается следующий:
  1. Получить интерфейс IShellItem для библиотеки изображений.
  2. Создаем интерфейс INamespaceWalk и с помощью него получаем массив PItemIDList для всех элементов библиотеки.
  3. Для всех найденных изображений, получаем интерфейс IShellItem
  4. с помощью метода DecodeImageFromThumbCache (описан ниже) получаем миниатюры изображений.
  5. Создаем объект TThumbnail и добавляем его в список.
procedure TMainForm.FindImages();
var picLib, si : IShellItem;
    nsWalk : INamespaceWalk;
    pidl : array of PItemIDList;
    pidlCount : Cardinal;
    i:integer;
    bitmap : ID2D1Bitmap;
    size : TD2D1SizeF;
    tItem : TThumbnail;
    fname : pchar;
begin
    SHGetKnownFolderItem(FOLDERID_PicturesLibrary,
                KF_FLAG_CREATE, 0,
                IID_IShellItem, pointer(picLib));

    nsWalk := CreateComObject(CLSID_NamespaceWalker) as INamespaceWalk;
    nsWalk.Walk( picLib,NSWF_NONE_IMPLIES_ALL,1,nil);

    nsWalk.GetIDArrayResult(pidlCount, PItemIdList(pidl));

    for i:=0 to pidlCount-1 do begin
        SHCreateItemFromIDList(pidl[i],IID_IShellItem,si);
        DecodeImageFromThumbCache(si,bitmap);

        Bitmap.GetSize(size);
        tItem := TThumbnail.Create(bitmap,animationManager,clientWidth/2,-size.height/2 -1);
        si.GetDisplayName(SIGDN_PARENTRELATIVEPARSING,fname);
        tItem.filename := fname;
        thumbs.Add(tItem);
    end;

    pidl := nil;
    layoutManager := TLayoutManager.Create(animationManager,animationTimer,transitionLibrary,thumbs);
    canvas.RenderTarget.GetSize(size);
    layoutManager.Arrange(size);
end;
Получение миниатюры для заданного IShellItem объекта реализуем с помощью интерфейса IShellItemImageFactory, если объект представляет изображение, то получим миниатюру, если же это какой либо другой тип файла, то получим его иконку.
procedure TMainForm.DecodeImageFromThumbCache(item: IShellItem; var bitmap: ID2D1Bitmap);
const size : TSize = (cx : 96; cy : 96);
var ImageFactory :IShellItemImageFactory;
    hBm : HBitmap;
    tBm  : TBitmap;
begin
    item.QueryInterface(IID_IShellItemImageFactory,imageFactory);

    imageFactory.GetImage(size,0,hBm);
    tBm := TBitmap.Create;
    tBm.Handle := hBm;

    bitmap := canvas.CreateBitmap(tBm);
    imageFactory := nil;
    tBm.Free();
end;
Использование данного метода может тормозить программу, поэтому его не рекомендуется использовать в главном потоке. Собственно вы запросто в этом убедитесь если в вашей библиотеке изображений будет несколько сотен картинок. Полный исходный код можно посмотреть здесь [Спасибо Алексею Тимохину за подкинутую ссылку на SkyDrive :)]. Заголовочный файл UIAnimation, взятый по ссылке указанной в начале статьи прилагается. Единственное, что в файл были добавлены идентификаторы классов (в оригинале они были расположены в отдельном файле).

 

Метки:  Direct2D  |  animation  |  Grid Layout 

Комментарии

Erik
27.01.2013 в 00:46
Насколько быстро работает это решение? у меня на Windows XP программа вылетает на запуске.
teran
28.01.2013 в 02:15
Direct2D и Windows Animation Library доступны начиная с Windows Vista, так что не мудрено что на ХП не запускается (:
по скорости особо не с чем сравнивать, вроде неплохо было.
Foot Issues
31.07.2017 в 22:54
Today, I went to the beach front with my kids.
I found a sea shell and gave it to my 4 year old daughter and said "You can hear the ocean if you put this to your ear." She
put the shell to her ear and screamed. There was a hermit crab inside and it pinched her ear.
She never wants to go back! LoL I know this is totally off topic but I had to tell someone!
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно