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:


