In Out In

主にAndroidの技術のはなし

ローマは1日にして滞在終了となる

ローマでの自由行動

2日目

チャペルの鐘の音がどこからか朝7:45に聞こえて来る。 泊まったのはサンタマリア大聖堂から中道に入ったところのホテル、サンレモ。 昨日はチェックインでカードをもらったがどうやって使うかわからず、ドアを最初開けれずスタッフにヘルプを求めた。古そうな建物だったが、カードには実はチップが埋め込まれていてドア下の黒い部分にかざすと小さな音がしてひねると開けれるようになった。焦った。 夜は2時くらいに寝たはずだが朝は目覚ましを7:30にかけていたにもかかわらず、7時には自然と目が覚めた。前日は12時間+2時間半待ちからの2時間半フライトを体験してきて寝不足だと思っていたけど高揚感だろうか、目は覚めるものだな。 朝食はビュッフェ形式でパンやらスポンジケーキやらいかにも朝食らしいものがあった。ソーセージやチーズもあったな。なかでもサラミが絶品だった。うますぎ。給仕室まで使った皿を持って行ったら不思議そうにお礼を言われた。これがジャパニーズおもてなしですよとドヤ顔を見せてやった。 時間を朝から巻いたのでゆったり歩きながらコロッセオに向かった。途中でサンタマリア大聖堂も見た。立派な外観で外には銃を持った自衛隊が2人いて、テロ対策なのかなと思いながら道路を渡った。道の途中には自然公園的なところを横切った。鳩も餌を求めてせっせと近寄って来るし、ゴミステーションにゴミを漁る青年もいる。結構ホームレスも見かけた。 なんだか自然と楽しいという声を出してしまうくらいには良い感じだった。 コロッセオに着くとガイドツアーに参加する人たちと待ち合わせた。この人たちがまた愉快で楽しい人たちだった。本当に感謝している。 「一人で来たんですか?」と声をかけてくれた女性。30前半くらいだろうか、海外には頻繁に行くらしく、ローマを最後に8日間のイタリア旅は終わりを迎えるそうだった。阪急交通社で来た旨を話すともっと質素なのにしなよ!とアドバイスをくれた。旅行はHISの方が断然いい。安いから今度から絶対そうしな!と言われた。おすすめはトルコ、エジプト、スペインだそうだ。どことなく化粧の感じがかよねぇに似ていた。モンサンミッシェルもおすすめされたそういえばだけど。 時間になってコロッセオのガイドを受けた。手荷物検査はどこも厳しい。シーズンオフだったせいかすぐに入れた。まじでデカかった。帰る日本人女性客5人に写真を頼まれた。緊張した。そのままバスで真実の口へ。ここもいつもは30-40分待つ時もあるらしいがめちゃめちゃ空いてて写真取り放題だった。 次はスペイン坂へ。トレビの泉を経由してジェラート屋さんへ。途中2ユーロを崩したくてガムを買うといいと言われたので1.5ユーロのラベルのあるガムを買ったら2ユーロ請求されてなにもくずせなかった。チップなのかぼったくりなのかすこしもやもやした。モデルの子役みたいな子供が同じツアーに参加していて、なんかドキドキした。絶対インスタグラムをやった方がいいと思った。これは絶対にモデルの道を志すべきだ。 そのまま最高裁判所を見ながらナヴォーナ広場へ。規制がかかったらしく出店はやってなかったので閑散としていた。 パンテオンにも行ったな。パンテオンの中は丸みを帯びていて太陽からの日差しを見て何時かわかるような仕組みになっているらしい。やばすぎやろ、そしてなぜか天上は筒抜けなので雨も入ってくる。ガラスという素材は古代は作る技術はなかったのかもしれない。 そこから次はバスでバチカン付近のレストランへ。カルボナーラを注文したけど自分の方が美味しく作れると思った。一緒に同行した家族2組と楽しく話した。目の前の少年は自分と身長も近くて落ち着いてたが中3だったのでたまげた。てっきり大学生くらいかと。。。 子役モデルはマレーシアに住んでいて英語がペラペラだった。トイレが汚い話で盛り上がった。 そこからバチカン市国へ。一瞬ポケットにマネークリップがなくてスリにあったかとビビったが普通にバックの中に入ってた。 中に入って美術館を見る。めちゃめちゃ綺麗。システィーナ礼拝堂やばすぎ。あれは一見の価値あり。アテネの学堂もよかった。ミケランジェロキモいもはや。どうしてあんな絵が描けるのか。 礼拝堂を見たら次はガイドと解散になった。 クーポラに追加料金を払えばいけるよと言われ、一部の家族はそこへ向かったので解散した。サンピエトロ大聖堂もやばかった。 そこからは一人で地下鉄を使ってテルミニ駅へ。充電残量と戦いながらサンタマリア大聖堂の中を見に行った。音楽が流れていて聖歌を少し聞いた。それを聞きながらお祈りしている人もいて信仰ってこういうことなのか、っていうのと、確かにこの人知を超えた建造物を過去の人々が作ってきたという圧倒的な歴史が心の拠り所になるのはなんかわかる気がした。 帰りにパンを買って帰った。 ピサの斜塔は見たほうがいいのと、旅行するならセブ島がいいらしい。バリは臭いんだって。

