|
![]() |
|
Registriert seit: 11. Aug 2007 357 Beiträge |
#1
Hallo,
ich habe heute mal etwas Zeit gehabt mit Firemonkey zu spielen. Folgender Code erstellt 11 bunte Tiles die, wenn Sie den Focus bekommen animiert sich vergrößern bzw. verkleinern. Das funktioniert auf allen Plattformen, sowohl via Touch, als auch mit Tastatur. Mein Problem ist, dass ich nicht weiß wie man die Scrollbox möglichst angenehm auf den fokussierten Eintrag anpasst. Hat da jemand eine Idee? Das ist so ziemlich das einzigste Problem was ich an dem Code noch habe. Aktuell lade ich in einem Thread die Grafiken habe mehrere Scrollboxen als Rubriken untereinander. Lediglich das horizontale und vertikale scrollen bekomme ich schlichtweg nicht hin.
Delphi-Quellcode:
unit UMain;
interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Layouts; type TTileItem = class(TControl) protected FZoomFactor: Single; FBackgroundColor: TAlphaColor; procedure Paint; override; procedure DoEnter; override; procedure DoExit; override; procedure SetZoomFactor(AValue: Single); procedure SetBackgroundColor(AValue: TAlphaColor); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor; property ZoomFactor: Single read FZoomFactor write SetZoomFactor; end; TForm3 = class(TForm) HorzScrollBox1: THorzScrollBox; procedure FormCreate(Sender: TObject); private { Private-Deklarationen } public { Public-Deklarationen } end; var Form3: TForm3; implementation uses FMX.Ani; {$R *.fmx} const cZoomIn = 0.9; cZoomOut = 1.0; cZoomTime = 0.4; constructor TTileItem.Create(AOwner: TComponent); begin inherited; CanFocus := true; ZoomFactor := 0; FBackgroundColor := TAlphaColors.Gray; end; destructor TTileItem.Destroy; begin inherited; end; procedure TTileItem.DoEnter; begin BringToFront; TAnimator.AnimateFloat(self, 'ZoomFactor', cZoomOut, cZoomTime, TAnimationType.Out, TInterpolationType.Quartic); end; procedure TTileItem.DoExit; begin SendToBack; TAnimator.AnimateFloat(self, 'ZoomFactor', cZoomIn, cZoomTime, TAnimationType.Out, TInterpolationType.Quartic); end; procedure TTileItem.SetBackgroundColor(AValue: TAlphaColor); begin if FBackgroundColor <> AValue then begin FBackgroundColor := AValue; repaint; end; end; procedure TTileItem.SetZoomFactor(AValue: Single); begin if AValue < cZoomIn then AValue := cZoomIn; if AValue > cZoomOut then AValue := cZoomOut; if FZoomFactor <> AValue then begin FZoomFactor := AValue; repaint; end; end; procedure TTileItem.Paint; var w, h: Single; R: TRectF; begin if Locked then Exit; w := Width * FZoomFactor; h := Height * FZoomFactor; R := RectF((Width - w) / 2, (Height - h) / 2, w, h); Canvas.Fill.Color := FBackgroundColor; Canvas.FillRect(R, 5, 5, AllCorners, AbsoluteOpacity); end; const ModernUIColors: Array [0 .. 10] of TAlphaColor = ($FFFF0097, $FF1BA1E2, $FFA200FF, $FF00ABA9, $FF8CBF26, $FFA05000, $FFE671B8, $FFF09609, $FFE51400, $FF339933, $FFFFFFFF); procedure TForm3.FormCreate(Sender: TObject); var i: integer; w, h: Single; item: TTileItem; begin w := 150; h := HorzScrollBox1.Height - 20; for i := 0 to high(ModernUIColors) do begin item := TTileItem.Create(self); with item do begin BackgroundColor := ModernUIColors[i]; Parent := HorzScrollBox1; Position.Point := PointF(i * 0.9 * w, 0); Width := w; Height := h; end; end; end; end. |
![]() |
Registriert seit: 15. Mär 2007 4.221 Beiträge Delphi 12 Athens |
#2
Hallo Peter,
bin icht ganz sicher was genau Du erreichen willst. Aber das Scrollen bekomment man durch die InteractiveGestures aktiviert. Im HorizScrollBox Object-Inspektor unter Touch\InteractiveGestures\Zoom diese Checkbox setzen. Dann kann man den Eventhandler hinzufügen, z.B. so
Delphi-Quellcode:
procedure TForm3.HorzScrollBox1Gesture(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean); var LScrollBox: TCustomScrollBox; LScaleNew: Single; begin if not (Sender is TCustomScrollBox) then Exit; if EventInfo.GestureID = igiZoom then begin LScrollBox := Sender as TCustomScrollBox; if not(TInteractiveGestureFlag.gfBegin in EventInfo.Flags) then begin LScaleNew := ( (LScrollBox.ClientWidth + EventInfo.Distance - FLastDIstance) / LScrollBox.ClientWidth); LScrollBox.Scale.X := LScrollBox.Scale.X * LScaleNew; LScrollBox.RealignContent; end; FLastDIstance := EventInfo.Distance; end; end; |
![]() |
Registriert seit: 11. Aug 2007 357 Beiträge |
#3
Danke,
ich wollte, wenn ich auf einen Eintrag mit Tab bzw. Shift+Tab gehe, dass dieser selektierte Eintrag in der Scrollbox den Fokus bekommt. So etwas wie bei einer Liste bei der ich über ScrollTo zu dem gewählten Element scrolle. Und dass möglichst animiert ![]() Peter |
![]() |
Registriert seit: 15. Mär 2007 4.221 Beiträge Delphi 12 Athens |
#4
Das mach es ja so, es zoomed einen Block bei Click größer, und man kann das Ganze dann noch manuell zoomen.
|
![]() |
Registriert seit: 11. Aug 2007 357 Beiträge |
#5
Danke,
aber ich stehe ein bisschen auf dem Schlauch. Ich möchte im Prinzip das aktive Tile in der Scrollbox fokussieren. Wahlweise über anklicken, scrollen oder mittels Links/Rechts. Ich habe mal den Testcode an den Post gehängt. Peter |
![]() |
Registriert seit: 11. Aug 2007 357 Beiträge |
#6
Delphi-Quellcode:
Ich hab das jetzt wie folgt gemacht. Das DoEnter macht den ganzen "magischen" Kram. So richtig schön finde ich das mit den Scrollen nicht, aber es geht. @Rollo: Vielleicht hast du ja eine bessere Idee.
unit FMX.TilesGrid;
interface uses System.SysUtils, System.Classes, System.Types, System.UITypes, FMX.Graphics, FMX.Types, FMX.Controls, FMX.Layouts; type TTileItem = class(TControl) protected FZoomFactor: Single; FBackgroundColor: TAlphaColor; procedure Paint; override; procedure DoEnter; override; procedure DoExit; override; procedure SetZoomFactor(AValue: Single); procedure SetBackgroundColor(AValue: TAlphaColor); function GetBarPos: Single; procedure SetBarPos(const AValue: Single); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor; property ZoomFactor: Single read FZoomFactor write SetZoomFactor; property BarPos: Single read GetBarPos write SetBarPos; end; implementation uses FMX.Ani, FMX.Stdctrls; const cZoomIn = 0.9; cZoomOut = 1.0; cZoomTime = 0.2; type TCustomScrollBoxCracker = class(TCustomScrollBox); constructor TTileItem.Create(AOwner: TComponent); begin inherited; CanFocus := true; ZoomFactor := 0; FBackgroundColor := TAlphaColors.Gray; end; destructor TTileItem.Destroy; begin inherited; end; function TTileItem.GetBarPos: Single; begin if Owner is TCustomScrollBox then result := TCustomScrollBoxCracker(Owner).HScrollBar.Value else result := 0; end; procedure TTileItem.SetBarPos(const AValue: Single); begin if Owner is TCustomScrollBox then TCustomScrollBoxCracker(Owner).HScrollBar.Value := AValue; end; procedure TTileItem.DoEnter; var NewScrollViewPos: single; MinWidth, MinHeight: Single; begin BringToFront; TAnimator.AnimateFloat(self, 'ZoomFactor', cZoomOut, cZoomTime, TAnimationType.Out, TInterpolationType.Quadratic); NewScrollViewPos := BarPos; MinWidth := 0; MinHeight := 0; if (BoundsRect.Left - NewScrollViewPos < MinWidth) then NewScrollViewPos := BoundsRect.Left; if (BoundsRect.Left - NewScrollViewPos < MinHeight) then NewScrollViewPos := BoundsRect.Right - TCustomScrollBox(Owner).Width; if (BoundsRect.Right - NewScrollViewPos > TCustomScrollBox(Owner).Width) then NewScrollViewPos := BoundsRect.Right - TCustomScrollBox(Owner).Width; TAnimator.AnimateFloat(self, 'BarPos', NewScrollViewPos, cZoomTime, TAnimationType.In, TInterpolationType.Linear); end; procedure TTileItem.DoExit; begin SendToBack; TAnimator.AnimateFloat(self, 'ZoomFactor', cZoomIn, cZoomTime, TAnimationType.In, TInterpolationType.Linear); end; procedure TTileItem.SetBackgroundColor(AValue: TAlphaColor); begin if FBackgroundColor <> AValue then begin FBackgroundColor := AValue; repaint; end; end; procedure TTileItem.SetZoomFactor(AValue: Single); begin if AValue < cZoomIn then AValue := cZoomIn; if AValue > cZoomOut then AValue := cZoomOut; if FZoomFactor <> AValue then begin FZoomFactor := AValue; repaint; end; end; procedure TTileItem.Paint; var w, h: Single; R: TRectF; begin if Locked then Exit; w := Width * FZoomFactor; h := Height * FZoomFactor; R := RectF((Width - w) / 2, (Height - h) / 2, w, h); Canvas.Fill.Color := FBackgroundColor; Canvas.FillRect(R, 5, 5, AllCorners, AbsoluteOpacity); end; end. Peter |
![]() |
Registriert seit: 22. Okt 2012 267 Beiträge |
#7
Das ist ja ne coole Idee. Ich war so frei und hab das mal etwas weitergesponnen.
Delphi-Quellcode:
Die Tiles zeichne ich bei dem OnPaint Notify und lade das selbe Bild als Hintergrund von dem TTileGrid. Man könnte die aktive Reihe noch mittels Scale Property vergrößern, aber so schaut es auch schon schick aus. Danke für den Ansatz.
unit UTilesGrid;
interface uses System.SysUtils, System.Classes, System.Types, System.UITypes, FMX.Graphics, FMX.Types, FMX.Controls, FMX.Layouts, FMX.StdCtrls, System.Generics.Collections; type TTileItem = class(TControl) protected FZoomFactor: Single; FBackgroundColor: TAlphaColor; FOnPaint: TNotifyEvent; procedure Paint; override; procedure DoEnter; override; procedure DoExit; override; procedure SetZoomFactor(AValue: Single); procedure SetBackgroundColor(AValue: TAlphaColor); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; published property BackgroundColor: TAlphaColor read FBackgroundColor write SetBackgroundColor; property ZoomFactor: Single read FZoomFactor write SetZoomFactor; property OnPaint: TNotifyEvent read FOnPaint write FOnPaint; end; TTileRow = class(TControl) protected FTiles: TObjectList<TTileItem>; FItemIndex: Integer; FScrollBox: THorzScrollBox; FTitle: TLabel; FSelected: TTileItem; function GetCount: Integer; function GetItem(AIndex: Integer): TTileItem; procedure SetItemIndex(AIndex: Integer); function GetScrollPos: Single; procedure SetScrollPos(const AValue: Single); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; function Add: TTileItem; procedure Delete(const AIndex: Integer); procedure ScrollIntoView(const AItem: TTileItem); property Items[index: Integer]: TTileItem read GetItem; default; property Count: Integer read GetCount; property ItemIndex: Integer read FItemIndex write SetItemIndex; published property ScrollPos: Single read GetScrollPos write SetScrollPos; property Title: TLabel read FTitle; property Scale; end; TTileGrid = class(TControl) protected FRows: TObjectList<TTileRow>; FItemIndex: Integer; FScrollBox: TVertScrollBox; function GetItem(AIndex: Integer): TTileRow; function GetCount: Integer; procedure SetItemIndex(AIndex: Integer); function GetScrollPos: Single; procedure SetScrollPos(const AValue: Single); public constructor Create(AOwner: TComponent); override; destructor Destroy; override; procedure ScrollIntoView(const AItem: TTileRow); function Add: TTileRow; procedure Delete(const AIndex: Integer); property Items[index: Integer]: TTileRow read GetItem; default; property Count: Integer read GetCount; property ItemIndex: Integer read FItemIndex write SetItemIndex; published property ScrollPos: Single read GetScrollPos write SetScrollPos; property Scale; end; implementation uses FMX.Ani; const cZoomIn = 0.9; cZoomOut = 1.0; cZoomTime = 0.2; type TCustomScrollBoxCracker = class(TCustomScrollBox); function IfThen(const AState: Boolean; const ATrue, AFalse: Integer) : Integer; inline; begin if AState then result := ATrue else result := AFalse; end; { TTileItem } constructor TTileItem.Create(AOwner: TComponent); begin inherited; CanFocus := true; ZoomFactor := 0; FBackgroundColor := TAlphaColors.Gray; end; destructor TTileItem.Destroy; begin inherited; end; procedure TTileItem.DoEnter; begin BringToFront; TAnimator.AnimateFloat(self, 'ZoomFactor', cZoomOut, cZoomTime, TAnimationType.Out, TInterpolationType.Quadratic); if Owner is TTileRow then TTileRow(Owner).ScrollIntoView(self); end; procedure TTileItem.DoExit; begin SendToBack; TAnimator.AnimateFloat(self, 'ZoomFactor', cZoomIn, cZoomTime, TAnimationType. In, TInterpolationType.Linear); end; procedure TTileItem.SetBackgroundColor(AValue: TAlphaColor); begin if FBackgroundColor <> AValue then begin FBackgroundColor := AValue; repaint; end; end; procedure TTileItem.SetZoomFactor(AValue: Single); begin if AValue < cZoomIn then AValue := cZoomIn; if AValue > cZoomOut then AValue := cZoomOut; if FZoomFactor <> AValue then begin FZoomFactor := AValue; repaint; end; end; procedure TTileItem.Paint; var w, h: Single; R: TRectF; begin if Locked then Exit; w := Width * FZoomFactor; h := Height * FZoomFactor; R := RectF((Width - w) / 2, (Height - h) / 2, w, h); Canvas.Fill.Color := FBackgroundColor; Canvas.FillRect(R, 5, 5, AllCorners, AbsoluteOpacity); if assigned(FOnPaint) then FOnPaint(self); end; { TTileRow } constructor TTileRow.Create(AOwner: TComponent); begin inherited; Height := 300; FTiles := TObjectList<TTileItem>.Create(true); FItemIndex := -1; FTitle := TLabel.Create(self); FTitle.Parent := self; FTitle.Align := TAlignLayout.Top; FTitle.Text := 'RowTitle'; FScrollBox := THorzScrollBox.Create(self); FScrollBox.Parent := self; FScrollBox.Align := TAlignLayout.Client; FScrollBox.ShowScrollBars := false; HitTest := true; end; destructor TTileRow.Destroy; begin FTiles.Free; FScrollBox.Free; inherited; end; function TTileRow.GetCount: Integer; begin result := FTiles.Count; end; function TTileRow.Add: TTileItem; begin result := TTileItem.Create(self); result.Parent := FScrollBox; FTiles.Add(result); end; function TTileRow.GetScrollPos: Single; begin if assigned(TCustomScrollBoxCracker(FScrollBox).HScrollBar) then result := TCustomScrollBoxCracker(FScrollBox).HScrollBar.Value; end; procedure TTileRow.SetScrollPos(const AValue: Single); begin if assigned(TCustomScrollBoxCracker(FScrollBox).HScrollBar) then TCustomScrollBoxCracker(FScrollBox).HScrollBar.Value := AValue; end; procedure TTileRow.ScrollIntoView(const AItem: TTileItem); var NewScrollViewPos: Single; MinWidth, MinHeight: Single; begin if FSelected <> AItem then begin NewScrollViewPos := ScrollPos; MinWidth := 0; MinHeight := 0; if (AItem.BoundsRect.Left - NewScrollViewPos < MinWidth) then NewScrollViewPos := AItem.BoundsRect.Left; if (AItem.BoundsRect.Right - NewScrollViewPos > FScrollBox.Width) then NewScrollViewPos := AItem.BoundsRect.Right - FScrollBox.Width; TAnimator.AnimateFloat(self, 'ScrollPos', NewScrollViewPos, cZoomTime, TAnimationType.In, TInterpolationType.Linear); FItemIndex := FTiles.IndexOf(AItem); FSelected := AItem; end; if Owner is TTileGrid then TTileGrid(Owner).ScrollIntoView(self); end; procedure TTileRow.Delete(const AIndex: Integer); begin FTiles.Delete(AIndex); end; function TTileRow.GetItem(AIndex: Integer): TTileItem; begin if (AIndex >= 0) and (AIndex < FTiles.Count) then result := FTiles[AIndex] else result := nil; end; procedure TTileRow.SetItemIndex(AIndex: Integer); begin if AIndex < 0 then AIndex := Count - 1 else if AIndex >= Count then AIndex := 0; FTiles[AIndex].SetFocus; end; { TTileGrid } constructor TTileGrid.Create(AOwner: TComponent); begin inherited; FRows := TObjectList<TTileRow>.Create; FItemIndex := -1; FScrollBox := TVertScrollBox.Create(self); FScrollBox.Parent := self; FScrollBox.Align := TAlignLayout.Client; FScrollBox.ShowScrollBars := false; HitTest := true; end; destructor TTileGrid.Destroy; begin FRows.Free; FScrollBox.Free; inherited; end; function TTileGrid.GetItem(AIndex: Integer): TTileRow; begin result := FRows[AIndex]; end; function TTileGrid.GetCount: Integer; begin result := FRows.Count; end; function TTileGrid.Add: TTileRow; begin result := TTileRow.Create(self); FRows.Add(result); result.Parent := FScrollBox; result.Align := TAlignLayout.Top; end; procedure TTileGrid.Delete(const AIndex: Integer); begin FRows.Delete(AIndex); end; procedure TTileGrid.SetItemIndex(AIndex: Integer); begin if AIndex < 0 then AIndex := Count - 1 else if AIndex >= Count then AIndex := 0; with FRows[AIndex] do ItemIndex := IfThen(ItemIndex = -1, 0, ItemIndex); end; function TTileGrid.GetScrollPos: Single; begin if assigned(TCustomScrollBoxCracker(FScrollBox).VScrollBar) then result := TCustomScrollBoxCracker(FScrollBox).VScrollBar.Value; end; procedure TTileGrid.SetScrollPos(const AValue: Single); begin if assigned(TCustomScrollBoxCracker(FScrollBox).VScrollBar) then TCustomScrollBoxCracker(FScrollBox).VScrollBar.Value := AValue; end; procedure TTileGrid.ScrollIntoView(const AItem: TTileRow); var NewScrollViewPos: Single; MinWidth, MinHeight: Single; i: Integer; begin NewScrollViewPos := ScrollPos; MinWidth := 0; MinHeight := 0; if (AItem.BoundsRect.Top - NewScrollViewPos < MinHeight) then NewScrollViewPos := AItem.BoundsRect.Top; if (AItem.BoundsRect.Bottom - NewScrollViewPos > FScrollBox.Height) then NewScrollViewPos := AItem.BoundsRect.Bottom - FScrollBox.Height; TAnimator.AnimateFloat(self, 'ScrollPos', NewScrollViewPos, cZoomTime, TAnimationType.In, TInterpolationType.Linear); AItem.SetFocus; FItemIndex := FRows.IndexOf(AItem); end; end. Christian PS: Das ganze geht auch noch einfacher und sauberer, aber ich dachte probiere ich einfach mal 100 Reihen mit jeweils 100 Einträgen. Die Idee war abzuschätzen, ob sich das wirklich lohnt so umzusetzen und es geht erstaunlich gut. Beim vertikalen Scrollen auf einem Touchpad ist das ganze jedoch etwas unschön. Da bleibt das Scrollen gerne hängen, wenn sich ein Icon in der Reihe in den Focus bewegt. Geändert von CHackbart ( 9. Sep 2019 um 20:05 Uhr) Grund: Rechtschreibfehler |
![]() |
Ansicht |
![]() |
![]() |
![]() |
ForumregelnEs ist dir nicht erlaubt, neue Themen zu verfassen.
Es ist dir nicht erlaubt, auf Beiträge zu antworten.
Es ist dir nicht erlaubt, Anhänge hochzuladen.
Es ist dir nicht erlaubt, deine Beiträge zu bearbeiten.
BB-Code ist an.
Smileys sind an.
[IMG] Code ist an.
HTML-Code ist aus. Trackbacks are an
Pingbacks are an
Refbacks are aus
|
|
Nützliche Links |
Heutige Beiträge |
Sitemap |
Suchen |
Code-Library |
Wer ist online |
Alle Foren als gelesen markieren |
Gehe zu... |
LinkBack |
![]() |
![]() |