With this situation, we must find the way to make ImageView/Button states list with only one PNG image (drawable). Through this post, I will present 2 solutions to resolved this problem, they are so simple and easy to apply in your code.
Solution 1: Use StateListDrawable
// Create the colorized image (pressed state) Bitmap one = BitmapFactory.decodeResource(context.getResources(), imageResource); Bitmap oneCopy = Bitmap.createBitmap(one.getWidth(), one.getHeight(), Bitmap.Config.ARGB_8888);Put "state Bitmap" into a Canvas and set it's color status:
Canvas c = new Canvas(oneCopy); Paint p = new Paint(); int color = ContextCompat.getColor(context, desiredColor); p.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); c.drawBitmap(one, 0, 0, p);Finally, adding this state (pressed) to an StateListDrawable, we'll have a new state which is not the default:
// Pressed State stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, new BitmapDrawable(context.getResources(), oneCopy));Put these step in a static method, wrap in a separated class, we can use it any where later:
package info.devexchanges.statesdrawable; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; import android.graphics.PorterDuffColorFilter; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.StateListDrawable; import android.support.annotation.ColorRes; import android.support.annotation.DrawableRes; import android.support.annotation.IntRange; import android.support.v4.content.ContextCompat; public class DrawableUtils { public static StateListDrawable getStateListDrawable(Context context, @DrawableRes int imageResource, @ColorRes int desiredColor, @IntRange(from = 0, to = 255) int disableAlpha) { // Create the colorized image (pressed state) Bitmap one = BitmapFactory.decodeResource(context.getResources(), imageResource); Bitmap oneCopy = Bitmap.createBitmap(one.getWidth(), one.getHeight(), Bitmap.Config.ARGB_8888); Canvas c = new Canvas(oneCopy); Paint p = new Paint(); int color = ContextCompat.getColor(context, desiredColor); p.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN)); c.drawBitmap(one, 0, 0, p); // Create the disabled bitmap for the disabled state Bitmap disabled = BitmapFactory.decodeResource(context.getResources(), imageResource); Bitmap disabledCopy = Bitmap.createBitmap(disabled.getWidth(), disabled.getHeight(), Bitmap.Config.ARGB_8888); Canvas disabledCanvas = new Canvas(disabledCopy); Paint alphaPaint = new Paint(); alphaPaint.setAlpha(disableAlpha); disabledCanvas.drawBitmap(disabled, 0, 0, alphaPaint); StateListDrawable stateListDrawable = new StateListDrawable(); // Pressed State stateListDrawable.addState(new int[]{android.R.attr.state_pressed}, new BitmapDrawable(context.getResources(), oneCopy)); // Disabled State stateListDrawable.addState(new int[]{-android.R.attr.state_enabled}, new BitmapDrawable(context.getResources(), disabledCopy)); // - symbol means opposite, in this case "disabled" // Default State stateListDrawable.addState(new int[]{}, ContextCompat.getDrawable(context, imageResource)); return stateListDrawable; } }In Activity, it's convenient to use through call:
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { btnEnabled.setBackgroundDrawable(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.holo_red_light, 100)); btnDisabled.setBackgroundDrawable(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.darker_gray, 100)); } else { btnEnabled.setBackground(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.holo_red_light, 100)); btnDisabled.setBackground(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.darker_gray, 100)); }Note: I checked the sdk-version because setBackground() only available from API 16, so in the lower API device, use setBackgroundDrawable() instead.
Output when click Button:
Solution 2: Use DrawableCompat
In programmatically code, just through setTinList() and setTinMode(), states which define in selector will be active:
Drawable normalDrawable = ContextCompat.getDrawable(this, R.mipmap.logo); Drawable wrapDrawable = DrawableCompat.wrap(normalDrawable); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { DrawableCompat.setTintList(wrapDrawable, getResources().getColorStateList(R.color.drawable_selector, this.getTheme())); } else { DrawableCompat.setTintList(wrapDrawable, getResources().getColorStateList(R.color.drawable_selector)); } DrawableCompat.setTintMode(wrapDrawable, PorterDuff.Mode.SRC_IN); imageView.setImageDrawable(wrapDrawable);Output when click at this ImageView:
Full Project code
package info.devexchanges.statesdrawable; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.support.v4.content.ContextCompat; import android.support.v4.graphics.drawable.DrawableCompat; import android.support.v7.app.AppCompatActivity; import android.widget.Button; import android.widget.ImageView; public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btnEnabled = (Button) findViewById(R.id.btn_enabled); Button btnDisabled = (Button) findViewById(R.id.btn_disabled); ImageView imageView = (ImageView) findViewById(R.id.image); //Use DrawableUtils if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) { btnEnabled.setBackgroundDrawable(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.holo_red_light, 100)); btnDisabled.setBackgroundDrawable(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.darker_gray, 100)); } else { btnEnabled.setBackground(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.holo_red_light, 100)); btnDisabled.setBackground(DrawableUtils.getStateListDrawable(this, R.mipmap.bg_green, android.R.color.darker_gray, 100)); } //Use DrawableCompat Drawable normalDrawable = ContextCompat.getDrawable(this, R.mipmap.logo); Drawable wrapDrawable = DrawableCompat.wrap(normalDrawable); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { DrawableCompat.setTintList(wrapDrawable, getResources().getColorStateList(R.color.drawable_selector, this.getTheme())); } else { DrawableCompat.setTintList(wrapDrawable, getResources().getColorStateList(R.color.drawable_selector)); } DrawableCompat.setTintMode(wrapDrawable, PorterDuff.Mode.SRC_IN); imageView.setImageDrawable(wrapDrawable); } }And it's layout: