手机 APP 的卖货界面

44 篇文章 0 订阅
18 篇文章 2 订阅

需求

网上买菜的微信小程序,其典型界面是左侧一个列表显示商品分类,右侧一个列表,显示商品明细。左侧列表要显示当前选中的是哪条分类记录(高亮这条记录)。右侧列表滑到底部后,往上再滑一次,自动切换到下一个分类,此时左侧列表的选中画面也同时要更新(高亮下一条记录)。

使用 Delphi 的实现

使用 Delphi 做手机 APP,不管是安卓还是 iOS,都是同一套代码,也就是采用 Delphi 的 FireMonkey 框架。

在 FireMonkey 框架下,要显示列表,比较常见的是 TListBox 控件,和 TListView 控件。

这里,采用 TListView 控件,比较简单。

实现方法

首先搞定数据。不管数据从哪里来,比如现在流行的是服务器有 REST 接口,那么,Delphi 程序可以调用 REST 接口获得 JSON 数据。具体做法这里不讨论。

获得数据以后,把数据放进 TFdMemTable 里面,用这个内存表来维护数据。

这里因为有一个分类数据和一个具体商品数据,那么,数据结构大概是:

分类数据:

分类编号,类名称

商品数据:

分类编号,商品编号,商品名称

内存表

那么,拖两个 TFdMemTable 到界面上(当然,应该放到一个 DataModule 里面去,这里仅仅是描述界面实现方法,所以就直接放界面上了),一个 FdMemTable1 用来存放分类数据,一个 FdMemTable2 用来存放商品名称数据。为它创建好字段。然后,鼠标右键点击一个 FdMemTable,在下拉菜单里面选择 Edit DataSet 就会看到一个表格,在这个表格里面填入一些数据。两个 FdMemTable 都填入数据,在设计期,你就已经有数据了。程序运行后,也能看到数据。这样不需要等写完从服务器拉数据的代码,就能看到界面效果。

界面

拖两个 ListView 到界面上,第一个 ListView1 的 Align 设置为 Left,让它占满屏幕左侧;第二个 ListView2 的 Align 设置为 Client,让它占满屏幕剩下部分。要想界面美观,设置一下这两个 ListView 的 Margin 的 Left 和 Right 的数字,从默认的 0 改为 2.

鼠标右键点击随便哪个 ListView ,在弹出来的下拉菜单,选择:Bind Visually,Delphi 的 IDE 会出现 Live Bindings Designer 窗口。在这个窗口里面,对 ListView 和 FdMemTable 拉线,实现在设计期的数据绑定。 这时候,已经可以在设计期的界面上看到 ListView 显示之前在 FdMemTable 里面输入的数据了。运行程序,确实能看到界面数据。

调试

在 Delphi 里面开发手机 APP,不必每次都把程序运行到手机上去调试看运行效果。可以选择编译目标是 WIN32,点 Delphi 界面上的绿色三角按钮(播放按钮,也就是运行程序的按钮),直接就在 Windows 里面看到这个程序编译为 Windows 程序运行起来了。同样也能看到 ListView 的效果。这样做比编译发布运行到手机上,时间短很多。

界面效果

1. 左侧分类的 ListView1 上面,用户选择(触摸,或者鼠标点击)一条记录,右侧 ListView2 上面要显示对应的分类的商品,而不是显示全部商品;同时左侧要高亮选中的分类条目;

2. 用户在右侧往上划动,右侧商品列表网上滚动,当滚动到最底一条时,用户再次往上划,左侧分类自动走到下一条分类,右侧的 ListView 显示对应的新的分类的商品列表。

界面效果实现

1. 把两个内存表做成主从表。这样,当主表的当前游标指向某条记录,从表跟随主表,自动被过滤,只能看到从表对应的主表的记录;

2. ListView2 滑到底部后,如何自动让分类表也就是主表走到下一条?

一. 主从表的实现

Delphi 里面,两个或者多个 DataSet 可以实现主次关系。实现起来非常简单,网上很多文章有讲到。这里只简单说一下:

1. 拖一个 DataSource1 到界面上,设置它的 DataSet 属性为 FdMemTable1(也就是商品分类表,主表);

2. 选择 FdMemTable2 商品列表,在属性窗口里面找到三个属性:

2.1. MasterSource,设置为 DataSource1;

2.2. MasterField,设置为【商品分类编号】字段的名字;

2.3. IndexFieldNames,设置为【商品分类编号】字段的名字。

到此搞定这两个表的主从表关系。

二. 显示商品列表的从表的 ListView2 滑到底

ListView2 滑到底,就执行 FdMemTable1.Next,则 ListView1 上显示的高亮记录自动走到下一条。如果分类走到底,就让它自动回到第一条:

with FdMemTable1 do
begin
  Next;
  if Eof then First;
end;

但是,我怎么知道这个 ListView2 已经滑到底了?

FireMonkey 的 TListView 有个下拉刷新的功能。但这个是下拉,而不是上划。

如何判断 ListView 的条目往上滚动,已经滚动到最后一条?代码如下:

  MyBottom := Self.GetBottomValueByItemCount(FdMemTable2.RecordCount);

  if (ListView2.ScrollViewPos = MyBottom) or (MyBottom <= 0) then
  begin
    //说明滚动到底了
  end;


