Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_launcher);
drawable.setColorFilter(Color.RED, PorterDuff.Mode.SRC_ATOP);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{}, drawable);
setBackground(stateListDrawable);
- 解説
原因は DrawableContainer の selectDrawable() での処理です。 5.0 から ColorFilter や tint を考慮するようになり、state に対応する Drawable に明示的に ColorFilter や tint をセットするようになりました。
4.4.0 の DrawableContainer.selectDrawable()
306 public boolean selectDrawable(int idx) {
...
336 if (d != null) {
337 d.mutate();
338 if (mDrawableContainerState.mEnterFadeDuration > 0) {
339 mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
340 } else {
341 d.setAlpha(mAlpha);
342 }
343 d.setVisible(isVisible(), true);
344 d.setDither(mDrawableContainerState.mDither);
345 d.setColorFilter(mColorFilter);
346 d.setState(getState());
347 d.setLevel(getLevel());
348 d.setBounds(getBounds());
349 d.setLayoutDirection(getLayoutDirection());
350 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
351 }
5.0.1 の DrawableContainer.selectDrawable()
412 public boolean selectDrawable(int idx) {
...
442 if (d != null) {
443 d.mutate();
444 if (mDrawableContainerState.mEnterFadeDuration > 0) {
445 mEnterAnimationEnd = now + mDrawableContainerState.mEnterFadeDuration;
446 } else if (mHasAlpha) {
447 d.setAlpha(mAlpha);
448 }
449 if (mDrawableContainerState.mHasColorFilter) {
450 // Color filter always overrides tint.
451 d.setColorFilter(mDrawableContainerState.mColorFilter);
452 } else {
453 if (mDrawableContainerState.mHasTintList) {
454 d.setTintList(mDrawableContainerState.mTintList);
455 }
456 if (mDrawableContainerState.mHasTintMode) {
457 d.setTintMode(mDrawableContainerState.mTintMode);
458 }
459 }
460 d.setVisible(isVisible(), true);
461 d.setDither(mDrawableContainerState.mDither);
462 d.setState(getState());
463 d.setLevel(getLevel());
464 d.setBounds(getBounds());
465 d.setLayoutDirection(getLayoutDirection());
466 d.setAutoMirrored(mDrawableContainerState.mAutoMirrored);
- 5.0 未満でどうするか
以下のコードでは、デフォルト用の Drawable からタップしたとき用の ColorFilter がかかった Drawable を用意し、StateListDrawable を構成して背景にセットしています。 このコードを 5.0 以降で実行すると思ったように動きますが、5.0 未満だとタップしたときに色が変わりません。
Drawable drawable = ContextCompat.getDrawable(context, R.drawable.ic_launcher);
Drawable pressedDrawable = drawable.getConstantState().newDrawable().mutate();
pressedDrawable.setColorFilter(pressedColor, PorterDuff.Mode.SRC_ATOP);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, pressedDrawable);
stateListDrawable.addState(new int[]{}, drawable);
setBackground(stateListDrawable);
しょうがないので、http://stackoverflow.com/questions/7979440/android-cloning-a-drawable-in-order-to-make-a-statelistdrawable-with-filters にあるように次のような StateListDrawable の subclass を用意します。
private static class PressedStateListDrawable extends StateListDrawable {
private final int pressedColor;
public PressedStateListDrawable(Drawable drawable, int pressedColor) {
super();
this.pressedColor = pressedColor;
addState(new int[]{android.R.attr.state_pressed}, drawable);
addState(new int[]{}, drawable);
}
@Override
protected boolean onStateChange(int[] states) {
boolean isPressed = false;
for (int state : states) {
if (state == android.R.attr.state_pressed) {
isPressed = true;
break;
}
}
if (isPressed) {
setColorFilter(pressedColor, PorterDuff.Mode.SRC_ATOP);
} else {
clearColorFilter();
}
return super.onStateChange(states);
}
@Override
public boolean isStateful() {
return true;
}
}
setBackground(new PressedStateListDrawable(drawable, pressedColor));