見るもの全てが新しい旅を始めよう

羽田空港からパリ/シャルルドゴール空港、続いてフィウミチーノ空港へ

1日目

飛行機12時間は結構ビビってたけど、案外半分を過ぎて、もう半分か、という感じ。 酔いも少ない。 機内食で出たご飯も結構美味しかった。日本の食材を使っているような気がしたからかもしれない。思い込みというのは案外大事そうだ。 日本とフランスを繋ぐ航空機なので、CAの方々たちはほとんどフランス人だった。でもアナウンスは日本語、フランス語、英語など何回 も同じ内容を別言語でアナウンスをすることで対応しているっぽい。 飛行機出発前の説明ビデオでは。人種の多様性を強調しているのか、黒人、ヨーロッパ系、アジア系の様々な人かつ短髪から長髪まで多様な髪型をした人たちが出演していた。この辺も国内旅行とは違うなと感じた。何より目の前にある電子タブレット。映画も見れて飛行機の航空情報、機内食のメニューまで見れる。神!!! これは長時間の飛行機に乗ってても飽きないだろうなぁ。 ずっと座っているせいで左のお尻と骨盤が痛い。とはいえ窓側なので気軽にトイレするために立つことも難しい。隣の微笑ましいカップルが席を立ったら一緒に立つようにしている。 トイレに行った時も海外の人がトイレの場所教えてくれるし、食べ終わった機内食のゴミも通路側の男性がもらってくれてCAに渡してくれたりした。チップを払いたい。 見るもの全てが新しい。聞こえて来る音も半分はわからない。海外旅行、最高ではないか。

Water pleaseて言ったらコーラが来てまあ良いかと思いながら受け取るものの、だいぶショックだった。おれの英語はクソだ。 とはいえ見た目で日本人とイメージを持たれていることが多いので、日本人は頑張って英語で言うよりも、水っていう方が伝わりそうだ。現にCAさんもお水って言ってて慣れているのだろう。 出会ったカップルを見ていて思うがフランスは男性がとても紳士で女性を丁寧に扱っている感じがした。女性が散らかしたものは男性が整理する。何かとサポートをする風景をよく見た。真のレディーファーストとはこういう物を言うのだ。 シャルルドゴール空港はマヨネーズの匂いがする。 空港広過ぎ。広いというのはスペースを多く使っているというのもあるし、ゲートも多い。 たぶんガイドがなければ余裕で辿り着けないだろう。見る人見る人綺麗な人が多くて、俳優や女優とは一体、、、となった。普通になれるやろ、っていうスペックの人がたくさんいる。ただスタイルとか含めると確かにだらぢないような人が多い印象を受けたが。 フィウミチーノ空港には23:25に到着する。いったい空港は何時まで空いてるんだろう、、GoogleMapを見るとバスも通っているらしい。電車はさすがに終電は早い。まああるだけでもありがたいけど。 タクシーは空港からテルミニ駅まで一律で48€らしい。これまた嬉しいやつやな。とはいえ自分が乗ったやつは日曜かつ深夜ということもあり割増で60€請求された。まあおそらくぼったくられたというやつで、海外旅行初日にして手痛い洗礼を浴びせられた。まあホテルまでちゃんと乗せていってくれたから悪い人ではない。チップということで自分の中で譲歩した。 バタンキュー。