function TForm1.GetBottomValueByItemCount(const ACount: Integer): Integer;
begin
  //当前 ListView2.ScrollViewPos 的最大值,滚动到底部的位置
  //滚动到最底部的位置就是滚动到最底部时的 ListView2.ScrollViewPos 这个值;
  //最底部时,ListView2.ScrollViewPos 刚好等于【条数 X 条高 - ListView 自己的高度】
  //如果条数不够,没装满,则这里计算出来的值就是个负数值。刚好装满,则计算出来的值应该是 0 ;
  Result := Trunc(Listview2.ItemAppearance.ItemHeight * ACount - ListView2.Height);
end;

这个已经滑到底的代码,在哪里执行?

把它放到上划手势的事件里面。

手势操作

1. 拖一个 GestureManager1 到界面上,选择 ListView2 的属性面板里面的:Touch -- Gesture Manager 属性,下拉选择为这个 GestureManager1。

2. ListView2 属性面板里面的 Touch -- Gestures -- Standard -- 勾选 Up,这样当你在 APP 里面上划,就能触发 ListView2 的 OnGesture 事件。

3. 在 ListView2 的 OnGesture 事件里面写代码,把上述判断是否到底的代码填进去。

到这里,需求里面描述的效果,已经做出来了。如果是在 Windows 底下测试,你用鼠标拉右侧 ListView2 的滚动条,拉到最底(这里是往下拉),然后,鼠标到 ListView2 里面,按住左键往上移动鼠标,同样会触发手势的 Up 操作,就能看到测试结果。

这时候,可以把安卓手机插到电脑上,然后在 Delphi 里面直接选择编译目标为这个安卓手机,点击绿色三角形运行箭头,等 Delphi 执行完编译和下载安装 APP,就能看到手机上这个 APP 跑起来了。能够看到两个 ListView 也能看到里面的数据。滑动,确实有效果。

优化

这里还有两个问题,让用户体验没那么好

1. 手指往上划,ListView2 滚动到底部,还没来得及看清楚最后一条,它已经切换分类,显示另外一个分类的商品了;

2. 切换太快,没留意到它已经切换了。

解决办法

一. 不要滑到底就切换

而是滑到底,能够停下来让用户看清楚商品列表。当用户再次往上划,才切换。

实现方法:增加一个布尔标志变量,第一次滑到底时它是 False,而 False 时,它不执行切换分类的代码;

二. 切换放慢

模仿那些网页版 APP 刷新数据要从远程服务器重新获取数据,有点慢,转个圈,也就是转圈提示用户,商品分类在切换中,商品列表正在更新。

实现方法:拖一个转圈的控件(AniIndicator1)到界面上,设置它的 Visible 为 False,不要显示。然后在切换商品分类的时候,显示这个转圈,并且暂停1秒到2秒,然后再切换。暂停程序不能让界面冻结,所以暂停2秒要放到一个线程里面去执行。这里我使用 TTask 来执行。

切换到完整代码如下:

function TForm1.GetBottomValueByItemCount(const ACount: Integer): Integer;
begin
  //当前 ListView2.ScrollViewPos 的最大值,滚动到底部的位置
  //滚动到最底部的位置就是滚动到最底部时的 ListView2.ScrollViewPos 这个值;
  //最底部时,ListView2.ScrollViewPos 刚好等于【条数 X 条高 - ListView 自己的高度】
  //如果条数不够,没装满,则这里计算出来的值就是个负数值。刚好装满,则计算出来的值应该是 0 ;
  Result := Trunc(Listview2.ItemAppearance.ItemHeight * ACount - ListView2.Height);
end;

procedure TForm1.ListView2Gesture(Sender: TObject; const EventInfo:
    TGestureEventInfo; var Handled: Boolean);
var
  MyBottom: Integer;
begin
  //('手势');
  MyBottom := Self.GetBottomValueByItemCount(FdMemTable2.RecordCount);

  if (ListView2.ScrollViewPos = MyBottom) or (MyBottom <= 0) then
  begin
    if not FGoBottom then
    begin
      FGoBottom := not FGoBottom;
      Exit; //第一次拉到底,不要走。第二次拉到底,才算用户要刷新。
    end;

    //这样直接切换,太突然,视觉上感觉不好。可以考虑切换代码放到一个动画结束后。
    TTask.Run(
      procedure
      begin
        TThread.Synchronize(nil,
          procedure
          begin
            AniIndicator1.Visible := True;
            AniIndicator1.Enabled := AniIndicator1.Visible; //显示转圈圈
          end
        );

        Sleep(1500);

        TThread.Synchronize(nil,
          procedure
          begin
            FdMemTable1.Next;
            if FdMemTable1.Eof then FdMemTable1.First;
            FGoBottom := not FGoBottom;

            AniIndicator1.Enabled := not AniIndicator1.Enabled;
            AniIndicator1.Visible := AniIndicator1.Enabled;  //转圈隐藏不显示
          end
        )
      end
    );
  end;
end;

说了那么多,代码就这几行。这就是用 Delphi 做界面比较厉害的地方。大部分的代码不需要写,仅仅在设计期鼠标点几下做点设置就搞定。这就是现在流行的所谓【Low code】

进阶

TListView 的条目的内容,本身可以做得很复杂,比如包含商品名称,商品价格,商品图片,等等。这个也可以在设计期搞定而无需写代码。具体如何搞,请翻一翻本博客前面的文章。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值