kivyMDチュートリアル其の漆 Components - Backdrop篇

みなさん、おはこんばんは。いかがお過ごしでしょうか。
今日もコピペで1千万!(久しぶり)なエンジニアを目指すためにもやっていきます。

最近か分からないですが、NoCodeが勢いを増していますね。どこかの記事かも忘れましたが、
主婦や学生でもアプリが開発出来た！という内容を見て我々エンジニアも一層ハードルが高く
なっているような気がしてなりません。ただ、エンジニア職がなくなる！といった主張は歴史的
経緯から真っ向否定したい所存です。

先週までやっていたThemesが終わり、今日からはComponentsに入っていきます。
まずはComponentsの種類から入っていきます。

`Components`

* Backdrop
* Banner
* Bottom Navigation
* Bottom Sheet
* Box Layout
* ~

と一気に全て列挙すると長すぎるので、最初の5個くらいまで。
個数でいうと40個くらいあります。 週1ペースでやるとおよそ1年間くらい掛かりそうです。
無事終わるかな。そしてそれだけではありません。その後にあるBehaviorsも含めると、48個くらい
になりちょうど1年間くらいの計算になります。そのときには、kivyMD完全に理解したという状況だと
いいですね。ただところどころ、ここはくっつけられるなとかもあるので大体ということで。

そして今日はここで終わりではなく、タイトルにもある通りBackdropに入ります。
今まではコードとAPIのところを別々にしていましたが、それだけだと長すぎるので
くっつけてお送りします。

## Backdrop

冒頭はいつも通り、MeterialDesignのBackdropのリンクが貼り付けられています。
大体どのページでもリンクが貼り付けられていたので、先に見とけよという意思表示があると感じず
にはいられません。ただし、これからはここも省略していきます。興味ある方は先に見ておくと便利
です。今日は概要だけ触れますが、2つのレイヤー(Front/Back layer)があってフロントが上がっ
たり下がったりするんだぜー(いい加減すぎ)ということを知っていればなんとなる分かります。

## Usage

使い方になります。詳しくはマニュアルもしくは以下の通りです。

```
<Root>:

    MDBackdrop:

        MDBackdropBackLayer:

            ContentForBackdropBackLayer:

        MDBackdropFrontLayer:

             ContentForBackdropFrontLayer:
```

とまぁ先ほどの通りのFront/Back Layerが表れてきています。名前だけでいうとプロレスの技
ですか？と聞きたくなるほどです。まずはMDBackdropウィジェットを決めてMDBackdropBack/
FrontLayerウィジェットを入れ込みます。それぞれにContentをまた持っている状態を作ります。

## Example

早速コードに入っていきます。

```python:vii/backdrop.py
from kivy.lang import Builder
from kivy.uix.screenmanager import Screen

from kivymd.app import MDApp

# Your layouts.
Builder.load_string(
    '''
#:import Window kivy.core.window.Window
#:import IconLeftWidget kivymd.uix.list.IconLeftWidget


<ItemBackdropFrontLayer@TwoLineAvatarListItem>
    icon: "android"

    IconLeftWidget:
        icon: root.icon


<MyBackdropFrontLayer@ItemBackdropFrontLayer>
    backdrop: None
    text: "Lower the front layer"
    secondary_text: " by 50 %"
    # icon: "transfer-down"
    on_press: root.backdrop.open(-Window.height / 2)
    pos_hint: {"top": 1}
    # _no_ripple_effect: True


<MyBackdropBackLayer@Image>
    size_hint: .8, .8
    source: "data/logo/kivy-icon-512.png"
    pos_hint: {"center_x": .5, "center_y": .6}
'''
)

# Usage example of MDBackdrop.
Builder.load_string(
    '''
<ExampleBackdrop>

    MDBackdrop:
        id: backdrop
        left_action_items: [['menu', lambda x: self.open()]]
        title: "Example Backdrop"
        radius_left: "25dp"
        radius_right: "0dp"
        header_text: "Menu:"

        MDBackdropBackLayer:
            MyBackdropBackLayer:
                id: backlayer

        MDBackdropFrontLayer:
            MyBackdropFrontLayer:
                backdrop: backdrop
'''
)


class ExampleBackdrop(Screen):
    pass


class TestBackdrop(MDApp):
    def __init__(self, **kwargs):
        super().__init__(**kwargs)

    def build(self):
        return ExampleBackdrop()


TestBackdrop().run()
```

とまぁ、コード全体はこうなっています。ただし、class側は馴染みのある書き方だし、
これまでと目新しさはないので端折ります。コード自体はkvでの宣言オンリー。今回は
ご丁寧にyour layoutsとUsage example~と分けて頂いているので、その辺りを中心に
分けて触れ込んでいきたいと思います。上記コードは少しマニュアルと変更しているので
ご注意ください。\# このあと理由がわかるかも

### Usage example of MDBackdrop.

