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

  • 15
    Like
  • 0
    Comment
More than 1 year has passed since last update.

前回はこちらです


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

device-2014-11-03-101007.png

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

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

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

https://github.com/google/iosched

FloatingWindow

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

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

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);
}

解像度に応じて分岐処理

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

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 属性です。

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

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

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>
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入門

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

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

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.isFloatingWindowboolean 属性で truefalse であるのにも関わらず、0 との評価をしています。

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

android.util.TypedValue

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

TypedValue.data

public int data

Basic data in the value, interpreted according to type

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

TypedValue.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

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

が格納されてました。

type 18 とは・・・

TypedValue.TYPE_INT_BOOLEAN

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

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

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

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

floatingWindowFlagoff.png

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


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

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


以上です。