键入时Firemonkey编辑/组合自动完成/自动提示

时间:2016-09-06 00:23:03

标签: delphi autocomplete firemonkey autosuggest

对于Windows / Android平台以及MacOS和iOS,使用Delphi / Firemonkey实现AutocompleteAutosuggest的方式是什么?

示例

当用户在Google搜索框中输入文字时,会显示一些快速建议。

VCL有很多实现IAutoComplete,但FMX的实现较少。需要的是 - FMX

3 个答案:

答案 0 :(得分:1)

我做了一些研究,并从不同的来源汇编了我在下面得到的内容。 我已使用open CommutativeSemiring CSR hiding (refl) open import Function using (_⟨_⟩_) zipWith-replicate-0# : ∀ {n}(xs : Vec Carrier n) → zipWith _+_ (replicate 0#) xs ≡ xs zipWith-replicate-0# [] = refl zipWith-replicate-0# (x ∷ xs) = proj₁ +-identity x ⟨ cong₂ _∷_ ⟩ zipWith-replicate-0# xs XE7/XE8上对此进行了测试。完全在FiremonkeyWin32上运行,非常确定Android

我曾经在计时器中调用建议,但下面的代码没有计时器。调用计时器或线程的过程是MacOS

TStyledSuggestEdit.DropDownRecalc

