6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

DelphiAdvent Calendar 2019

Day 12

[小ネタ]色々使えるVertScrollBox

Last updated at Posted at 2019-12-11

#はじめに
前回に続いてFireMonkey初心者による初心者向け記事となります。

Delphiでスマートフォンアプリが作れる!と意気込んでみたものの、いざ作り出すとスマートフォンっぽいUIってどんなだっけ?となりまして。まずは既存のものを真似をするのがいいかなと思い至りました。
そんなわけで(?)、TVertScrollBoxを利用した作例を二つ、紹介したいと思います。

##例1:Twitterの上部エリアを再現する
Twitterを眺めているとチラチラ視界に入るアイツ
01s.png

###やり方
02.png
図のような親子構造でコントロールを配置し、FormのOnCreateイベントでListViewコントロールの高さを親コントロールの高さ(今回はForm1.ClientHeight)に設定するだけです。

04.png
(ListViewの仮データとしてPrototypeBindSourceを使用しています。詳細はこちら

※親コントロールにMarginPaddingの設定をしている場合、その考慮も必要です。
※TabControlが親の場合、高さの計算はTabControl.Height-TabControl.TabHeightになります。

###挙動を似せる
そのままでも結構それっぽいのですが、以下の機能が再現できていません。
・上部パネルが半分以上表示されていると、途中でスワイプを止めても全て表示される
・上部パネルが半分以上隠れていると、途中でスワイプを止めても全て隠れる

TVertScrollBoxOnViewportPositionChangeイベントに以下のようなコードを書いたところ、近い動きになりました。


uses System.Math;

procedure TForm1.VertScrollBox1ViewportPositionChange(Sender: TObject;
  const OldViewportPosition, NewViewportPosition: TPointF;
  const ContentSizeChanged: Boolean);
begin

  if ABS(VertScrollBox1.AniCalculations.CurrentVelocity.Y) < 100 then //スクロール速度が一定以下だったら
  begin
    if VertScrollBox1.ViewportPosition.Y < Panel1.Height * 0.5 then
    begin
      VertScrollBox1.AniCalculations.MouseWheel(0,-5); //マウスホイール操作を疑似発生する(上スクロール)
    end
    else if VertScrollBox1.ViewportPosition.Y >= Panel1.Height * 0.5 then 
    begin
      VertScrollBox1.AniCalculations.MouseWheel(0,6);  //下スクロール
    end;
  end;
end;

※マジックナンバー箇所は目分量で調整しました
※どの動作環境でも同じ速さでスクロールするかわかりません

わかり辛いのですが、こんな動きになります。(Android実機動作例)
twitterLike.gif

##例2:入力欄をタップするとスクロールする(パンする)UI
Delphiで作成したスマートフォンアプリは、ソフトウェアキーボード表示の際に自動でパンしません。入力欄が隠れないようなUIデザインをまずは考える必要があります。

どうしても画面の下側にも入力欄を設けたい場合、入力欄の土台となるコントロールの高さを余分にとるか、画面下部にソフトウェアキーボードの高さ分のパネルを配置し、TVertScrollBoxでスクロールできるようにする、などの方法があります。
今回は後者をやってみます。
(TVertScrollBoxを使わずに、土台の位置を直接ずらすことも可能です)

41.png

Panel_VK(画面下部パネル)のHeightについては、スクリーン高さの4割ぐらいを設定しておきます。AlignはどちらもTopです。

ソフトウェアキーボードの高さはForm.OnVirtualKeyboardShownイベントにて、引数のBoundsから取得できますが、簡略化のため、キーボードの高さがPanel_VKの高さを越えないものとして作成します。

起動時にパネルの高さを設定し、入力コントロールのOnEnterイベントにてViewportPositionの値を変更します。


procedure TForm1.FormCreate(Sender: TObject);
begin
  Panel1.Height := ClientHeight;
  Panel_VK.Height := ClientHeight * 0.4;
end;

procedure TForm1.EditEnter(Sender: TObject);
var
  bottom:single;
  overlapped:single;
begin
  bottom := TControl(sender).Position.Y + TControl(sender).Height;

  overlapped := Panel1.Height - Panel_VK.Height - bottom;
  if overlapped < 0 then
    VertScrollBox1.ViewportPosition := TPointF.Create(0,-overlapped);
end;

(動作例)
VirtualKeyScroll1.gif

###なめらかスクロール
せっかくなのでアニメーションさせたい! しかしViewportPositionTPointF型なのでFloatAnimationが適用できません。困った・・。
と思っていたら作例がありました。 → TPointAnimation

このコードを拝借して、コンポーネント化して配置するとこのようになります。(参考:コンポーネントの新規作成

42.png(VertScrollBox1の子になるように配置してください)

EditEnterイベント内のコードを一部変更します。


  if overlapped < 0 then
    VertScrollBox1.ViewportPosition := TPointF.Create(0,-overlapped);


  if overlapped < 0 then
  begin
    PointAnimation1.StopValue := TPointF.Create(0,-overlapped);
    PointAnimation1.Start;
  end;

VirtualKeyScroll2.gif

(Android実機動作)
VirtualKeyScroll3.gif

###OnVirtualKeyboardHiddenは使わないの?
当初はShown時にPanel_VKの高さを設定して、Hidden時にゼロに戻したらいいんや!と思ったのですが、環境によってはOnVirtualKeyboardHiddenイベントが発生しないとのことです(BlackberryKey2もだめでした!)。気をつけましょう!

#おわりに
取り留めのない記事になってしまいましたが、(Delphi未経験の方には)こんなこともできるんだな~ 程度に見てもらえれば幸いです。

6
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?