DELPHI盒子
!实时搜索: 盒子论坛 | 注册用户 | 修改信息 | 退出
检举帖 | 全文检索 | 关闭广告 | 捐赠
技术论坛
 用户名
 密  码
自动登陆(30天有效)
忘了密码
≡技术区≡
DELPHI技术
移动应用开发
Web应用开发
数据库专区
报表专区
网络通讯
开源项目
论坛精华贴
≡发布区≡
发布代码
发布控件
文档资料
经典工具
≡事务区≡
网站意见
盒子之家
招聘应聘
信息交换
论坛信息
最新加入: fsfwrfwsf
今日帖子: 5
在线用户: 4
导航: 论坛 -> 移动应用开发 斑竹:flyers,iamdream  
作者:
男 aminghanhua (阿明) ▲▲▲▲▲ -
注册会员
2022/8/17 10:57:29
标题:
Delphi 安卓平台 image载入大图的方法 浏览:853
加入我的收藏
楼主: 本人菜鸟一枚。LoadFromFile载入大图会造成UI卡顿,之前在Win平台使用PostMessage方法解决,请问在安卓平台解决载入大图卡顿的方法。
我尝试使用匿名线程的方式,网上查询会造成阻塞。求解决办法。
procedure TMainForm.ListBoxItem13Click(Sender: TObject);
var
  SearchField: string; // 要查找的字段名
  SearchValue: string; // 要查找的字段值
  ai: TAniIndicator;
begin
  TabControl3.TabIndex := 0;
  Text1.Text := '规划图';
  MainTab2CiyeTab.ExecuteTarget(self);

  SearchField := 'name';
  SearchValue := '%' + '规划图' + '%';
  ImageViewer1.Bitmap:=nil;
  if ImageViewer1.Bitmap.IsEmpty then
  begin
  TThread.CreateAnonymousThread(
    procedure()
    begin
      try
        TThread.Synchronize(TThread.CurrentThread,
          procedure()
          begin
          ai := TAniIndicator.Create(ImageViewer1);
          ai.Parent := ImageViewer1;
          ai.Style := TAniIndicatorStyle.Circular;
          ai.StyleLookup := 'aniindicatorstyle';
          ai.Height := 50;
          ai.Width := 50;
          ai.Position.X := (ImageViewer1.Width - ai.Width) / 2;
          ai.Position.Y := (ImageViewer1.Height - ai.Height) / 2;
          ai.Visible := true;
          ai.Enabled := true;
          end);
          sleep(10);
          TThread.Synchronize(TThread.CurrentThread,
          procedure()
          begin
          if System.SysUtils.FileExists(TPath.Combine(TPath.GetPublicPath,'规划图')) then
          begin
//          Application.ProcessMessages;
          ImageViewer1.Bitmap.LoadFromFile
          (TPath.Combine(TPath.GetPublicPath, '规划图.jpg'))
          end
          else
          begin
          TThread.Synchronize(TThread.CurrentThread,
          procedure()
          begin
          Toast('照片载入中...');
          with FDQuery3 do
          begin
          close; // 先关闭数据模块中的Query
          Sql.Clear; // 清空Query中的SQL值
          Sql.Add('select * from  heliushuixitu where ' + SearchField
          + ' like ''%' + SearchValue + '%''');
          Open();
          TBlobField(FieldByName('image'))
          .SaveToFile(TPath.Combine(TPath.GetPublicPath,'规划图.jpg'));
//          Application.ProcessMessages;
          ImageViewer1.Bitmap.LoadFromFile
          (TPath.Combine(TPath.GetPublicPath, '规划图.jpg'));
          end;
          end);
          end;
          end);
      finally
        ai.Enabled := False;
        ai.Visible := False;
        ai.Free;
      end;
      sleep(1000);
    end).start;
  end;
  ToolButtion.Visible := true;
  ToolShareButton.Visible := true;
end;
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/8/17 13:25:24
1楼: your procedure it's very confuse, in fact!

0) try dont mix many "threads" in cascade... it's complicated for analise and cause bugs!!! 

---- Synchronize( ... Synchronize(...) ...) !!!! it's wrong!!!
---------- synchronize ask to main-thread an "imediatelly update on the screen"!


0.1) dont mix "Threads" with "Application.ProcessMessages" it's not necessary...  use TTask() for parallel processing if necessary!!!

0.2) dont use "Sleep(...)" into Threads... if you really needs, use "TThread.Sleep(...)" or "System.SyncObjs.TEvent.SetEvent(...)" for threads!!!


1) try separate each task in a "little procedures"

2) try dont mix object-creation together with its usage!

3) using "Synchronize(...)" you are forcing an "updates imediately" in your screen... if any task is more quick than your screen-updates, your updates can dont appears like expected...."  (it will be delayed or same it dont appears in time)... try usar "Queue(...)" for put it in queue.... then your main-thread can works!

