前回はこちらです
SessionDetailActivity を手元にある Nexus 7 (2013) で表示してみると、スマートフォンとは違う表示でした。
周りに余白のある、ダイアログのような表示の仕方です。
どうやって実装しているんでしょうね。
すごく知りたくなりました。知りたいですよね…?
FloatingWindow
ダイアログのような表示の仕方、iosched では FloatingWindow と呼んでいるそうです。
activity.getWindow().getAttributes() で取得した WindowManager.LayoutParams を弄って実現しています。
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);
}
解像度に応じて分岐処理
手持ちのスマートフォンとタブレットで表示が違っていたので、その分岐処理をしている箇所を見てみます。
@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 属性です。
<declare-styleable name="ThemeIOSched">
<attr name="isFloatingWindow" format="boolean" />
</declare-styleable>
この属性の値を、各解像度別の styles.xml で定義することで、FloatingWindow として表示するか否か決めてます。
属性値を定義している styles.xml は2つのみです。
<!-- 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>
<!-- 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 としています。
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
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 にどんな値が格納されているのかを見ます。
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.isFloatingWindow に false を指定していたら 0、 true なら 1 が data に格納されるようです。
なのですが、true を指定しているのに -1 が格納されている・・・
ちなみに、FloatingWindow で表示しなかった手持ちのスマートフォンでは以下のようになってます。
R.attr.isFloatingWindow を false にしているので data には正しく 0 が格納されてました。
R.attr.isFloatingWindow を true にした場合 1 ではなく -1 が格納されてドキュメント通りではないので、(floatingWindowFlag.data != 0) という評価にしているのでしょうか。
ひとまず、R.attr.isFloatingWindow を false にした場合の 0 以外であれば true と見なしているようですね。
以上です。


