2014年11月17日月曜日

Android CheckBox 画像の正しいカスタマイズ方法

各テーマでのチェックボックスのスタイルは android:checkboxStyle で指定されています。
以下は 4.0.3 のコードですが、この部分は 3.0 で Theme.Holo が追加されて以降特に変わっていません。

http://tools.oesf.biz/android-4.0.3_r1.0/xref/frameworks/base/core/res/res/values/themes.xml
  1. <style name="Theme">  
  2.   ...  
  3.   <item name="checkboxStyle">@android:style/Widget.CompoundButton.CheckBox</item>  
  4.   ...  
  5. </style>  
  6. ...  
  7. <style name="Theme.Holo">  
  8.   ...  
  9.   <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>  
  10.   ...  
  11. </style>  
  12. ...  
  13. <style name="Theme.Holo">  
  14.   ...  
  15.   <item name="checkboxStyle">@android:style/Widget.Holo.CompoundButton.CheckBox</item>  
  16.   ...  
  17. </style>  
  18. ...  
  19. <style name="Theme.Holo.Light" parent="Theme.Light">  
  20.   ...  
  21.   <item name="checkboxStyle">@android:style/Widget.Holo.Light.CompoundButton.CheckBox</item>  
  22.   ...  
  23. </style>  


Widget.Holo.CompoundButton.CheckBox と Widget.Holo.Light.CompoundButton.CheckBox を見ると、Widget.CompoundButton.CheckBox をそのまま継承しているだけです。

http://tools.oesf.biz/android-4.0.3_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#1003
  1. <style name="Widget.Holo.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">  
  2. </style>  
  3.   
  4. <style name="Widget.Holo.Light.CompoundButton.CheckBox" parent="Widget.CompoundButton.CheckBox">  
  5. </style>  
つまり、Theme であろうが Theme.Holo であろうが Widget.CompoundButton.CheckBox が使われるということです。


このスタイルですが、4.1 と 4.2 で微妙に変わります。

4.1まで

http://tools.oesf.biz/android-4.1.2_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#345
  1. <style name="Widget.CompoundButton.CheckBox">  
  2.     <item name="android:background">@android:drawable/btn_check_label_background</item>  
  3.     <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item>  
  4. </style>  

4.2以降

http://tools.oesf.biz/android-4.2.0_r1.0/xref/frameworks/base/core/res/res/values/styles.xml#345
  1. <style name="Widget.CompoundButton.CheckBox">  
  2.     <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item>  
  3. </style>  
4.1 までは Widget.CompoundButton.CheckBox で android:background が指定されていますが、4.2 以降ではなくなっています。


実は 4.2 で paddingLeft の使い方が変わりました。Rtl に対応するためだと思われます。

4.1 までは左端からテキストの間が paddingLeft でした。そのため、チェックボックス画像分の paddingLeft を指定するために、背景画像が使われていました。
一方、4.2 からはチェックボックス画像とテキストの間が paddingLeft になりました。



チェックボックスをカスタマイズする場合

この違いのため、チェックボックスをカスタマイズする場合は注意が必要です。

例えばチェックボックス画像を 32 x 32 dp で作成し、画像とテキストの間を 4dp あけたいとしたら、次のように values/dimens.xml と values-v17/dimens.xml を用意する必要があります。

values/dimens.xml
  1. <resources>  
  2.     <!-- 32 + 4 = 36 -->  
  3.     <dimen name="check_button_padding_left">36dp</dimen>  
  4. </resources>  
values-v17/dimens.xml
  1. <resources>  
  2.     <dimen name="check_button_padding_left">4dp</dimen>  
  3. </resources>  



チェックボックスの画像を変えるには、checkboxStyle に指定するスタイルで android:button に drawable を指定するか、テーマで android:listChoiceIndicatorMultiple に drawable を指定します。

values/styles.xml
  1. <resources>  
  2.   
  3.     <style name="AppTheme" parent="Theme.Holo.Light">  
  4.         <item name="android:checkboxStyle">@style/CheckBoxStyle</item>  
  5.     </style>  
  6.   
  7.     <style name="CheckBoxStyle" parent="android:Widget.CompoundButton.CheckBox">  
  8.         <item name="android:button">@drawable/my_custom_checkbox</item>  
  9.         <item name="android:paddingLeft">@dimen/check_button_padding_left</item>  
  10.     </style>  
  11. </resources>  
