Delphi中保存图像列表
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Delphi中保存图像列表相关的知识,希望对你有一定的参考价值。
参考技术A 最近在做项目时遇到将图像列表(TImageList)中一系列的图像保存到指定的文件或二进制流中 以便在需要时进行动态恢复的情况 于是在Delphi的帮助中查找TImageList类相关的属性 方法 遗憾的是Delphi在TImageList中并未提供SaveToFile和SaveToStream方法 所以针对TImageList目前的限制 必须采取其它的办法来扩展TImageList的功能 以满足实际项目的需要 一 解决方法 方法一 使用API函数ImageList_Write和ImageList_Read 二者都需要指定一个类型为IStream的参数 前者的作用是将指定句柄的图像列表保存到类型为IStream的二进制流中 后者是从类型为IStream的二进制流中读出原先保存的图像列表 并且返回指向这个图像列表的句柄 IStream是一个OLE对象 它在Delphi中的声明为TStreamAdapter = class(TInterfacedObject IStream) 意为TStreamAdapter是从TInterfacedObject继承下来的操纵 IStream接口的对象 通过TStreamAdapter对象可以实现Delphi内部TStream对象对ISTream接口对象的操纵 方法二 从TImageList继承一个子类TImageListEx 实现自定义的SaveToFileEx和SaveToStreamEx方法 在默认情况下TImageList中保存的图像是由普通图像及其掩码图像组合而成 所以必须调用其基类TCustomImageList的Protected部分提供的GetImages(Index: Integer; Image Mask: TBitmap)方法 以获得图像列表中指定索引号的位图及其掩码位图 之后分别保存到自定义的文件或二进制流中 此外还需提供LoadFromFileEx和LoadFromStreamEx方法从自定义的文件或二进制流中恢复图像集合二 实现步骤 自定义的TImageListEx控件在Public部分一并实现了对上述两种方法的封装 TImageListEx类源代码如下 unit ImageListEx;
interface
uses Windows SysUtils Classes Graphics Controls Commctrl ImgList Consts;
type TImageListEx = class(TImageList) public procedure LoadFromFile(const FileName: string);//实现API方式保存 procedure LoadFromStream(Stream: TStream); procedure SaveToFile(const FileName: string); procedure SaveToStream(Stream: TStream); procedure LoadFromFileEx(const FileName: string);//实现自定义方式保存 procedure LoadFromStreamEx(Stream: TStream); procedure SaveToFileEx(const FileName: string); procedure SaveToStreamEx(Stream: TStream); end;
procedure Register;
implementation
procedure Register; begin RegisterComponents( ImageListEx [TImageListEx]); end;
TImageListEx
procedure TImageListEx LoadFromFile(const FileName: string); var Stream: TStream; begin Stream := TFileStream Create(FileName fmOpenRead); try LoadFromStream(Stream); finally Stream Free; end; end;
procedure TImageListEx LoadFromFileEx(const FileName: string); var Stream: TStream; begin Stream := TFileStream Create(FileName fmOpenRead); try LoadFromStreamEx(Stream); finally Stream Free; end; end;
procedure TImageListEx LoadFromStream(Stream: TStream); var SA: TStreamAdapter; begin SA := TStreamAdapter Create(Stream); try Handle := ImageList_Read(SA);//将当前图像列表的句柄指向从二进制流中得到的句柄 if Handle = then raise EReadError CreateRes(@SImageReadFail); finally SA Free; end; end;
procedure TImageListEx LoadFromStreamEx(Stream: TStream); var Width Height: Integer; Bitmap Mask: TBitmap; BinStream: TMemoryStream;
procedure LoadImageFromStream(Image: TBitmap); var Count: DWORD; begin Image Assign(nil); Stream ReadBuffer(Count SizeOf(Count));//首先读出位图的大小 BinStream Clear; BinStream CopyFrom(Stream Count);//接着读出位图 BinStream Position := ;//流指针复位 Image LoadFromStream(BinStream); end;
begin Stream ReadBuffer(Height SizeOf(Height)); Stream ReadBuffer(Width SizeOf(Width)); Self Height := Height; Self Width := Width;//恢复图像列表原来的高度 宽度 Bitmap := TBitmap Create; Mask := TBitmap Create; BinStream := TMemoryStream Create; try while Stream Position <> Stream Size do begin LoadImageFromStream(Bitmap);//从二进制流中读出位图 LoadImageFromStream(Mask);//从二进制流中读出掩码位图 Add(Bitmap Mask);//将位图及其掩码位图合并添加到图像列表中 end; finally Bitmap Free; Mask Free; BinStream Free; end; end;
procedure TImageListEx SaveToFile(const FileName: string); var Stream: TStream; begin Stream := TFileStream Create(FileName fmCreate); try SaveToStream(Stream); finally Stream Free; end; end;
procedure TImageListEx SaveToFileEx(const FileName: string); var Stream: TStream; begin Stream := TFileStream Create(FileName fmCreate); try SaveToStreamEx(Stream); finally Stream Free; end; end;
procedure TImageListEx SaveToStream(Stream: TStream); var SA: TStreamAdapter; begin SA := TStreamAdapter Create(Stream); try if not ImageList_Write(Handle SA) then//将当前图像列表保存到二进制流中 raise EWriteError CreateRes(@SImageWriteFail); finally SA Free; end; end;
procedure TImageListEx SaveToStreamEx(Stream: TStream); var I: Integer; Width Height: Integer; Bitmap Mask: TBitmap; BinStream: TMemoryStream;
procedure SetImage(Image: TBitmap; IsMask: Boolean); begin Image Assign(nil);//清除上一次保存的图像 避免出现图像重叠 with Image do begin if IsMask then Monochrome := True;//掩码位图必须使用单色 Height := Self Height; Width := Self Width; end; end;
procedure SaveImageToStream(Image: TBitmap); var Count: DWORD; begin BinStream Clear; Image SaveToStream(BinStream); Count := BinStream Size; Stream WriteBuffer(Count SizeOf(Count));//首先保存位图的大小 Stream CopyFrom(BinStream );//接着保存位图 end;
lishixinzhi/Article/program/Delphi/201311/8481
Flutter中如何保存每个列表对象的状态
【中文标题】Flutter中如何保存每个列表对象的状态【英文标题】:How to preserve the state of each list object in Flutter 【发布时间】:2020-03-31 06:50:02 【问题描述】:我有一个网络套接字,它发送带有一些数据的图像,然后我将它们显示在 AnimatedList
中。问题是每个新数据都进来了,它会重新加载列表中的每一个项目,列表会重新加载每个图像。也因为图片是base64转换的,所以不能兑现。
有什么方法可以保留AnimatedList
中每个对象的状态,以便在添加新项目时不会重新加载列表中的所有其他对象/图像。
AnimatedList
样本:
AnimatedList(
key: node.validListKey,
controller: widget.scrollController,
padding: EdgeInsets.fromLTRB(10, 10, 0, 10),
initialItemCount: node.data.length,
itemBuilder: (context, index, animation)
return FadeTransition(
opacity: CurvedAnimation(
parent: animation,
curve: Interval(0, 0.2, curve: Curves.ease)),
child: SizeTransition(
sizeFactor:
CurvedAnimation(parent: animation, curve: Curves.ease),
child: DataContainer(data[index])
),
);
,
),
基本解码示例:
child: AspectRatio(
aspectRatio: 1 / 1,
child: ExtendedImage.memory(
base64.decode(data.base),
fit: BoxFit.cover,
loadStateChanged: (state)
if (state.extendedImageLoadState ==
LoadState.completed)
return ExtendedRawImage(
image: state.extendedImageInfo?.image,
);
return Center(child: CircularProgressIndicator());
,
),
),
更新#1 似乎问题在于我如何在列表中插入项目。因为我在列表的开头插入了新项目,由于某种原因,它只为最后一个元素调用 init 状态,而不是最近添加的元素。当我在列表末尾插入时,问题就解决了。有什么办法可以强制为最近添加的项目而不是最后一个项目调用初始化状态?
插入项目示例:
array.add(data);
array.insert(0, data);
if (key.currentState != null)
key.currentState.insertItem(0, duration: _insertDuation);
更新#2
因此,我能够通过将 AnimatedList
包裹在 SingleChildScrollView
中,添加 reverse: true
、shrinkWrap: true
并在 initState
方法中添加 decode
来解决我的图像重新加载的第一个问题。
SingleChildScrollView(
child : ListView.builer(
shrinkWrap: true,
reverse: true,
physics: NeverScrollableScrollPhysics(),
itemBuilder: (BuildContext context, int index)
...
,
)
);
但现在问题出在remove
方法上。当我尝试从列表中删除项目时,基本小部件内容保持不变,但图像会更改一个索引。我已经尝试为每个小部件添加唯一键,但是在添加数据时会强制重新加载每个图像。
_data.removeAt(0);
if (_validListKey.currentState != null)
_validListKey.currentState.removeItem(
0, (context, animation) => Container(),
duration: Duration.zero);
【问题讨论】:
很可能是您的node.validListKey
发生了变化。 AnimatedList
不会破坏列表项的状态
@RémiRousselet 我使用Provider
包来管理AnimatedList
(final NodeHelper node = Provider.of<NodeHelper>(context, listen: false);
) 的状态和密钥。这会是个问题吗?
【参考方案1】:
您应该在 initState
函数(或在 initState
中触发的异步函数)中执行 base64.decode(data.base)
并缓存结果。
您的构建函数不应触发此解码,或者不应运行任何具有副作用的代码。现在,在每次构建时,您都在进行解码并为ExtendedImage
提供不同的Uint8List
实例。如果ExtendedImage
被正确实现,当它在新的build()
调用中被赋予相同的引用时,它不应该做任何UI 更改。
【讨论】:
问题是在initState
中我一直只得到第一个值,直到它离开屏幕。因此,所有图像都是相同的。也许问题出在 Keys
... 之类的其他东西上以上是关于Delphi中保存图像列表的主要内容,如果未能解决你的问题,请参考以下文章