2011年12月1日木曜日

Android AlertDialog の背景を変更する for 4.x

どうも、お久しぶりです。
あんざいゆきです。
世の中の Advent Calendar 祭りに乗り遅れたので、一人 Advent Calendar やることにしました。


---

前に Android AlertDialog の背景を変更する というエントリを書いたのですが、どうも 3.0 以降だとこのハックがうまくいかないという報告があり確かめてみました。

このエントリの通りにやると


のようなダイアログになるはずなのですが、悲しいかな、Android 4.0 のエミュレータで実行したら、こんな感じになってしまいました。





orz

外枠はでてるけど、AlertDialog の内部の色が変わってないので、android:alertDialogStyle が効かなくなってるっぽいです。

AlertDialog の内側の属性値の android:topBright などを参照しているのは AlertController です。
この AlertController に android:topBright などが設定されたテーマを渡さなければいけないのですが、このテーマを渡すためのメソッドである AlertController#AlertParams() が呼ばれているのが AlertDialog.Builder 内の以下の部分だけなのです。

Android 4.0
  1. 358         public Builder(Context context) {  
  2. 359             this(context, resolveDialogTheme(context, 0));  
  3. 360         }  
  4.   
  5. 373         public Builder(Context context, int theme) {  
  6. 374             P = new AlertController.AlertParams(new ContextThemeWrapper(  
  7. 375                     context, resolveDialogTheme(context, theme)));  
  8. 376             mTheme = theme;  
  9. 377         }  


つまり、AlertDialog.Builder(context) ではなく、AlertDialog.Builder(context, theme) で android:alertDialogStyle が定義されているテーマを渡さなければなりません。なぜなら、Builder(context) の方では、resolveDialogTheme() という内部メソッドに対して第2引数に 0、つまり android:alertDialogStyle が定義されていないものを渡しているからです。

一方、Android 2.3 では第2引数にテーマを渡せるコンストラクタが AlertDialog.Builder にありません

Android 2.3
  1. 272         public Builder(Context context) {  
  2. 273             P = new AlertController.AlertParams(context);  
  3. 274         }  


しかし、AlertParams() には Builder の引数の Context をそのまま渡しているため、アプリのテーマとして設定されているものが AlertController にわたり、アプリのテーマとして android:alertDialogStyle を指定しておけば反映されるという状態でした。

で、結論としては、こうすればOKです。

  1. // 独自ダイアログをつかってテーマを指定  
  2. public void showMyDialog(View v) {  
  3.     MyAlertDialog myDialog = new MyAlertDialog(this, R.style.MyDialog);  
  4.     myDialog.setTitle("dialog");  
  5.     myDialog.setMessage("test!");  
  6.     myDialog.setButton("OK", (OnClickListener) null);  
  7.     myDialog.show();  
  8. }  

↑これは上記のエントリと同じ

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     ...  
  5.   
  6.     <style name="MyDialog" parent="@android:style/Theme.Dialog">  
  7.         <item name="android:windowBackground">@drawable/rect</item>  
  8.         <item name="android:buttonStyle">@style/CustomButton</item>  
  9.         <item name="android:alertDialogStyle">@style/AlertDialog</item>  
  10.     </style>  
  11.   
  12.     ...  
  13.   
  14. </resources>  


この MyDialog の方にも @style/AlertDialog をいれます!
前はアプリケーション、もしくはアクティビティのテーマに設定していましたが、ダイアログのテーマとして指定する方にも入れると Android 4.0 でも適用されます!

ちなみに、Android 4.0 から Theme の style item の android:alertDialogTheme が追加されていました。
なので、上記のエントリで AlertDialog を継承した独自 Dialog クラスを作成していますが、これをせずに

  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <resources>  
  3.   
  4.     <style name="MyTheme" parent="@android:style/Theme.Black">  
  5.         <item name="android:alertDialogStyle">@style/AlertDialog</item>  
  6.         <item name="android:alertDialogTheme">@style/MyDialog</item>  
  7.     </style>  
  8.   
  9.     ...  
  10.   
  11.     <style name="MyDialog" parent="@android:style/Theme.Dialog">  
  12.         <item name="android:windowBackground">@drawable/rect</item>  
  13.         <item name="android:buttonStyle">@style/CustomButton</item>  
  14.     </style>  
  15.     ...  
  16. </resources>  


のようにすれば外枠やボタンを反映できるようになりました。


0 件のコメント:

コメントを投稿