or
  1. <resources>  
  2.   
  3.     <style name="AppTheme" parent="Theme.Holo.Light">  
  4.         <item name="android:listChoiceIndicatorMultiple">@drawable/my_custom_checkbox</item>  
  5.         <item name="android:checkboxStyle">@style/CheckBoxStyle</item>  
  6.     </style>  
  7.   
  8.     <style name="CheckBoxStyle" parent="android:Widget.CompoundButton.CheckBox">  
  9.         <item name="android:paddingLeft">@dimen/check_button_padding_left</item>  
  10.     </style>  
  11. </resources>  





おまけ : CompoundButton.java の変更について

onDraw() については、Rtl のとき右端に描画されるようになっているだけで、paddingLeft の扱いは変わっていません。

4.1.2

http://tools.oesf.biz/android-4.1.2_r1.0/xref/frameworks/base/core/java/android/widget/CompoundButton.java#228
  1. 227     @Override  
  2. 228     protected void onDraw(Canvas canvas) {  
  3. 229         super.onDraw(canvas);  
  4. 230   
  5. 231         final Drawable buttonDrawable = mButtonDrawable;  
  6. 232         if (buttonDrawable != null) {  
  7. 233             final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;  
  8. 234             final int height = buttonDrawable.getIntrinsicHeight();  
  9. 235   
  10. 236             int y = 0;  
  11. 237   
  12. 238             switch (verticalGravity) {  
  13. 239                 case Gravity.BOTTOM:  
  14. 240                     y = getHeight() - height;  
  15. 241                     break;  
  16. 242                 case Gravity.CENTER_VERTICAL:  
  17. 243                     y = (getHeight() - height) / 2;  
  18. 244                     break;  
  19. 245             }  
  20. 246   
  21. 247             buttonDrawable.setBounds(0, y, buttonDrawable.getIntrinsicWidth(), y + height);  
  22. 248             buttonDrawable.draw(canvas);  
  23. 249         }  
  24. 250     }  

4.2.0

http://tools.oesf.biz/android-4.2.0_r1.0/xref/frameworks/base/core/java/android/widget/CompoundButton.java#252
  1. 251     @Override  
  2. 252     protected void onDraw(Canvas canvas) {  
  3. 253         super.onDraw(canvas);  
  4. 254   
  5. 255         final Drawable buttonDrawable = mButtonDrawable;  
  6. 256         if (buttonDrawable != null) {  
  7. 257             final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK;  
  8. 258             final int drawableHeight = buttonDrawable.getIntrinsicHeight();  
  9. 259             final int drawableWidth = buttonDrawable.getIntrinsicWidth();  
  10. 260   
  11. 261             int top = 0;  
  12. 262             switch (verticalGravity) {  
  13. 263                 case Gravity.BOTTOM:  
  14. 264                     top = getHeight() - drawableHeight;  
  15. 265                     break;  
  16. 266                 case Gravity.CENTER_VERTICAL:  
  17. 267                     top = (getHeight() - drawableHeight) / 2;  
  18. 268                     break;  
  19. 269             }  
  20. 270             int bottom = top + drawableHeight;  
  21. 271             int left = isLayoutRtl() ? getWidth() - drawableWidth : 0;  
  22. 272             int right = isLayoutRtl() ? getWidth() : drawableWidth;  
  23. 273   
  24. 274             buttonDrawable.setBounds(left, top, right, bottom);  
  25. 275             buttonDrawable.draw(canvas);  
  26. 276         }  
  27. 277     }  
4.2 で padding の扱いが変わった理由は、getCompoundPaddingLeft() と getCompoundPaddingRight() を Override して padding を上書きするようになったからです。
  1. 227     @Override  
  2. 228     public int getCompoundPaddingLeft() {  
  3. 229         int padding = super.getCompoundPaddingLeft();  
  4. 230         if (!isLayoutRtl()) {  
  5. 231             final Drawable buttonDrawable = mButtonDrawable;  
  6. 232             if (buttonDrawable != null) {  
  7. 233                 padding += buttonDrawable.getIntrinsicWidth();  
  8. 234             }  
  9. 235         }  
  10. 236         return padding;  
  11. 237     }  
  12. 238   
  13. 239     @Override  
  14. 240     public int getCompoundPaddingRight() {  
  15. 241         int padding = super.getCompoundPaddingRight();  
  16. 242         if (isLayoutRtl()) {  
  17. 243             final Drawable buttonDrawable = mButtonDrawable;  
  18. 244             if (buttonDrawable != null) {  
  19. 245                 padding += buttonDrawable.getIntrinsicWidth();  
  20. 246             }  
  21. 247         }  
  22. 248         return padding;  
  23. 249     }  
このメソッドは TextView の onMeasure() などから呼ばれています。


0 件のコメント:

コメントを投稿