kv側のコードを抽出し直したのが以下のコードになります。
少し順番が変わるとややこしいので、先ほどあったUsageでの使い方から触れ込みます。
こちらはマニュアルと同等です。

```python
# Usage example of MDBackdrop.
Builder.load_string(
    '''
<ExampleBackdrop>

    MDBackdrop:
        id: backdrop
        left_action_items: [['menu', lambda x: self.open()]]
        title: "Example Backdrop"
        radius_left: "25dp"
        radius_right: "0dp"
        header_text: "Menu:"

        MDBackdropBackLayer:
            MyBackdropBackLayer:
                id: backlayer

        MDBackdropFrontLayer:
            MyBackdropFrontLayer:
                backdrop: backdrop
'''
)
```

まずはExampleBackdropルートウィジェットから。このウィジェットはBackdrop
ウィジェットだけ入れ込んでいます。バインディングするためにidをbackdropとして、
left_action_itemsを設定しています。left_action_itemsの仕様は以下の通りに
なります。

`left_action_items`

> The icons and methods left of the kivymd.uix.toolbar.MDToolbar 
> in back layer. For more information, see the kivymd.uix.toolbar.MDToolbar 
> module and left_action_items parameter.

> left_action_items is an ListProperty and defaults to [].

今回からは使用されている部分だけを上記のように引用したいと思います。この辺りも今後は
いちいち依頼をしないようにします。んで、この使い方というとMDToolbarのアイコンとかを
ラベルを左に配置させるものなのでしょうかね。試しにright_action_itemsとかやると全然
そのようになってくれなくMDToolbarの[≡]マークが左右両方に配置されたりだとか挙動が不明
です。結構このパーツだけでなく他もいじっていたりするのですが、なぜか意図しない挙動が多い
ように思えます。ここは安全にleft_action_itemsで決めておきましょう。このプロパティは
ListPropertyでデフォルトは空となっています。

そして、titleはタイトルのままで詳しくはマニュアルを参照ください。
タイトルはツールバーに配置されます。

次のradius_left/rightの仕様は以下となっています。

`radius_left`

> The value of the rounding radius of the upper left corner of the front layer.
> radius_left is an NumericProperty and defaults to 16dp.

`radius_right`

> The value of the rounding radius of the upper right corner of the front layer.
> radius_right is an NumericProperty and defaults to 16dp.

こちらはフロントレイヤー側の角丸の丸みとなりますかね。こちらも先ほどの挙動の不明さに
つながりますが、変更してもデフォルトから変わっていない様子を伺えました。まだ対応中なの
かな。あ、ちなみにですがkivyMDのバージョンは0から始まっている通りまだ正式リリースとは
至ってないようです(すごい今更)。まだベータ版とかなのですかね。なので今後こういった挙動
も改善されるかそのままなのかは神のみぞ知るところでしょうか。

とまぁ本題から逸れていますが、なんにせよ丸みを決めるというのは変わらずでしょう。こちらは
Numericなプロパティでデフォルトは16dpとのこと。

あとはMDBackdropウィジェットはheader_textのみになりますが仕様はマニュアル参照という
ことで。こちらはfront layerのテキスト文字列になります。

あとは連なるMDBackdropBack/FrontLayerですが、それぞれの子ウィジェットとしてContentを
仕込んでいきます。MyBackdropBackLayer側ではなぜidをbacklayerとしているのかは分かりません
でしたが、MyBackdropFrontLayer側ではbackdropに親元のbackdropを定義しています。

### Your layouts.

今度は別のところでkv側のContentあたりのコードを抽出し直したのが以下のコードになります。
こちらもさっきと同様、マニュアルと同等です。

```python
# Your layouts.
Builder.load_string(
    '''
#:import Window kivy.core.window.Window
#:import IconLeftWidget kivymd.uix.list.IconLeftWidget


<ItemBackdropFrontLayer@TwoLineAvatarListItem>
    icon: "android"

    IconLeftWidget:
        icon: root.icon


<MyBackdropFrontLayer@ItemBackdropFrontLayer>
    backdrop: None
    text: "Lower the front layer"
    secondary_text: " by 50 %"
    icon: "transfer-down"
    on_press: root.backdrop.open(-Window.height / 2)
    pos_hint: {"top": 1}
    _no_ripple_effect: True


<MyBackdropBackLayer@Image>
    size_hint: .8, .8
    source: "data/logo/kivy-icon-512.png"
    pos_hint: {"center_x": .5, "center_y": .6}
'''
)
```

まずは、import文ですが動かすためにはこれらが必要になってきます。両方とも必須なので
絶対に消すなんてことは許されません。試しにWindowの方を消すと見事に動かなくなりましたw

#### ItemBackdropFrontLayer

続けてItemBackdropFrontLayerウィジェットですがTwoLineAvatarListItemを継承
しています。これで2行表示のリストアイテムとなっているのですが、iconがandroidで
表示されなかったりとわけわかめ状態です。ただ他のアイコンは表示されています。

#### MyBackdropFrontLayer

