PowerApps│感謝を送り合う社内SNSアプリの作り方 ②タイムライン画面

こんにちは、あんこ先生です。

前回は、サンクスアプリの設計を決めて、SharePointリストを3つ用意しました。

今回はいよいよ画面づくりです。

投稿がずらっと並ぶタイムライン画面を作っていきましょう。

SNSでいちばん見られる画面ですから、ここが気持ちよく動くと一気に「それっぽく」なりますよ。

まだ読んでいない人は、先に設計とリスト準備の回でリストを用意しておいてくださいね。

画面の骨組みは3つだけ


最初に、この画面の構造を確認しておきましょう。

置くものは3つです。

上に画面タイトル、まん中に投稿一覧のギャラリー、下にボトムメニュー。

たったこれだけです。

まずは新しい画面を追加して、名前を scrTimeline にしておきます。

画面の Fill は、うっすらグレーにしておくと投稿カードが引き立ちますよ。

//画面 scrTimeline の Fill(投稿カードを引き立てる薄いグレー)
RGBA(245, 246, 247, 1)

いちばんの山場は「行の高さ」

さて、ここからが今回の本題です。

タイムラインに並ぶ投稿は、本文の長さがバラバラですよね。

ひとことだけの「ありがとう!」もあれば、5行くらいの熱いメッセージもあります。

ここで、ギャラリーの高さを決め打ちしてしまうと、こうなります。

短い投稿は下がスカスカ、長い投稿は文字がはみ出して次の投稿と重なってしまいます

わたしも最初これで「なんか汚いな…」となりました。

解決策は3つの合わせ技です。

① ギャラリーは「縦(可変の高さ)」を選ぶ

まず、ギャラリーを挿入するときに、レイアウトで「縦(可変の高さ)」を選びます。

名前は galTimeline にしておきましょう。

ふつうの「縦」ギャラリーは、TemplateSize で決めた高さで全部の行がそろいます。

いっぽう「可変の高さ」は、行の中身に合わせて高さが決まります。

並べるデータは、Postsを新しい順にします。

//ギャラリー galTimeline の Items(投稿を新しい順に並べる)
SortByColumns(Posts, "Created", SortOrder.Descending)

Created は、SharePointが自動で記録している登録日時のことですね。

ギャラリーの高さと位置も決めておきます。

プロパティ設定する式
Y70
HeightParent.Height – 70 – 64
WidthParent.Width
TemplatePadding0

高さの式は、画面の高さから上のタイトル分(70)と、下のメニュー分(64)を引いています。

こうしておけば、ギャラリーがメニューの下に潜り込みません。

ギャラリーそのものの基本をおさらいしたい人は、すぐできるギャラリーコントロールの使い方と活用事例もどうぞ。

② ラベルは AutoHeight をオンにする

次に、中に置くラベルを「文字の量で伸びる」状態にします。

AutoHeighttrue にするだけです。

これを本文のラベルに付けると、3行の投稿なら3行分、6行の投稿なら6行分の高さになります。

③ Y座標を「ひとつ上の下端」から積む

ここがコツです。

ラベルの高さが変わるのですから、その下に置く部品のY座標も、固定の数字にしてはいけません。

//本文ラベル lblBody の Y(ひとつ上の下端から積むので、上が伸びても重ならない)
lblAuthorToUser.Y + lblAuthorToUser.Height + 6

「ひとつ上の部品のY座標 + その高さ + すき間」で位置を決めます。

こうすると、上の部品が伸びたぶんだけ、下の部品も一緒に下がってくれます。

ここが個人的に一番おもしろい:ドミノ倒しのように積む
Y座標を「ひとつ上の下端から」でつないでいくと、部品が数珠つなぎになります。本文が伸びる → タグが下がる → 画像が下がる → 区切り線が下がる。そしていちばん下の部品の位置が、その行の高さそのものになります。つまり「行の高さをいくつにするか」を自分で計算する必要がないんですね。部品をきちんと積んでおけば、高さは勝手に決まってくれる。この考え方が分かると、可変高さのギャラリーが一気にラクになりますよ。

投稿の中身を並べる

では、ギャラリーの中に置くものを順番に見ていきましょう。

左にアバター、右に投稿者名・本文・タグ・画像、という並びにします。

アバター(投稿者の顔写真)

まずは左側のアバターから。

モダンコントロールのアバターを追加して、名前を avaAuthor にします。

プロパティ設定する式
ImageThisItem.登録者.Picture
Shape‘Avatar.Shape’.Circular
Width50
X16
Y12

投稿者の写真は、SharePointが自動で記録している登録者の列から取れます。

ここで、ちょっとした落とし穴があります。

アバターコントロールそのものの使い方は、モダンコントロール アバター(Avatar)の使い方にまとめてありますので、あわせてどうぞ。

投稿者 → 受取者と、投稿時間

次に、誰から誰へのありがとうかを表示します。

テキストラベルを追加して、名前は lblAuthorToUser に。