以下是您使用它的方式:

  • 创建多设备应用程序
  • 在高清表单上放置常用unit FMX.Edit.Suggest2; interface uses FMX.Edit.Style, FMX.Controls.Presentation, FMX.Controls.Model, FMX.Presentation.Messages, FMX.Edit, FMX.Controls, FMX.ListBox, System.Classes, System.Types; const PM_DROP_DOWN = PM_EDIT_USER + 10; PM_PRESSENTER = PM_EDIT_USER + 11; PM_SET_ITEMINDEX = PM_EDIT_USER + 12; PM_GET_ITEMINDEX = PM_EDIT_USER + 13; PM_GET_SELECTEDITEM = PM_EDIT_USER + 14; PM_SET_ITEMCHANGE_EVENT = PM_EDIT_USER + 15; PM_GET_ITEMS = PM_EDIT_USER + 16; type TSelectedItem = record Text: String; Data: TObject; end; TStyledSuggestEdit = class(TStyledEdit) private FItems: TStrings; FPopup: TPopup; FListBox: TListBox; FDropDownCount: Integer; FOnItemChange: TNotifyEvent; FItemIndex: integer; FDontTrack: Boolean; FLastClickedIndex: Integer; function _GetIndex: Integer; procedure _SetIndex(const Value: Integer); procedure _SetItems(const Value: TStrings); protected procedure CheckIfTextMatchesSuggestions; // used to find out if a typed text matches any of suggestions and then do select function GetListBoxIndexByText(const AText: string): Integer; procedure OnItemClick(const Sender: TCustomListBox; const Item: TListBoxItem); procedure PMDropDown(var AMessage: TDispatchMessage); message PM_DROP_DOWN; procedure MMDataChanged(var AMessage: TDispatchMessageWithValue<TDataRecord>); message MM_DATA_CHANGED; procedure PMSetSize(var AMessage: TDispatchMessageWithValue<TSizeF>); message PM_SET_SIZE; procedure PMGetItemIndex(var AMessage: TDispatchMessageWithValue<Integer>); message PM_GET_ITEMINDEX; procedure PMSetItemIndex(var AMessage: TDispatchMessageWithValue<Integer>); message PM_SET_ITEMINDEX; procedure PMGetItems(var AMessage: TDispatchMessageWithValue<TStrings>); message PM_GET_ITEMS; procedure PMGetSelectedItem(var AMEssage: TDispatchMessageWithValue<TSelectedItem>); message PM_GET_SELECTEDITEM; procedure PMSetItemChangeEvent(var AMessage: TDispatchMessageWithValue<TNotifyEvent>); message PM_SET_ITEMCHANGE_EVENT; procedure PMPressEnter(var AMessage: TDispatchMessage); message PM_PRESSENTER; procedure DoChangeTracking; override; procedure RebuildSuggestionList(AText: String); procedure RecalculatePopupHeight; procedure KeyDown(var Key: Word; var KeyChar: Char; Shift: TShiftState); override; procedure DropDownRecalc(ByText: string; Delay: integer = 100); //Delay parameter is a preparation for calling by a thread or a timer public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function _SelectedItem: TSelectedItem; property _Items: TStrings read FItems write _SetItems; property _ItemIndex: Integer read _GetIndex write _SetIndex; property _OnItemChange: TNotifyEvent read FOnItemChange write FOnItemChange; end; TStyleSuggestEditProxy = class(TPresentationProxy) protected function CreateReceiver: TObject; override; end; TEditSuggestHelper = class helper for TEdit public type private function GetIndex: Integer; procedure SetIndex(const Value: Integer); procedure SetOnItemChange(const Value: TNotifyEvent); function GetItems: TStrings; public procedure AssignItems(const S: TStrings); procedure ForceDropDown; procedure PressEnter; function SelectedItem: TSelectedItem; property OnItemChange: TNotifyEvent write SetOnItemChange; property ItemIndex: Integer read GetIndex write SetIndex; property Items: TStrings read GetItems; end; implementation uses FMX.Presentation.Factory, FMX.Types, System.SysUtils, System.Math, System.Rtti, uDsTimers.FMX, {$IFDEF MSWINDOWS}Winapi.Windows,{$ENDIF} System.UITypes; { TStyleSuggestEditProxy } function TStyleSuggestEditProxy.CreateReceiver: TObject; begin Result := TStyledSuggestEdit.Create(nil); end; { TStyledSuggestEdit } procedure TStyledSuggestEdit.CheckIfTextMatchesSuggestions; var I: integer; begin if FItemIndex = -1 then begin I := self.GetListBoxIndexByText(Edit.Text); if I <> -1 then try OnItemClick(nil, FListBox.ListItems[I]); //try-except: maybe missing items if calling from a timer event or within a thread FListBox.RemoveObject(FListBox.ListItems[I]); RecalculatePopupHeight; except end; end; end; constructor TStyledSuggestEdit.Create(AOwner: TComponent); begin inherited; FItems := TStringList.Create; FItemIndex := -1; FPopup := TPopup.Create(self); FPopup.Parent := Self; FPopup.PlacementTarget := Self; FPopup.Placement := TPlacement.Bottom; FPopup.Width := Width; FListBox := TListBox.Create(self); FListBox.Parent := FPopup; FListBox.Align := TAlignLayout.Client; FListBox.OnItemClick := OnItemClick; FDropDownCount := 5; FListBox.Width := Self.Width; FPopup.Width := Self.Width; FLastClickedIndex := -1; end; destructor TStyledSuggestEdit.Destroy; begin FPopup := nil; FListBox := nil; FItems.Free; inherited; end; procedure TStyledSuggestEdit.DoChangeTracking; begin inherited; if Edit.Text <> _SelectedItem.Text then FLastClickedIndex := -1; if not FDontTrack and (FLastClickedIndex = -1) then begin _ItemIndex := -1; DropDownRecalc(Edit.Text); end; end; function TStyledSuggestEdit.GetListBoxIndexByText(const AText: string): Integer; begin for Result := 0 to FListBox.Count - 1 do if FListBox.ListItems[Result].Text.ToLower = AText.ToLower then Exit; Result := -1; end; function TStyledSuggestEdit._GetIndex: Integer; begin Result := FItemIndex; end; procedure TStyledSuggestEdit.KeyDown(var Key: Word; var KeyChar: Char; Shift: TShiftState); begin inherited; case Key of vkReturn: if FListBox.Selected <> nil then begin OnItemClick(FListBox, FListBox.Selected); end; vkEscape: FPopup.IsOpen := False; vkDown: begin if FListBox.Selected <> nil then FListBox.ItemIndex := Min(FListBox.Count - 1, FListBox.ItemIndex + 1) else if FListBox.Count > 0 then FListBox.ItemIndex := 0; end; vkUp: begin if FListBox.Selected <> nil then FListBox.ItemIndex := Max(0, FListBox.ItemIndex - 1); end; end; if Assigned(OnKeyDown) then OnKeyDown(Edit, Key, KeyChar, Shift); end; procedure TStyledSuggestEdit.MMDataChanged(var AMessage: TDispatchMessageWithValue<TDataRecord>); var Data: TDataRecord; begin Data := AMessage.Value; if Data.Value.IsType <TStrings> and (Data.Key = 'Suggestions') then FItems.Assign(Data.Value.AsType<TStrings>) end; procedure TStyledSuggestEdit.OnItemClick(const Sender: TCustomListBox; const Item: TListBoxItem); begin FLastClickedIndex := Item.Tag; _ItemIndex := Item.Tag; FPopup.IsOpen := Sender = nil; // whenever OnItemClick is being called programmatically `Sender` must be passed as `nil`, Edit.SetFocus; // otherwise considered as real-user-click and should close popup end; procedure TStyledSuggestEdit.PMPressEnter(var AMessage: TDispatchMessage); var K: word; KC: Char; begin K := vkReturn; KC := #13; KeyDown(K, KC, []); end; procedure TStyledSuggestEdit.PMDropDown(var AMessage: TDispatchMessage); begin inherited; DropDownRecalc('',10); end; procedure TStyledSuggestEdit.PMGetItemIndex(var AMessage: TDispatchMessageWithValue<Integer>); begin AMessage.Value := self._ItemIndex; end; procedure TStyledSuggestEdit.PMGetItems(var AMessage: TDispatchMessageWithValue<TStrings>); begin AMessage.Value := Self._Items; end; procedure TStyledSuggestEdit.PMGetSelectedItem(var AMEssage: TDispatchMessageWithValue<TSelectedItem>); begin AMEssage.Value := self._SelectedItem; end; procedure TStyledSuggestEdit.PMSetItemChangeEvent(var AMessage: TDispatchMessageWithValue<TNotifyEvent>); begin FOnItemChange := AMessage.Value; end; procedure TStyledSuggestEdit.PMSetItemIndex(var AMessage: TDispatchMessageWithValue<Integer>); begin self._ItemIndex := AMessage.Value; end; procedure TStyledSuggestEdit.PMSetSize(var AMessage: TDispatchMessageWithValue<TSizeF>); begin inherited; FPopup.Width := Width; end; procedure TStyledSuggestEdit.RebuildSuggestionList(AText: String); var i: integer; Word: string; begin FListBox.Clear; FListBox.BeginUpdate; AText := AText.ToLower; try for i := 0 to FItems.Count - 1 do if AText.IsEmpty or FItems[i].ToLower.StartsWith(AText) then begin FListBox.AddObject(TListBoxItem.Create(FListBox)); FListBox.ListItems[FListBox.Count - 1].Tag := I; FListBox.ListItems[FListBox.Count - 1].Data := FItems.Objects[i]; FListBox.ListItems[FListBox.Count - 1].Text := FItems[i]; end; finally FListBox.EndUpdate; end; end; procedure TStyledSuggestEdit.RecalculatePopupHeight; begin if FListBox.Items.Count > 0 then begin FPopup.Height := FListBox.ListItems[0].Height * Min(FDropDownCount, FListBox.Items.Count) + FListBox.BorderHeight; FPopup.PopupFormSize := TSizeF.Create(FPopup.Width, FPopup.Height); end else begin FPopup.Height := 1; // instead this it's possible to hide FPopup.IsOpen := false; FPopup.PopupFormSize := TSizeF.Create(FPopup.Width, FPopup.Height); end; end; function TStyledSuggestEdit._SelectedItem: TSelectedItem; begin if FItemIndex = -1 then begin Result.Text := ''; Result.Data := nil; end else begin Result.Text := FItems[FItemIndex]; Result.Data := FItems.Objects[FItemIndex]; end; end; procedure TStyledSuggestEdit._SetIndex(const Value: Integer); begin if (Value >= -1) and (Value < FItems.Count) and (Value <> FItemIndex) then begin FDontTrack := true; FItemIndex := Value; if (FItemIndex >= 0) and (Edit.Text <> _SelectedItem.Text) then begin Edit.Text := _SelectedItem.Text; Edit.GoToTextEnd; end; if Assigned(FOnItemChange) then FOnItemChange(Edit); FDontTrack := false; end; end; procedure TStyledSuggestEdit._SetItems(const Value: TStrings); begin FItems := Value; _ItemIndex := -1; end; procedure TStyledSuggestEdit.DropDownRecalc(ByText: string; Delay: integer); begin // Here is possible to use a timer call or a call in a thread; if not self.FDontTrack then begin Self.RebuildSuggestionList(ByText); Self.RecalculatePopupHeight; self.FPopup.IsOpen := self.FListBox.Items.Count > 0; CheckIfTextMatchesSuggestions; end; end; { TEditHelper } procedure TEditSuggestHelper.PressEnter; begin if HasPresentationProxy then PresentationProxy.SendMessage(PM_PRESSENTER); end; function TEditSuggestHelper.SelectedItem: TSelectedItem; begin if HasPresentationProxy then PresentationProxy.SendMessageWithResult<TSelectedItem>(PM_GET_SELECTEDITEM, Result); end; procedure TEditSuggestHelper.SetIndex(const Value: Integer); begin if HasPresentationProxy then PresentationProxy.SendMessage<Integer>(PM_SET_ITEMINDEX, Value); end; procedure TEditSuggestHelper.SetOnItemChange(const Value: TNotifyEvent); begin if HasPresentationProxy then PresentationProxy.SendMessage<TNotifyEvent>(PM_SET_ITEMCHANGE_EVENT, Value); end; procedure TEditSuggestHelper.ForceDropDown; begin if HasPresentationProxy then PresentationProxy.SendMessage(PM_DROP_DOWN); end; function TEditSuggestHelper.GetIndex: Integer; begin if HasPresentationProxy then PresentationProxy.SendMessageWithResult<Integer>(PM_GET_ITEMINDEX, Result); end; function TEditSuggestHelper.GetItems: TStrings; begin if HasPresentationProxy then PresentationProxy.SendMessageWithResult<TStrings>(PM_GET_ITEMS, Result); end; procedure TEditSuggestHelper.AssignItems(const S: TStrings); begin self.Model.Data['Suggestions'] := TValue.From<TStrings>(S); end; initialization TPresentationProxyFactory.Current.Register('SuggestEditStyle', TStyleSuggestEditProxy); finalization TPresentationProxyFactory.Current.Unregister('SuggestEditStyle'); end. 组件
  • 在“事件”标签上为TEdit定义以下内容:

    TEdit.OnPresentationNameChoosing
  • 通过procedure TForm1.Edit1PresentationNameChoosing(Sender: TObject; var PresenterName: string); begin inherited; PresenterName := 'SuggestEditStyle'; end;

  • sl: TStrings添加项目
  • sl.AddObject('Name', TIntObj.Create(10));分配给您的编辑方:sl: TStrings
  • 如果您在输入时不需要Edit1.AssignItems(sl);能力,请在代码中注明TStyledSuggestEdit.CheckIfTextMatchesSuggestions