4) you can use "TLayout"s to create your UI with all components/controls "invisible" and when you need it, just "visible=true"... it's more quick than create all time.

5) do the test with little parts until you know "how all works"?



at end, works with "threads" it's not so easy!!! needs very study about it!!!
Think about!
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/8/17 13:27:10
2楼: look for your code:


TThread.CreateAnonymousThread(
  TThread.Synchronize(TThread.CurrentThread,
     TThread.Synchronize(TThread.CurrentThread,
//     Application.ProcessMessages;
          TThread.Synchronize(TThread.CurrentThread,
//          Application.ProcessMessages;


how this works?  this structure makes no sense!!!!
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 hs_kill (lzl_17948876) ★☆☆☆☆ -
普通会员
2022/8/17 19:09:31
3楼: 你这代码略鬼畜啊.....
开了个线程, 然后在线程里执行了2个同步函数...那你这线程有个毛线用啊

你说加载图像慢, 那也就是Bitmap.LoadFromFile慢, 所以你只需要在线程里创建个bitmap变量去LoadFromFile, 然后同步界面去加载这个bitmap就行了

至于界面初始化的那些乱七八糟的东西, 放到主进程里执行吧, 线程同步还有切换问题, 更慢
----------------------------------------------
http://www.cnblogs.com/lzl_17948876/
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/8/18 0:17:28
4楼: 写了一大段,提示我有非发字符。提交不了。贴到我的博客里面去。有兴趣的去我的博客看。
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/8/18 0:30:18
5楼: if you needs just read a field-Picture and show in your screen using a ListBox like a parameter of search, I think that is not necessary use "THREAD" for a little task!!!


try my simple code...
-- for test, using a FDMemTable with "BioLife.fds" from FireDAC samples in RAD Studio


implementation

{$R *.fmx}

procedure TForm1.MyFillListBox(ADataSet: TFDMemTable); // or using LiveBindings....
begin
  Form1.ListBox1.Items.Clear;
  //
  if (ADataSet = nil) then
    exit;
  //
  while not ADataSet.Eof do
  begin
    Form1.ListBox1.Items.Add(Format('%d_%s', [ADataSet.FieldByName('Species No').AsInteger, ADataSet.FieldByName('Common_Name').AsString]));
    //
    ADataSet.Next;
  end;
end;

function TForm1.MyReadMyBlobFieldBitmap(ABlobField: TBlobField): TBitmap;
var
  MyStream          : TStream;
  MyBufferAnsiString: AnsiString;
  MyStringPosFounded: integer;
begin
  result := nil;
  //
  if (ABlobField = nil) or not(ABlobField.DataType = TFieldType.ftBlob) or (ABlobField.IsNull) then
    exit;
  //
  MyStream := Form1.FDMemTable1.CreateBlobStream(ABlobField, TBlobStreamMode.bmRead);
  try
    if (MyStream.Size > 0) then
    begin
      SetLength(MyBufferAnsiString, 100); // reading 100st bytes for found "BM" ---> Bitmap!!! if "ok" then load it to TImage...
      MyStream.Read(PAnsiChar(MyBufferAnsiString)^, 100);
      //
      MyStringPosFounded := Pos('BM', string(MyBufferAnsiString));
      if (MyStringPosFounded > -1) then
        MyStream.Position := MyStringPosFounded - 1;
      //
      result := TBitmap.Create; // need free after usage...
      result.LoadFromStream(MyStream);  // here it can raise an exception if not possible load a "bitmap"....!!!
    end;
  finally
    MyStream.Free;
  end;
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  FDMemTable1.Open;
  //
  MyFillListBox(FDMemTable1);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  FDMemTable1.Close;
end;

procedure TForm1.MyFoundMyID(AID: integer);
var
  MyBmp: TBitmap;
begin
  if (ListBox1.ItemIndex = -1) then
    exit;
  //
  AID := StrToIntDef(ListBox1.Items[ListBox1.ItemIndex].Substring(0, Pos('_', ListBox1.Items[ListBox1.ItemIndex]) - 1), -1); // my "Species No" on string....
  //
  if FDMemTable1.Locate('Species No', VarArrayOf([AID])) then
  begin
    MyBmp := MyReadMyBlobFieldBitmap(TBlobField(Form1.FDMemTable1Graphic));
    try
      Image2.Bitmap.Assign(MyBmp);
    finally
      MyBmp.Free;
    end;
  end;
end;

procedure TForm1.ListBox1Click(Sender: TObject);
begin
  MyFoundMyID(ListBox1.ItemIndex)
end;

initialization

ReportMemoryLeaksOnShutdown := true;

finalization

end.
此帖子包含附件:
PNG 图像
大小:94.1K
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/8/18 0:33:56
6楼: 写好了。地址:

https://blog.csdn.net/pcplayer/article/details/126396870
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/8/18 0:48:54
7楼: screeshot2
此帖子包含附件:
PNG 图像
大小:155.6K
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 aminghanhua (阿明) ▲▲▲▲▲ -
注册会员
2022/8/18 11:03:23
8楼: 感谢楼上的帮我解决问题,本人菜鸟,不太懂。最后还是用线程解决载入大图导致界面卡顿的问题。
var
  Form1: TForm1;
  SearchField: string; // 要查找的字段名
  SearchValue: string; // 要查找的字段值
  ai: TAniIndicator;

implementation

{$R *.fmx}


constructor TLoadImage.Create;
begin
  inherited Create(False);
  FreeOnTerminate := True;
end;

destructor TLoadImage.Destroy;
begin
  inherited;
end;

procedure TLoadImage.DoExecute;
begin
  try
    Form1.ImageViewer1.Bitmap.LoadFromFile (TPath.Combine(TPath.GetPublicPath, '规划图.jpg'));
  finally
    ai.Visible := false;
    ai.Enabled := false;
    ai.Free;
  end;
end;

procedure TLoadImage.Execute;
begin
  DoExecute;
end;

procedure TForm1.FDConnection1BeforeConnect(Sender: TObject);
begin
  FDConnection1.Params.Values['Database'] := TPath.Combine(TPath.GetPublicPath,'ImageDb.db');
end;

procedure TForm1.ListBoxItem1Click(Sender: TObject);
var
 FLoadImage:TLoadImage;
begin
  ChangeTabAction1.ExecuteTarget(self);
  try
    ai := TAniIndicator.Create(Form1.ImageViewer1);
    ai.Parent := Form1.ImageViewer1;
    ai.Style := TAniIndicatorStyle.Circular;
    ai.StyleLookup := 'aniindicatorstyle';
    ai.Height := 50;
    ai.Width := 50;
    ai.Position.X := (Form1.ImageViewer1.Width - ai.Width) / 2;
    ai.Position.Y := (Form1.ImageViewer1.Height - ai.Height) / 2;
    ai.Visible := true;
    ai.Enabled := true;
  except

  end;

  SearchField := 'name';
  SearchValue := '%' + '规划图' + '%';

  with FDQuery1 do
  begin
    close;
    Sql.Clear;
    Sql.Add('select * from  heliushuixitu where ' + SearchField + ' like ''%' + SearchValue + '%''');
    Open();
    TBlobField(FieldByName('image')).SaveToFile(TPath.Combine(TPath.GetPublicPath, '规划图.jpg'));
  end;

  FLoadImage:=TLoadImage.Create;
end;
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/8/18 13:58:27
9楼: 楼上,看你的代码,没有线程。

另外,看你的代码,是从数据库的 BLOB 字段把文件数据调出来,保存为文件,再加载文件。

那么,你的代码耗时的原因,可能是从数据库取数据出来。尤其是你的客户端和数据库之间的网 - 络 - 带 - 宽不够的时候。

对数据库的操作不涉及界面,所以,对数据库的操作,也就是你的打开那个表的操作,是可以放进线程里面执行的。

另外,数据库的表打开了,BLOB 字段有内容了,没必要保存为文件,再从文件加载。直接内存加载就好了。保存为文件再从文件加载,两次读写磁盘,也是很慢很耗时的。

当然,把图片数据加载到图片控件上显示这个动作,不能在线程里面执行。
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/8/18 13:58:56
10楼: MD,网 - 络 - 带 - 宽 是非法敏感词。
----------------------------------------------
-
作者:
男 aminghanhua (阿明) ▲▲▲▲▲ -
注册会员
2022/8/18 14:43:28
11楼: 感谢楼上的解答!
不知道这种是不是叫子线程,我想解决的问题是载入大图界面不卡顿,我用这种方式解决了UI阻塞的问题,界面不卡顿知识有载入的时间。
同时为了别的功能,没有直接使用内存流,而是保存成了图片。
再次感谢!
----------------------------------------------
-
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/8/18 18:58:43
12楼: 楼上,你的问题就是 UI 阻塞。

大图片假设载入时间长,比如需要10秒,那个不管你用线程还是用主线程,都是没办法解决的。你的问题是:这 10 秒里面,界面不要冻结成死机一样。

所以,你要把耗时的事情,和界面无关的事情,都放到线程里面取做。这样,界面不会死,当然,图片仍然需要 10 秒后才会出来。但因为界面没有死,你可以显示进度,也可以让用户在等待过程中做其它操作。

我那篇博客文章里面(本来要贴到这里的,但提示有敏感词),另外提到的是,把图片数据赋予界面显示控件比如 TImage 的代码,必须是主线程执行。假设图片太大,这一步也很耗时,那也会把界面阻塞卡住,如何解决?解决办法:显示小图。把大图缩成小图可能本身也很耗时,但这个过程因为没有涉及到界面,可以放到线程里面去做。

总之,就是把耗时的事情放到线程里面去做,最后显示的代码,必须放到主线程去做。

现在用 Delphi 写这样的代码,代码架构会比较简单,比以前专门要创建一个 TThread 的继承类的写法,简单很多:

TTask.Run(
  procedure
  begin
     DoSomeThings;//非常耗时的操作放在这里,就是由线程来做;
     TThread.Synchronize(nil,
       procedure
       begin
         ShowYourPicture; //操作界面控件带来界面变化的代码,放进主线程。
       end
     );
  end

)


这样的代码就解决你的问题了。懂了这个原理,其它任何的操作如果遇到界面冻结,都可以这样处理。
----------------------------------------------
-
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/8/19 2:36:04
13楼: by @pcplayer
"当然,把图片数据加载到图片控件上显示这个动作,不能在线程里面执行。"

Here's a caveat in the comment:

It is possible to process the image request in the DB or even in a URL, and, through a technique, for example, using "interfaced classes" to send the result (the image) directly to the method responsible for processing (for example, show the image on the screen of that form), and continue the thread's actions.

In the case of mass processing by the thread, it may be necessary to create a "list" of each result already processed that will be passed to the interfaced class, which will be responsible for filtering what it is really interested in.

For example, a form that displays a list of images for each item in a ListBox's list, or a list of images in a Frames list, etc...

Naturally, it will be necessary to adapt the objects (the one that receives the response from the thread's processing) so that there is no conflict between sending and receiving information.

So it's not a very simple task for beginners. Especially when it comes to "threads"...
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 ddrfan (若苗瞬) ▲▲▲▲△ -
注册会员
2022/8/19 10:14:11
14楼: 楼主后面贴的代码,我觉得有点问题:

TLoadImage
应该是线程类,只是没贴声明部分。这没问题。

但是执行部分又没同步,直接操作了UI。
procedure TLoadImage.DoExecute;
begin
  try
    Form1.ImageViewer1.Bitmap.LoadFromFile ("xxxx");
  finally
    ai.Visible := false;
    ai.Enabled := false;
    ai.Free;
  end;
end;
实测这样都OK么?

最后,全局变量ai := TAniIndicator.Create(Form1.ImageViewer1);
在主线程点击时创建。
在线程执行后销毁……

那么(既然不卡UI了)连续点2下ListBox的项目,应该程序就报错了吧?
----------------------------------------------
Bye bye DDRFAN...
作者:
男 emailx45 (emailx45) ▲▲▲▲△ -
注册会员
2022/8/19 10:17:38
15楼: as I said, "he needs so much study about thread to learn that is not so easy copy a internet copy and all work...!
----------------------------------------------
The higher the degree, the greater the respect given to the humblest!
作者:
男 pcplayer (pcplayer) ★☆☆☆☆ -
普通会员
2022/8/19 17:49:12
16楼: 楼主,针对你的对多线程概念不清楚的情况,我又写了一个博客:

https://blog.csdn.net/pcplayer/article/details/126427519
----------------------------------------------
-
作者:
男 glings (glings) ★☆☆☆☆ -
普通会员
2022/8/19 19:45:03
17楼: pcplayer 热心人!按此在新窗口浏览图片
----------------------------------------------
-
作者:
男 luckyrandom (luckyrandom) ★☆☆☆☆ -
普通会员
2022/8/19 21:51:36
18楼: 感谢pcplayer。。又学到一招
之前看过delphi的多线程,云里雾里,后来一深圳大神写了个示例才搞懂
资质有限,尴尬。。尴尬
----------------------------------------------
SQL SERVER DBA QQ:315054403 曾经的Delphier  缘在上海
作者:
男 ddrfan (若苗瞬) ▲▲▲▲△ -
注册会员
2022/8/19 22:08:54
19楼: 哈哈,变成了多线程基础教学:)
确实用新版的 TTask 方便很多。
可以下面这样等待线程完成:

...
var
 tasks: TArray<ITask>;
...
Setlength (tasks ,xxx);

for i:=0 to xxx-1 do
begin
  tasks[i] :=
  TTask.Create (procedure
  begin
    DoSomething();
  end);
  tasks[i].Start;
end
TTask.WaitForAll(tasks);

但是传值还是有问题,比如一堆文件,那么 FileName[i] 直接是传不进去的。
----------------------------------------------
Bye bye DDRFAN...
信息
登陆以后才能回复
Copyright © 2CCC.Com 盒子论坛 v2.1 版权所有 页面执行54.6875毫秒 RSS