ここで、投稿時間も一緒に出してしまいましょう。

SNSらしく「3分前」「2時間前」という表示にしたいですよね。

Text プロパティに、こちらを入れます。

//ラベル lblAuthorToUser の Text(誰から誰へ+投稿からの経過時間を1行で出す)
//経過分数を With でまとめ、範囲ごとに表示を切り替える
ThisItem.Author.DisplayName & " → " & ThisItem.ToUser.DisplayName
& "  ・ "
& With(
    { mins: DateDiff(ThisItem.Created, Now(), TimeUnit.Minutes) },
    Switch(
        true,
        mins < 1,     "たった今",
        mins < 60,    Text(mins) & "分前",
        mins < 1440,  Text(RoundDown(mins / 60, 0)) & "時間前",
        mins < 10080, Text(RoundDown(mins / 1440, 0)) & "日前",
        Text(ThisItem.Created, "yyyy/mm/dd")
    )
)

やっていることは単純です。

DateDiff で「投稿から今まで何分たったか」を出して、その分数で表示を切り替えているだけ。

60分未満なら「◯分前」、1日未満(1440分)なら「◯時間前」、1週間未満(10080分)なら「◯日前」。

それより古いものは、素直に日付を出します。

ひとことメモ:Switch(true, …) という書き方
Switch の1つめに true を置くと、「条件が true になったものを上から順に採用する」という書き方ができます。If をいくつも重ねるより読みやすいので、範囲で分岐したいときに便利ですよ。

計算に使った分数は With でまとめてあります。

同じ計算を何度も書かずに済むので、こういう分岐では重宝します。

このラベルの位置とサイズは、こうしておきます。

プロパティ設定する式
AutoHeighttrue
FontWeightFontWeight.Semibold
Size14
WidthParent.TemplateWidth – 102
X86
Y12

Width は、左のアバター(幅50 + 余白)を避けた残りの幅にしています。

本文

本文のラベル lblBody は、シンプルです。

プロパティ設定する式
TextThisItem.Body
AutoHeighttrue
Size14
WidthParent.TemplateWidth – 102
X86
YlblAuthorToUser.Y + lblAuthorToUser.Height + 6

さっそく Y が「ひとつ上の下端」から積まれていますね。

ここから下の部品は、すべてこの形でつないでいきます。

タグ

タグのラベル lblTags は、複数選択の列なので少し工夫します。

//タグラベル lblTags の Text(複数選択のタグをつなげて1つの文字列にする)
Concat(ThisItem.Tags, Value & "  ")

Concat で、選ばれたタグをつなげて1つの文字列にしています。

プロパティ設定する式
AutoHeighttrue
ColorRGBA(0, 120, 212, 1)
Size12
WidthParent.TemplateWidth – 102
X86
YlblBody.Y + lblBody.Height + 6

タグだけ青くしておくと、SNSらしさが出ますよ。

画像

画像は、投稿によって「ある・ない」が分かれますよね。

画像コントロール imgPost を追加して、こう設定します。

プロパティ設定する式
ImageThisItem.Image
Visible!IsBlank(ThisItem.Image)
WidthIf(IsBlank(ThisItem.Image), 0, 300)
HeightSelf.Width
X86
YlblTags.Y + lblTags.Height + 6

画像が無いときは、見えなくするだけでなく幅と高さを 0 にするのがポイントです。

さきほどの「ドミノ倒し」を思い出してください。

高さが 0 になれば、その下の部品がぴったり詰まってくれます。

Visible を false にしただけだと、場所は空いたままで、無駄なすき間が残ってしまうんですね。

ちなみに HeightSelf.Width を入れると、正方形で表示できます。

投稿の区切り線

最後に、投稿と投稿のあいだに線を引きます。

四角形(長方形)を追加して、名前を recDivider に。

プロパティ設定する式
FillRGBA(225, 227, 229, 1)
Height1
WidthParent.TemplateWidth
X0
YimgPost.Y + imgPost.Height + 16

高さ1の四角形を置くだけで、細い罫線になります。

この線がいちばん下の部品なので、この線の位置が行の高さを決めていることになりますね。

ボトムメニューをギャラリーで作る

さて、画面の下に固定するメニューを作りましょう。

タイムライン・投稿・マイページ・ランキングの4つを行き来できるようにします。

ここで、素直に作ろうとすると、こうなりがちです。

アイコンを4つ置いて、ボタンを4つ置いて、それぞれに座標と遷移先を書いて…。

正直、しんどいです。

そこで、メニューもギャラリーで作ってしまいます

メニューの中身をテーブルで書く

横方向のギャラリーを追加して、名前を galBottomMenu にします。

Items には、メニュー項目そのものを書いてしまいます。

//ボトムメニュー galBottomMenu の Items(メニュー項目そのものをデータとして書く)
//Key は画面を見分ける文字列、icon は表示するアイコン
Table(
    { Key: "Timeline", icon: Icon.Home },
    { Key: "Post",     icon: Icon.Add },
    { Key: "MyPage",   icon: Icon.Person },
    { Key: "Ranking",  icon: Icon.TrendingUpwards }
)