自信がある人は自信に満ち溢れている

自分は自信がない。 自信がなさすぎて気にしすぎる性格をしているし、自信がなさすぎていつも胃の調子が悪い。

自信がある人は自信があるからこそ自信がある人足り得るのだと思う。 表情や、ファッションや、言動や、立ち振る舞いに自信というのが漏れ出しているような人がいる。

学生時代もそうだった。

あ〜こういう人になりたい

こう思わせられた人は自信があるように思えた。とかく自信がある理由はなんでもいい。自分の中で納得して腑に落ちていればいい。 なんか自信があるという人もいれば、自分のようにあらゆることが完璧でないと自信を持てないという人もいる。 ただ自信があるという確信があればそれは魅力になるんだと思う。

とはいえ、中途半端な状態で自信を持ちたくないなとも思ったりして。いろんな経験を20代のうちにして、人から尊敬されるような実績も作って、30代くらいで自信を付けれればいいなと思う。第一印象は魅力的に思われることはできても、長く時間を共にしたり、仕事で一緒になった時に影で幻滅されたくはない。それこそ自分のような完璧主義の人間には最も自信を打ち砕かれる要因になる。

先日髪を切った。

日本一予約が取れない美容師を予約して切ってもらった。予約するにはインスタグラムのストーリーで予約解放の情報を事前にゲットし、その解放時刻から1分が経過するまでに予約を完了させなければならない。そうやって向こう1ヶ月の予約が全て埋まる。とんでもない美容師だ。 なぜそんなに人気なのかというと、カットの技術はさることながら、ヘアスタイルの日本一を決める大会で3連覇。何よりプロフェッショナルに出演したということが要因が大きい。メディアに取り上げられるとやはり興味をそそられる。

そんな美容師にカットをしてもらって体感したのがやはり自信だった。漏れ出していた。身に纏っていた。とても魅力的であった。 正直髪型とかどうでもよくて、その人と1対1の時間を作れたという体験の方が圧倒的に価値があったように思えた。

何をしていてもかっこいい。歩くだけで、指示を出すだけで、声を出すだけで、なぜか魅力がある。

そこで感じたのは、自信がある人には嘘やたぶんがないということだった。 発言、言動が全て自分の実体験から話されているのだと思った。本で誰かが書いていた言葉を発しているのではない。世間の常識を正当化するように吐き捨てているわけでもない。20代で経験してきた時に得た自分なりの答えを試行錯誤して出した考えを元に自分の言葉として紡ぐ。出した言葉の裏には見えない説得力として努力の山が積み上がっているような気がした。

こういう大人になりたいと久しぶりに思えた。

学生時代の中身のない他者への憧れとは全く違う、内面から溢れ出るエネルギーに感動したのは人生でも初めてだったかもしれない。 それくらい興奮した瞬間だった。

自分は自信がないからこそ、まずは既存のコミュニティに裸足で立ち向かっていかないといけない。鍛えた武器で実績を出して、寄っていく人生から寄られる人生になっていかないといけない。寄られる頃には自分にも自信が満ち溢れているんだろうなと将来像をあのプロフェッショナル美容師に定めることにした。

最高を体験するのはとてもいいことだ。 最高とそこそこの違いがなんとなくわかるようになる。無駄なものには手をつけなくなる。今回最高の美容師を体験したのもそれが理由だったりした。 思わぬ成果もあったが、最高を知っておいて損はないなと改めて思った。最高には最高を目指す人たちが集まる。とてもいい環境だ。

