【Android】AndroidでFlowLayout（タグクラウド的なあれ）を実現する

2018/2/28、スタディプラス株式会社を退社しました。
2019/11/5、リポジトリが消えていたので、近々書き直します。今となっては[Flow](https://developer.android.com/reference/android/support/constraint/helper/Flow)を使えばいい気もしますが…

---

こんにちは、りんごのマークをこよなく愛すmayahiroです。
りんごのマークをこよなく愛していますが、今もポジションはAndroidアプリエンジニアです。

アドベントカレンダーもひと段落した、今日この頃、皆様年末進行でやられていらっしゃいますでしょうか？

# タグクラウドっぽいレイアウト

さて、Android開発において、タグクラウドっぽいレイアウトを行いたい場合、どうすれば良いのでしょう？
~~この度、[Studyplus](https://studyplus.jp)の[Androidアプリ](https://play.google.com/store/apps/details?id=jp.studyplus.android.app&hl=ja)において、~~それっぽいレイアウトを実現する必要があり、Google先生に聞いたところ以下の2つのライブラリが有名なようでした。

- https://github.com/ApmeM/android-flowlayout
- https://github.com/blazsolar/FlowLayout

これらのライブラリを利用してもよかったのですが、初めてのCustomLayoutを作るいい機会かもと考え、自前のシンプルなFlowLayoutを作成することにしました。

- なお、ここでいうタグクラウドっぽいレイアウトについては、以下のようなレイアウトを思い浮かべてください。
![f95eabbc-afbc-9853-8d33-c894463fac46.png](https://qiita-image-store.s3.amazonaws.com/0/69858/8cd43387-68ec-730a-dc22-968252dec5a6.png)

## 実装

~~[FlowLayout.java](https://github.com/studyplus/FlowLayoutSampleApplication/blob/master/app/src/main/java/jp/studyplus/android/flowlayoutsampleapplication/FlowLayout.java)をご覧ください。~~

~~[onMeasure](https://github.com/studyplus/FlowLayoutSampleApplication/blob/master/app/src/main/java/jp/studyplus/android/flowlayoutsampleapplication/FlowLayout.java#L55)で必要な幅と高さを計算して、[onLayout](https://github.com/studyplus/FlowLayoutSampleApplication/blob/master/app/src/main/java/jp/studyplus/android/flowlayoutsampleapplication/FlowLayout.java#L99)で実際に子Viewを配置しています。~~

## というわけで、紆余曲折ありましたが出来上がりました

何事も初めてというのは大変なものですが、人間やれば出来るものです。
ということで、出来上がったFlowLayoutを使ったサンプルアプリは~~[こちら](https://github.com/studyplus/FlowLayoutSampleApplication)~~。

~~- https://github.com/studyplus/FlowLayoutSampleApplication~~

ちなみに、本体アプリから切り出す過程で別物といえるほど書き換えてしまったので、これから本体アプリにフィードバックする作業ががが・・・

## 作ったレイアウト

1.画面幅を考慮して子Viewをいい感じに配置する

![b222040a-f239-7909-a32f-5d7110b45000.png](https://qiita-image-store.s3.amazonaws.com/0/69858/cd8318fe-9b36-8e6d-0317-5598253c4a75.png)

2.子Viewが全て同じサイズだった場合にいい感じにマージンを調整しつつ配置する

|マージン未調整|マージン調整|
|---|---|
|![94043621-96e0-3680-f7b4-d880da422ca3.png](https://qiita-image-store.s3.amazonaws.com/0/69858/64585ab4-0c96-6e8d-219c-8e757a3c67a9.png)|![1efedc9d-d104-9025-fb10-9e74fb65f673.png](https://qiita-image-store.s3.amazonaws.com/0/69858/5476e723-11f5-65c0-f461-32ced7482508.png)|
|![a09cfdfc-e201-8624-57c8-0ef52d68aee2.png](https://qiita-image-store.s3.amazonaws.com/0/69858/a0542f6e-3490-7c89-efd8-14c490e1f6f4.png)|![118092fd-7946-2ca1-e240-7ca41d698a1d.png](https://qiita-image-store.s3.amazonaws.com/0/69858/743c03e1-7451-d0e8-37ff-b62596c6fe98.png)|

3.画面の向きに関わらず、いい感じに配置します

|ポートレート|ランドスケープ|
|---|---|
|![945e2256-7a4e-909a-4032-3629da2d947c.png](https://qiita-image-store.s3.amazonaws.com/0/69858/27114699-3f52-b93b-7d38-ebebdb25d57c.png)|![a9555ce5-2036-21ce-debf-debae7c26654.png](https://qiita-image-store.s3.amazonaws.com/0/69858/f98bdc55-1c85-dc56-b634-0afa22b9c2d8.png)|
|![b0239555-86a2-5d7b-6c2c-3b6c1cfe5878.png](https://qiita-image-store.s3.amazonaws.com/0/69858/48dd0f0d-20f1-3bfd-03a5-9d10899c1c86.png)|![772744af-065e-e075-694f-afb8e5389824.png](https://qiita-image-store.s3.amazonaws.com/0/69858/a841419c-0883-ffd2-f4fe-7c9a5993d489.png)|
|![f886f315-3d9f-5e44-c8dd-dcf2409781de.png](https://qiita-image-store.s3.amazonaws.com/0/69858/4a086dae-9650-569c-fe18-1eb936f47a43.png)|![e99599ba-d692-6237-be2a-1ec0f636dbb7.png](https://qiita-image-store.s3.amazonaws.com/0/69858/302e866c-17c8-e47e-e49e-76368f92ac71.png)|
|![1b602734-532e-609e-1dee-2de8d9b88836.png](https://qiita-image-store.s3.amazonaws.com/0/69858/edf04638-0d97-3f8f-74e6-b1957fcf5d6d.png)|![09e1f782-00f7-2b63-fb85-d47d6ae268bc.png](https://qiita-image-store.s3.amazonaws.com/0/69858/416d5949-cccd-7348-7e23-be85c62c04ed.png)|

## 使い方

1.適当にlayoutに配置

  ``` xml
<xyz.mayahiro.flowlayoutsampleapplication.FlowLayout
    android:id="@+id/flow_layout_1"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="16dp"
    app:allChildrenSameSize="false"
    app:minMargin="4dp" />
  ```

  - allChildrenSameSizeを `true` にすると上記2のマージン調整が実行されます
    - **必ず、全ての子Viewを同じサイズにしてください**

2.適当にデータをセット

  ``` java
LayoutInflater layoutInflater = LayoutInflater.from(this);

FlowLayout flowLayout1 = (FlowLayout) findViewById(R.id.flow_layout_1);
String label = "ho";
for (int i = 0; i < 10; i++) {
    CheckableChip chip = (CheckableChip) layoutInflater.inflate(R.layout.checkable_chip, flowLayout1, false);
    chip.setData(R.drawable.ic_local_offer, label, false, true);
    flowLayout1.addView(chip);

    label += "ho";
}
  ```

3.詳細については~~[サンプルプロジェクト](https://github.com/studyplus/FlowLayoutSampleApplication)~~をご覧ください。

## 未だに悩んでる箇所

### allChildrenSameSize

命名がイマイチなのはさておき・・・全子Viewが同じサイズならマージン調整とかした方が綺麗だよなーという考えで作ったのですが、ここは`layout_align`に`auto`と`justify`を設定できるようにして、`justify`だった場合はマージン調整（均等割り付け）を行うべきなのか？？？

ただ、それだと最終行だけやたらと子View間が離れたりするなー・・・

などなど、と未だにうだうだと悩んでいるのですが、この辺はきっと時が解決してくれるでしょう。

## 最後に

初めてのCustomLayoutということで、「これはちょっと・・・」という部分もあるかもしれません。。。
ご意見、ご感想、Issue、Pull request等々、お待ちしております。