Table 関数で、その場でデータを作っています。

Key は画面を見分けるための文字列、icon は表示するアイコンです。

面白いのは、アイコンそのものをデータとして持たせられるところ。

ギャラリー側は、画面下に貼り付くように設定します。

プロパティ設定する式
FillRGBA(255, 255, 255, 1)
Height64
WidthParent.Width
YParent.Height – 64
TemplateSizeParent.Width / 4
TemplatePadding0

TemplateSize を画面幅の4分の1にしているので、4つがちょうど並びます。

ボタンは1つ置くだけ

あとは、ギャラリーの中にモダンボタンを1つ置くだけです。

名前は btnMenu にします。

プロパティ設定する式
IconThisItem.icon
Text“”
LayoutButtonLayout.IconOnly
HeightParent.TemplateHeight
WidthParent.TemplateWidth

LayoutIconOnly にすると、文字なしのアイコンだけのボタンになります。

遷移は OnSelect に書きます。

//メニューボタン btnMenu の OnSelect(押された項目の Key を見て行き先を振り分ける)
Switch(
    ThisItem.Key,
    "Timeline", Navigate(scrTimeline, ScreenTransition.None),
    "Post",     Navigate(scrPost, ScreenTransition.None),
    "MyPage",   Navigate(scrMyPage, ScreenTransition.None),
    "Ranking",  Navigate(scrRanking, ScreenTransition.None)
)

押された項目の Key を見て、行き先を振り分けています。

そして今いる画面だけ色を変えたいので、Appearance で分岐します。

//メニューボタン btnMenu の Appearance(今いる画面だけ目立たせる)
//ほかの画面に置くときは、この文字列をその画面の Key に変える
If(ThisItem.Key = "Timeline", ButtonAppearance.Primary, ButtonAppearance.Transparent)

この画面はタイムラインなので、Key が “Timeline” のときだけ目立つ見た目になります。

ほかの画面に貼るときは、ここの文字列を変えるだけでOKです。

ひとことメモ:4隅の角丸を0にする
モダンボタンは、そのままだと角が丸くなっています。メニューとして敷き詰めると角丸が気になるので、RadiusTopLeft など4つとも 0 にしています。小さいことですが、ここを整えるとぐっと「アプリらしい」見た目になりますよ。
ここが落とし穴:遷移先の画面がまだ無い
Navigate の行き先に、まだ作っていない画面の名前を書くとエラーになります。先に空の画面を3つ追加して、scrPost / scrMyPage / scrRanking と名前だけ付けておきましょう。中身は次回以降で作っていきますからね。

モダンボタンのプロパティをもう少し知りたい人は、モダンコントロール ボタン(Button)の使い方もどうぞ。

つまずきやすいプロパティ名

モダンコントロールは、クラシックと名前が違うプロパティがあります。

わたしが実際にエラーを出したところを、まとめておきますね。

やりたいこと誤り正しい書き方
文字を太字にするWeightFontWeight
文字の色を変えるFontColorColor
右寄せにするAlign.EndAlign.Right
ボタンにアイコンを付けるIcon.ThumbLike“ThumbLike”(文字列)

いちばん引っかかったのは、いちばん下のアイコンです。

モダンボタン のアイコンは、列挙型ではなく文字列で指定します

クラシックのアイコンコントロールは Icon.Home のように書くので、つい同じ書き方をしてしまうんですね。

ちなみに、さきほどのボトムメニューでは ThisItem.icon と書いていました。

これは Table の中に Icon.Home という形で持たせているので、そのまま渡せています。

まとめ

今回はここまでにしましょう。

・本文の長さがバラバラなら、ギャラリーは「縦(可変の高さ)」、ラベルは AutoHeight にする。
・下に置く部品のY座標は「ひとつ上の下端+すき間」で積む。いちばん下の部品の位置が、そのまま行の高さになる。
・画像のように「ある・ない」が分かれるものは、無いときに幅と高さを 0 にして詰める。
・メニューもギャラリーで作れる。項目を Table で書いて、ボタンは1つだけ置けばいい。

次の一手として、次回はこのタイムラインにいいね機能を付けます。

一度押したら青くなって、もう一度押すと取り消せる、あのトグルですね。

「同じ人が二重にいいねできないようにする」ところが、ちょっとした腕の見せどころです。

それでは、次の記事でお会いしましょう。

ABOUT US
七草あんこ
非IT系中間管理職やってます。社命によりoffice365を主軸とした業務改善プロジェクトメンバーに任命されたことでPowerAppsと出会えました。いまではビジネス・プライベートを問わず、欠かせないツールになっています。導入初期やアプリ作成時に遭遇した諸問題の解決法とサンプルアプリの作り方を紹介していきます。主にTwitterで情報収集しているので不明点など呟いているとお邪魔するかもしれません。