最高の職場、最高の景色、あらゆる最高の何かをできる限り早く体験していきたいと思う。

どこかの最高になれば隣の最高に近づきやすくなる。最高はさらなる最高を目指して最高同士でつながり始める。 世間は狭い。似た者同士。

今までのつまらない人生が少しだけ面白くなり始めた気がした。

詳解AdMobを書いた

最近Android界隈のプレゼン資料を拝読させてもらっていて、頭に 詳解 とつけるとなんかイケてる記事な気がして自分への暗示と読んでもらいたいという期待も込めてつけてみた。

今年のDroidKaigiに応募したものの、採用はされなかったので供養もかねてQiitaに投稿してみた。

qiita.com

サンプルも作った。

github.com

業務で約2年間くらい片手間で広告の実装をやってきていて、結構な時間を費やして思考錯誤した。 搭載していたアドネットワークも何度も作り直したし、Android Jetpackとの親和性も考えながらより良い設計を考えたりもした。 広告界隈もAndroidに比べると微速ではあるが進化しているようで、勉強してみると意外と面白かったりする。

年末で今の会社を退職するのでエントリーがてら記事にできそうなものかつ、あまり情報が落ちていないものを書いてみた。

晴れた気持ちでヨーロッパ旅行へ向かう。

「この機能があったら」という願望は社内では発言しないほうが良いと思う話

暑い

梅雨も明け、夏を感じ始めると夏バテとともに仕事でもイライラすることが増えてくる。

エンジニアとのコミュニケーションには気を使って欲しい

既存の不具合や改善点を議論する時、ふと非エンジニアが思いついたアイデアや、開発スケジュール上で実装が控えている新機能や改善点を引き合いに出される時がある。

 

「あーこの機能が入ってれば◯◯の問題は解決できるのに」

とか

「この◯◯を実現したいので早くこの機能入れましょう!」

といった感じだ。

 

エンジニアとのこういったコミュニケーションは負の感情しか生まないのでやめたほうがいいと思う。Slack上で見るだけで一瞬とてもイライラしてしまう。

 

なぜイライラするのか考えてみた

自分のイライラの感情の中には下に列挙したような要素が入り混じっているのかなと感じた。

  • それを実装するのはエンジニアだということ

  • 整理された開発スケジュールを無視して発言されていること

  • ユーザー視点の意見ではなく個人の満足を求めて発言されていることが多いと感じること

  • たいていが余計な一言

 

実装するのはあくまでエンジニア

この前提はどうしても覆らない。そしてリソース量は社内によって変わるだろうが、そのリソースの中でどれだけ品質を高く、最短で機能を実現していくかを考えていくことがプロダクトオーナー(ベンチャーでは経営者が担当することもあり得る)の役割であり、エンジニアの最優先タスクとなる。普段の業務でサボっているなら別だが、大抵はスプリントに追われ、ユーザーやプロダクトオーナーからの要件を日々実装しているはずだ。そんな中でその構造化された開発体制を無視して権力や発言力の高い非エンジニアが割って入ってくるのは自分勝手ですごく気になる。

 

開発スケジュールを無視した発言である

エンジニアはなんのために開発スケジュールを作るのか。大きく2点あると思う。1つは限られたリソースを有効に使うため(自分を守るともとれる)。もう1つは非エンジニアとの要件に関するコミュニケーションを円滑に行うためである。

一度要件を分解して実装を考えないとエンジニアもどれくらいの時間がかかりそうかを見積もるのは難しく、非エンジニアならさらに難しいことだと思う。そこから発生する認識のズレはコミュニケーションを取っていく上で大きな壁になる。そこをクリアにしていくのが開発スケジュールだと思っている。今回物申したい件ではこの開発スケジュールが無視されている。なんのために開発スケジュールを日々整理し、スプリントMTGで全社に共有し、全社MTGなどで共有し、日々のイシュー管理をしているのかわからなくなってしまう。

 ユーザー視点の意見ではなく個人の満足を求めて発言

