前回はこちらです
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
と見なしているようですね。
以上です。