GoogleI/O 2014 公式アプリ iosched の SessionDetailActivity はどのように実装されているのか (タブレット編)


前回は[こちら](http://qiita.com/ShogoMizumoto/items/a0a1723dd97e48c8a9e3)です

***

SessionDetailActivity を手元にある Nexus 7 (2013) で表示してみると、スマートフォンとは違う表示でした。


![device-2014-11-03-101007.png](https://qiita-image-store.s3.amazonaws.com/0/35834/e473b119-d829-81d3-b960-6ed7a84e0467.png "device-2014-11-03-101007.png")



周りに余白のある、ダイアログのような表示の仕方です。

どうやって実装しているんでしょうね。

すごく知りたくなりました。知りたいですよね…？

https://github.com/google/iosched

## FloatingWindow

ダイアログのような表示の仕方、iosched では FloatingWindow と呼んでいるそうです。

`activity.getWindow().getAttributes()` で取得した `WindowManager.LayoutParams` を弄って実現しています。

```Java:SessionDetailActivity
private void setupFloatingWindow() {
    // configure this Activity as a floating window, dimming the background
    WindowManager.LayoutParams params = getWindow().getAttributes();
    params.width = getResources().getDimensionPixelSize(R.dimen.session_details_floating_width);
    params.height = getResources().getDimensionPixelSize(R.dimen.session_details_floating_height);
    params.alpha = 1;
    params.dimAmount = 0.4f;
    params.flags |= WindowManager.LayoutParams.FLAG_DIM_BEHIND;
    getWindow().setAttributes(params);
}
```

***

## 解像度に応じて分岐処理

手持ちのスマートフォンとタブレットで表示が違っていたので、その分岐処理をしている箇所を見てみます。

```Java:SessionDetailActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
    // 省略

    boolean shouldBeFloatingWindow = shouldBeFloatingWindow();
    if (shouldBeFloatingWindow) {
        setupFloatingWindow();
    }

    // 省略
}

private boolean shouldBeFloatingWindow() {
    Resources.Theme theme = getTheme();
    TypedValue floatingWindowFlag = new TypedValue();
    if (theme == null || !theme.resolveAttribute(R.attr.isFloatingWindow, floatingWindowFlag, true)) {
        // isFloatingWindow flag is not defined in theme
        return false;
    }
    return (floatingWindowFlag.data != 0);
}
```

メソッド名や変数名を見れば一目瞭然ですね。

以下の条件を満たせば FloatingWindow として表示しています。

* `R.attr.isFloatingWindow` の属性値が定義されている
* `R.attr.isFloatingWindow` の属性値が 0 以外である

### R.attr.isFloatingWindow

これは `res/values/attr.xml` で独自に定義されてる `boolean` 属性です。

```XML:res/values/attrs.xml
<declare-styleable name="ThemeIOSched">
    <attr name="isFloatingWindow" format="boolean" />
</declare-styleable>
```

この属性の値を、各解像度別の `styles.xml` で定義することで、FloatingWindow として表示するか否か決めてます。
属性値を定義している `styles.xml` は２つのみです。

```XML:res/values/styles.xml
<!-- Intermediary theme for SessionDetails (so we can override it on large screens) -->
<style name="Theme.IOSched.SessionDetails.Base" parent="Theme.IOSched">
    <item name="isFloatingWindow">false</item>
</style>
```

```XML:res/values-sw600dp/styles.xml
<!-- Intermediary theme for SessionDetails, making it a floating dialog -->
<style name="Theme.IOSched.SessionDetails.Base" parent="Theme.IOSched">
    <item name="isFloatingWindow">true</item>
</style>
```

ご覧の通り、ベースは `false` とし、`sw600dp` では `true` としています。
短辺が 600dp 以上であればこの属性値は `true` なので、FloatingWindow として表示される、という仕組みなのですね。

参考:[いまさら聞けないdp入門](http://qiita.com/nein37/items/0a92556a80c6c14503b2)

### (floatingWindowFlag.data != 0) という判定は一体・・・？

FloatingWindow で表示するか判定するメソッドの `shouldBeFloatingWindow()` は返り値を `boolean` としています。

```Java:SessionDetailActivity.java
private boolean shouldBeFloatingWindow() {
    Resources.Theme theme = getTheme();
    TypedValue floatingWindowFlag = new TypedValue();
    if (theme == null || !theme.resolveAttribute(R.attr.isFloatingWindow, floatingWindowFlag, true)) {
        // isFloatingWindow flag is not defined in theme
        return false;
    }
    return (floatingWindowFlag.data != 0);
}
```

その返り値を評価しているコード・・・　一見、不安になりませんか？　不安になりましたね？

`R.attr.isFloatingWindow` は `boolean` 属性で `true` か `false` であるのにも関わらず、0 との評価をしています。

私はここで不安になったので調べてみました。（察しの良い方でしたら生温かい目で見守ってください）

## android.util.TypedValue

`TypedValue` クラスの `data` というパブリックフィールドを見ているので、まずそれを調べます。

### [TypedValue.data](http://developer.android.com/reference/android/util/TypedValue.html#data)

> public int data
>> Basic data in the value, interpreted according to type

`TypedValue` クラスのパブリックフィールドである `type` に応じて値が格納されるようです。

### [TypedValue.type](http://developer.android.com/reference/android/util/TypedValue.html#type)

> public int type
>> The type held by this value, as defined by the constants here. This tells you how to interpret the other fields in the object.

名前の通りですね。どんな `data` であるかの種別を表してます。

実際に `shouldBeFloatingWindow()` のローカル変数 `floatingWindowFlag` にどんな値が格納されているのかを見ます。

![floatingWindowFlag.png](https://qiita-image-store.s3.amazonaws.com/0/35834/b6c6fb6e-23b3-83b5-25ae-b43d4508cec8.png "floatingWindowFlag.png")

type には 18 (0x12)
data には -1 (0xffffffff)

が格納されてました。

type 18 とは・・・

### [TypedValue.TYPE_INT_BOOLEAN](http://developer.android.com/reference/android/util/TypedValue.html#TYPE_INT_BOOLEAN)

> The data field holds 0 or 1 that was originally specified as "false" or "true".
> Constant Value: 18 (0x00000012)

なるほど！ `R.attr.isFloatingWindow` に `false` を指定していたら 0、 `true` なら 1 が `data` に格納されるようです。

なのですが、`true` を指定しているのに -1 が格納されている・・・

ちなみに、FloatingWindow で表示しなかった手持ちのスマートフォンでは以下のようになってます。

![floatingWindowFlagoff.png](https://qiita-image-store.s3.amazonaws.com/0/35834/37e73e72-2b9d-81f2-1e2b-cf77a377476d.png "floatingWindowFlagoff.png")

`R.attr.isFloatingWindow` を `false` にしているので `data` には正しく 0 が格納されてました。

***

`R.attr.isFloatingWindow` を `true` にした場合 1 ではなく -1 が格納されてドキュメント通りではないので、`(floatingWindowFlag.data != 0)` という評価にしているのでしょうか。

ひとまず、`R.attr.isFloatingWindow` を `false` にした場合の 0 以外であれば `true` と見なしているようですね。

***

以上です。