これは全てに当てはまるわけではないと思うし、完全に個人の見解だが、まずそもそも、その機能があったとして、ユーザーが抱える問題を解決できるかはわからない。その機能があれば便利になる、何かが円滑に進む、そういったことはあるかもしれない。ただその機能がなくてもユーザーはサービスを使ってくれることが多い。単発で思いつく機能の価値なんてそのレベル。

少し話は逸れるが、「この機能があったら」という願望があればあるほど経営者やプロダクトオーナーへの信頼は失われていくと思っている。なぜかというと、その願望を実際に開発スケジュールに組み込んでエンジニアリソースを活用して機能をリリースするまでが彼らの役割だからだ。願望を言うだけなら子どもでもできる。何よりその願望をエンジニアに直接投げられても、すでに開発スケジュールは埋まっているから、エンジニアとしては、「残業して早くこの機能作ってくれ」「おれが欲しいから片手間で実装頼んだ」みたいな解釈と取れるよなということがよくある。

大人しく待つことが信頼に繋がることもあると思う

まずは開発スケジュールの相談から始めて、余裕のあるリソースはあるか、リリースを優先順位的にずらせるものはあるかをセットで考えてもらいたい。その上で議論をしよう。そしてそこで直近の実装が難しいとなったら大人しく実装完了を待っててくれ。

まとまらないけれど

最近、仕事の中で、働き方とか周囲の人との関わり方を考えることが増えた気がする。

この人はどういう人なのか。

こういう関わり方をすると良い雰囲気になるだろうか。

こういう仕組みを導入すればチームはうまく回るだろうか。

今自分に足りないもの、チームに足りないもの、会社に足りないものはなんだろうか。

 

今まではチーム内において自分の上に上司がいて、基本はその人の指示に従うというスタイルだった。それはそれで不満もなく、特に意見が食い違うこともなくて、自分の作業に集中できていた。同時に、考えなくても良いことを無意識のうちに頭の中から排除していたし、何か聞かれれば自分の意見を答える。何かを任せられればそれを一生懸命妥協せずにやる。そういう意識で会社に向かっていた。これはこれでとても大変で、常に他人への配慮が欠かせなかった。上司の顔を立てる、自分の意見を強く押し過ぎないこと、チームの雰囲気を良くすることはあっても悪くすることはできないというプレッシャーがあった。チームの一員として、チームのために動くことが求められた。

 

リーダーというものは、一緒に作業している人たちの不安や懸念を取り除いて、快適に作業ができるようにする役割が半分を占めるレベルで重要だと思うようになった(もう半分はざっくりしているけど、 `前進していく力` だろうか)。

そういう意味で前の上司はとても良いリーダーであったし、参考にできるところが多々あった。

そんな上司が抜けたときから、考えることが莫大に増えたのである。

上司が抜ける話を聞いた時は、そこまでやることは今までと変わらないだろうなと思っていた。属人化している作業は極力無くしてきたし、日々の業務も、どちらかが体調不良になっても肩代わりできるように長年かけて情報共有を積み重ねてきた。チームのための気配り、働き方を常に上司から指摘されてきたので、ここに関しては根拠のない自身があった。あれだけ頑張ってきたんだから、これ以上大変なことなんてそうそうないだろうな〜と。

ただ、いざ蓋を開けてみると、実際の自分の作業と上司の作業では属人化している作業ばっかりだったことに気づいた。もう少ししっくりくるように言い換えると、本当に属人化している作業は気付けないままだったのだ。

エンジニアはコードを書く時間よりも考える時間の方が圧倒的に多い。優秀なエンジニアになるには、いかにコードを書かないかだ、という格言を聞いたこともある。

自分に足りないこと、前の上司にあって、自分にはなかったもの、それは考えることだ。考えるといっても、コードの書き方や設計の話ではない。

チームとしてどうありたいか。

抜け落ちた作業はないか。

チームがうまく回るにはどうしたら良いか。

他のチームはうまく回っているか。

そういう気配り、常に色々な問題を認識しようと考えることが今まで出来ていなかったのだと気付かされた。