测试Autoselect

表格参考

Form1

代码参考

object Form1: TForm1
  Left = 0
  Top = 0
  Caption = 'Form1'
  ClientHeight = 325
  ClientWidth = 225
  FormFactor.Width = 320
  FormFactor.Height = 480
  FormFactor.Devices = [Desktop]
  OnCreate = FormCreate
  DesignerMasterStyle = 0
  object Edit1: TEdit
    Touch.InteractiveGestures = [LongTap, DoubleTap]
    Align = Top
    TabOrder = 0
    OnPresentationNameChoosing = Edit1PresentationNameChoosing
    Position.X = 20.000000000000000000
    Position.Y = 57.000000000000000000
    Margins.Left = 20.000000000000000000
    Margins.Right = 20.000000000000000000
    Size.Width = 185.000000000000000000
    Size.Height = 22.000000000000000000
    Size.PlatformDefault = False
    object Button2: TButton
      Align = Right
      Cursor = crArrow
      Margins.Left = 1.000000000000000000
      Margins.Top = 1.000000000000000000
      Margins.Right = 1.000000000000000000
      Margins.Bottom = 1.000000000000000000
      Position.X = 156.500000000000000000
      Position.Y = 0.500000000000000000
      Scale.X = 0.500000000000000000
      Scale.Y = 0.500000000000000000
      Size.Width = 56.000000000000000000
      Size.Height = 42.000000000000000000
      Size.PlatformDefault = False
      StyleLookup = 'arrowdowntoolbutton'
      TabOrder = 0
      Text = 'Button2'
      OnClick = Button2Click
    end
  end
  object Button1: TButton
    Align = Top
    Margins.Left = 30.000000000000000000
    Margins.Top = 10.000000000000000000
    Margins.Right = 30.000000000000000000
    Position.X = 30.000000000000000000
    Position.Y = 89.000000000000000000
    Size.Width = 165.000000000000000000
    Size.Height = 22.000000000000000000
    Size.PlatformDefault = False
    TabOrder = 1
    Text = 'Set 3rd item'
    OnClick = Button1Click
  end
  object Label1: TLabel
    Align = Top
    Size.Width = 225.000000000000000000
    Size.Height = 57.000000000000000000
    Size.PlatformDefault = False
    Text = 'Label1'
  end
end

经过测试的Win32 [Windows 7/8]和Android 4.4.4设备[MI3W]

希望这会有所帮助。任何进一步的想法和建议都表示赞赏。

答案 1 :(得分:0)

在上一个Delphi XE10更改行的答案中

Result := TStyledSuggestEdit.Create(nil);

 Result := TStyledSuggestEdit.Create(nil, Model, PresentedControl);

在函数TStyleSuggestEditProxy.CreateReceiver: TObject;

Data.Key = 'Suggestions' to Data.Key = 'suggestions'

中更改TStyledSuggestEdit.MMDataChanged

答案 2 :(得分:0)

对于iOS(我没有在Android上进行检查,但也可以运行),将TMemo或TEdit的ControlType设置为 Platform -这将显示T9自动完成功能并检查拼写。 enter image description here