また続けてMyBackdropFrontLayerですが、先ほどの定義しておいたItemBackdropFront
Layerを継承しています。フロントのためのものなので相互に関連していることが分かります。
結果は後で載せますが、アイコンもフロントのためなんだなと分かります。

まずはbackdropですが、Noneかーい、定義せんのかーいとツッコミそうでしたが、よく考えると
MDBackdrop(ExampleBackdropルートウィジェット)のところでbackdropを渡していたので
当たり前です。こんな馬鹿なことは考えないでおきましょう。ただしこうやってbackdropをここで
定義することもできます。\# 意味はあるのか・・・

あとはtextなんかも1行目のテキスト文字列だし、secondary_textもそのままだしで目新しい
ものはないようです。と言いたいところですが、あ！ということがあります。鋭い方はすでに気づ
かれたとは思いますが、iconはこちら側に定義していました。transfer-downとあるのでこれは
正しい挙動のようです。先ほどのicon: "android"はデフォルトということなのですね。やっと
完全に理解しました。試しにtransfer-downのアイコン定義をコメントアウトするとデフォルト
のandroidアイコンが登場されます。こちらも結果キャプチャの方に。

on_pressはリストを押すと計算したところまで下げるよーということになっています。ここでは
Windowの半分まで下げますよとしていますね。

pos_hintはそのパーツを上下左右どこに配置しますかー？ということなので、ここではMyBack
dropFrontLayerを上に合わせますねー( {"top": 1} )としています。

MyBackdropFrontLayer最後のプロパティですが、_no_ripple_effectはリップルエフェクト
のON/OFFを切り替えられます。このリップルエフェクトは何かというと、ボタンや今回ではリスト
を選択したときに表れる波紋上のアニメーションとのことみたいです。これは試しに見てもらった
方が分かりやすいこととボタンとかで出てくるだろうと見ているので詳しくは省略ということで。
githubではコメントアウトしているコードを載せているので、見てみたいという方はそちらを
お試しに。めちゃくちゃUX効果は悪いです。※今回の組み合わせという意味で。
あと当たり前ですが、コメントアウトしたということで分かるとは思いますがデフォルトはFalse
なのでご注意を。

あと、今更ですがこれだけAPIの仕様説明などなく長々と書いたのはmodule documentationを
読むのをメンドくさがりやめたためですwなのでこちらの説明は上記で以上ということで。

#### MyBackdropBackLayer

当ページも最後の触れ込みとなりました。back layerに関してのプロパティは少なくなっています。
こちらはImageを継承させています。なので、マニュアルでもあるし画像を表示させてんだなと気づ
いた方は鋭いです！まぁ、当たり前ですが。

んで、まずはsize_hintですが、これはいいですかね。詳しくは以下を参照ということで。ここは
kivyでも同じです。ネタバレになりますが、「Windowサイズの80% × 80%」という暗号だけ。
> [Programming Guide(翻訳済み) » Widgets(翻訳済み)]※size_hintで検索すると説明とか出てきます
> https://pyky.github.io/kivy-doc-ja/guide/widgets.html

sourceは見たまんまですね。特に触れ込む必要もなく。

pos_hintもさっき触れましたね。ここではcenterがプレフィックスで付くことを注意したい
ところです。おそらく自分が教授だったら、ここあたりを問題で出しそうです。そんなことは
一生なさそうだけど。

## 結果

ということで、試した結果が異なるためにExampleのコードが変わった理由でした。iconとかが
どうなっているかが目に見えて分かってきます。

![21.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/5ba89aa4-43e6-b688-56cb-db17e4368a70.png)

![22.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/82826/060e2ce3-6b7a-1958-6c9a-515f2a97b570.png)

1枚目はMyBackdropFrontLayerでiconプロパティをコメントアウトしたものですよね。
見事にandroid君(あってる？)が写っています。カスタムアイコンを使っている場合は
こうやってデフォルトアイコンを決めておくと、アイコンがないという不具合の時は何も
表示されないということが防げます。どんな状況？と言われそうですが。

2枚目は_no_ripple_effectをコメントアウトした件になります。もうエフェクトがもっさり
しすぎて波紋が遅れて見えてますね。正式なアプリとしてですが、この組み合わせにおいては
避けたい挙動になります。

## まとめ

さぁ、いかがだったでしょうか。何点かはおや？というところがありましたが、まだ発展する
ところなのか、はたまた私のやり方が悪かったのでしょうか。少しもやもやしたのは否めません
でしたが、Backdropについてはこんな感じで実装しましょうということは分かったとは思います。
まだまだ、kivyMDは奥が深いですね。精進しなければ。

とまぁこういった感じで締めくくろうとは思いますが、次回はバナーのことになります。今回より
かは楽しめるかな。ということでこの辺でまたの機会をということで。

それでは、ごきげんよう。

## 参照

> Components » Backdrop
> https://kivymd.readthedocs.io/en/latest/components/backdrop/