問題を解決するには、まずは問題を問題だと認識することから始まる。問題を発見するのが一番難しい。日々の作業の中で生まれる違和感、まとまった情報を見たときの差分、メンバーが裏側で考えていることや感じていること、そういった所に意識を向けなければ気付くことは難しい。そういった気付きが前の上司は圧倒的に多かった。

ただこれは自分が会社に対して期待されている水準や立場、チームの状況によって持つ意識も変わる。そういう意味では、今までの自分の役割的には出来なくても仕方なかったかなと思う。そういう意識は極力持たないようにしてきた。振り返ってみると、上司にはよくここに関しては注意されていた。意識を高く持つこと、気付けるようになるように意識すること、自分の作っているアプリは常に触らないとダメだということ。チームで働く以上は自分と同じレベルで意識を持っていてほしい。そういうことだろう。現実にはそれを実現することは難しいが、チームの理想としては必ずそうだろう。追い求め続けることは大事なことだと思う。

このタイミングで、自分がこういうことを考えやすい立場になれたのはとてもありがたいことだった。今までの自分の状況からその意識を変えられるのは環境だけだったと思う。

 

苦労や悩みも増えたけど、今までよりも1つや2つ高い視点から意識を巡らせることが求められているし、それに応えようと頑張れている。こうやって人は成長していくのかなと達観している。

 

立場が変わると頻繁に話をする人が変わり、内容も大きく変わる。今自分が話すのは技術以外のことが多くなってきた。技術の話も技術以外の話もそうだが、積み重ねている人というのは、話し方でわかるものだ。言い回しや業界用語、話の組み立て方、その人から発する全ての言葉がその人のスキルセットを物語る。

能力は基本、誰かの真似をすることで自分のものにしたときに付いていくものだ。すごい人たちはすごい人たちから吸収してきたわけだ。話し方も、技術も、生き方も。

真似をすることで自分を見失うこともある。自分は絶賛その真っ只中で、今これは何を話しているんだ、ということがよくあって、たいして自分の意見でもない真っ当な正論をかっこよく言い切って見せる。恥ずかしくなりながら頭の中でその `誰か` を退場させる。2度と出てこないでくれ。

おそらくその経験を何度も繰り返して、浸透したところでそれは無意識での振る舞いとなり、成長し、自分になる。

 

書きたいことがあるはずだけど、それを言葉にし始めると全然文字を打てなくなってすごくモヤモヤしている。これはまだ自分の中の誰かが自分になりきれていない証拠だ。

25歳になったときにはこうなっていたい、という理想をこの前メモに書いた。その誰かになれるようにまだまだモヤモヤし続けようと思う。

 

DataBindingとRecyclerViewをもう少し便利に使う話

初めてブログを書き始めます。

コミュ障なので勉強会とかでは発表する勇気がないのでこうやってひっそりと記事にして情報を発信していく所存です。

誰かの参考になれば幸いでございます。

RecyclerViewとDataBindingとフィードUI

フィードを表示するビューをRecyclerViewで作って、かつ最近なんかはGroupieを使ってDataBindingの自動生成クラスをBindableItemに詰めて、onBindで data のタグ内に定義してあったkotlinのdataクラスをセットする、なんて実装は結構簡単で、パフォーマンスも良く、誰がみてもイメージしやすい形なのではないかなと思います。 GroupieはBindableItemを継承して作ったItemの種類ごとにViewTypeを判別してくれたり、クリックリスナーもItemのコンストラクタに入れてあげると、onBindの引数からpositionが取れたり、コンストラクタに一緒に詰めていたkotlin dataクラスをそのまま引数で返したりもできるので、Adapterを一から作るよりもエンジニアによって実装方法が大きく変わったりしないのでとても良いなと思っています。

1つのフィードUIでユーザー体験を成立させるのは難しい

特に最近のアプリは機能が多様化しているし、SNSなアプリを取ってみても、 いいね機能コメント機能アーカイブ的機能シェア機能各画面への遷移 など1つのフィードで出来ることが増えてきました。さらにはフィードの合間で広告が入ったり、地味に機能が異なる似たフィードUI、他のユーザーのおすすめ、運営からのピックアップ、フォーマットが異なるメディアコンテンツ、などなど。デザイン要件は広がる一方です。

普段使っているライブラリをさらに賢く使いたい

業務でコードを書いていると、スケジュールの厳しさやその日の気分に負けて安易なコーディングを選択してしまうかもしれません。しかし、そのチームやあなたの怠惰はいつの日かチームもしくはあなたの首を締めにくるでしょう。リファクタリングと機能開発は両立していかなければいけないのです。しかし難しいのは、自分の書いたコードがそもそも安易なのかすら分からないことではないでしょうか。常に隣にスーパーエンジニアが居てくれたらどれほどいいことか。話は少し外れましたが、こういったエンジニアを悩ませてくる機能開発に対して、今回はフィードUIを例にとってみて、こういうことやると良いのでは?みたいな意見を少ないですが書いていこうかなと思います。

複数の複雑化していくフィードUIに対するアプローチ

実装的な視点 : Groupieを使う

  • これに尽きる気がします。余計な実装は省いてくれつつ、痒いところに手が届いてくれる。内部ではDiffUtilを使ってくれているし、executePendingBindings()も呼んでくれるし、Databindingを使うこともできて各クラスの記述を少なく出来るし可読性も上がります。部分的な更新処理も比較的簡単に書くことができて非常に便利です。 そしてAdapterクラスが各画面ごとに増えていくのが防げるって結構メリットだなと思っています。似た要件を持った画面って結構振り返ると多かったりすると思っていて、そんな時にItem単位で見た目を管理できるっていうのは非常に柔軟性が高いと思います。 Groupieやその他RecyclerViewに関するTipsが載っている詳細は有志の方々が記事で残してくださっているのでそちらを参考にしてくださいませ。

  • executePendingBindings()系の記事では一番まとまっているかなと思います。 techbooster.org

  • Groupieがある昨今はDiffUtilをベタで使うってあまりない気がしますが。 qiita.com

  • 実務での経験と紐付いていて参考になるところしかないです。 qiita.com

  • ラクして実装したい場合はこういうの使うのも良いのかも。 qiita.com

実装的な視点 : 共通化を検討する

AbstractActivityを作ろうってわけじゃないんですが、共通なUIに関してはDataBinding的要素で言うと、Baseなレイアウトを定義してIncludeしていこうよ、っていうことと、共通で参照するデータはBaseなkotlin dataクラスを定義して継承していくと良いのではないかなと思っています。メリットとしては以下です。

  • 同じようなコードが増えるのを防げる
  • デザイナーが作る時と似たデザイン設計になると思う
    • フィードUIをシンボル化する的な?Sketchでは共通パーツは1箇所変更すると他の参照しているところも一気に変わってくれるらしい
  • 無謀なデザインが来た時に逃げ道になる
    • 通化できないことによるデザイン的な違和感を指摘できたり、「これ対応するとこういう風に工数増えるので...」といった説得材料になる
  • コードそのものが仕様になる
    • 仕様的なズレが要件定義の段階で指摘できる可能性が広がる、というのと、フィードUIはItemクラスとlayoutのxmlさえ見れば理解できるようになると思っています。

ざっくりLayoutとkotlin dataクラスの例を書いてみたいと思います。 今回はBaseなデザインに 画像タイトルメッセージユーザー名 の入ったUIを想定していて、そこに何か追加のパーツが必要なデザインが降りてきた場合を考えてみます。

Base Class

// item_base_feed.mxl
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools">

    <data>

        <variable
            name="viewModel"
            type="com.sample.feed.BaseFeedDataViewModel" />
    </data>

    // megeタグとか使っても良いのかも
    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <ImageView
            android:id="@+id/image"
            android:layout_width="50dp"
            android:layout_height="50dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:loadImage="@{viewModel.image}"/>       
       
        <TextView
            android:id="@+id/title"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/space_6dp"
            android:ellipsize="end"
            android:maxLines="1"
            android:text="@{viewModel.title}"
            app:layout_constraintEnd_toEndOf="@+id/parent"
            app:layout_constraintStart_toEndOf="@+id/image"
            app:layout_constraintTop_toTopOf="parent"/>

        <TextView
            android:id="@+id/message"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:ellipsize="end"
            android:maxLines="1"
            android:text="@{viewModel.message}"
            app:layout_constraintEnd_toEndOf="@+id/parent"
            app:layout_constraintStart_toEndOf="@+id/image"
            app:layout_constraintTop_toBottomOf="@+id/title"/>

        <FrameLayout
            android:id="@+id/item_layout_container"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:foreground="@drawable/ripple"
            android:onClick="@{viewmodel::onClickItemLayoutContainer}"
            app:layout_constraintBottom_toBottomOf="@+id/image"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/user_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginStart="@dimen/space_4dp"
            android:singleLine="true"
            android:ellipsize="end"
            app:clickListener="@{viewmodel::onClickUserName}"
            android:text="@{viewmodel.userName}"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/image"/>

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
abstract class BaseFeedDataViewModel {

    val title = ObservableField<String>()
    val message = ObservableField<String>()
    val userName = ObservableField<String>()
    val image = ObservableField<String>()
    
    open fun setData(feed: BaseFeed) {
        title.set(feed.title)
        message.set(feed.message)
        userName.set(feed.userName)
        
        profileUrl.set(feed.image)
        profileUrl.notifyChange()
    }

    open fun clearData() {
        profileUrl.set(null)
    }

    abstract fun onClickItemLayoutContainer(@Suppress("UNUSED_PARAMETER") view: View)

    abstract fun onClickUserName(@Suppress("UNUSED_PARAMETER") view: View)
}

Extend Class

// item_extend_feed.xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:bind="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="viewModel"
            type="com.sample.feed.ExtendFeedDataViewModel" />
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <!-- 何か新たに定義したい情報を増やせばOK -->

        <include
            android:id="@+id/base_feed_view"
            layout="@layout/item_base_feed"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/◯◯◯"
            bind:viewmodel="@{viewModel}" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
class ExtendFeedDataViewModel(private val listener: OnFeedClickActionListener) : BaseFeedDataViewModel() {

    // 何か新たに定義したい情報を増やせばOK
    val ◯◯◯ = ObservableField<String>()

    private lateinit var currentExtendFeed: ExtendFeed

    override fun setData(feed: ExtendFeed) {
        super.setData(feed)
        currentExtendFeed = feed

        // 新たに定義したい値のセット
    }

    override fun clearData() {
        super.clearData()
    }

    override fun onClickItemLayoutContainer(@Suppress("UNUSED_PARAMETER") view: View) {
        listener.onFeedClick(currentExtendFeed)
    }

    override fun onClickUserName(@Suppress("UNUSED_PARAMETER") view: View) {
        listener.onUserClick(currentExtendFeed)
    }
}

今回の記事ではGroupieは本題ではなかったのでDataBindingに処理を寄せた形でサンプルを書いてみています。 このスタイルのポイントは主に3点あるかなと思います。

  • BaseFeedDataViewModelのonClickのメソッドはabstractメソッドになっていること
    • 拡張したViewModelの方で特定の処理を書ける
    • DataBindingのonClickは問題なく反応してくれます
  • item_extend_feedからbindで渡しているExtendFeedDataViewModelはBaseFeedDataViewModelを継承しているので問題なく渡せるということ
    • こちらもDataBindingのバインド処理はうまく機能してくれます
  • 新しいフィードUIが要件として登場しても少ないコードと正確さで実現できる柔軟性がありそう(だと思ってます)

GroupieのBindableItemのonBindメソッド内で処理を書いても似たようなことは実現できそうですね! クリックリスナーはGroupieだとonBindメソッドが呼ばれるたびにセットしてしまったりするのはこちらのコードだと防げそうではありますが、そこまで徹底しなくても良いのではとも思う今日この頃。はやく梅雨明けないかな。