StateListColorFilteredDrawable
posted in Utils on February 23, 2016
StateListColorFilteredDrawable lets you assign a number of ColorFilters to a single Drawable and swap them according to states.
Usage
First you have to create the Drawable
you want to filter. Then wrap it in a StateListColorFilteredDrawable
and call addState to set the ColorFilter
for a specific state.
// Create a drawable directly
Drawable original = ...;
// Wrap the drawable
StateListColorFilteredDrawable colored = new StateListColorFilteredDrawable(original);
// Add states
colored.addState(new int[]{android.R.attr.state_pressed}, Color.RED, PorterDuff.Mode.MULTIPLY);
colored.addState(StateSet.WILD_CARD, Color.GREEN, PorterDuff.Mode.MULTIPLY);
// User the colored drawable
view.setBackgroundDrawable(colored);
If you inflated your view from an XML-File and want to modify the background you can also use the static injectInBackground()
method.
// Directly inject the StateListColorFilteredDrawable into the background of a view.
StateListColorFilteredDrawable colored = StateListColorFilteredDrawable.injectInBackground(view);
// Add states
colored.addState(new int[]{android.R.attr.state_pressed}, Color.RED, PorterDuff.Mode.MULTIPLY);
colored.addState(StateSet.WILD_CARD, Color.GREEN, PorterDuff.Mode.MULTIPLY);
// No need to call view.setBackgroundDrawable(colored);
The Class
/*
* Copyright 2016 Max Nagl
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package na.gl.util;
import android.graphics.ColorFilter;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffColorFilter;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.util.StateSet;
import android.view.View;
import java.util.ArrayList;
import java.util.List;
/**
* Lets you assign a number of ColorFilters to a single Drawable and swap them according
* to states.
*/
public class StateColorFilteredDrawable extends LayerDrawable {
private final List<ColorFilterState> states = new ArrayList<>();
private class ColorFilterState {
int[] state;
ColorFilter colorFilter;
}
/**
* Creates a new layer drawable with the list of specified layers that changes
* colorFilters according to states.
*
* @param layers a list of drawables to use as layers in this new drawable,
* must be non-null
*/
public StateColorFilteredDrawable(@NonNull Drawable[] layers) {
super(layers);
}
/**
* Creates a new layer drawable with the list of specified layers that changes
* colorFilters according to states.
*
* @param layer a drawable to use as only layers in this new drawable,
* must be non-null
*/
public StateColorFilteredDrawable(@NonNull Drawable layer) {
this(new Drawable[] {layer});
}
/**
* Wraps the background of a view with a StateColorFilteredDrawable.
* @param view The view with the background to wrap.
* @return The wrapped background.
*/
@SuppressWarnings("deprecation")
public static @Nullable
StateColorFilteredDrawable injectInBackground(@NonNull View view) {
Drawable original = view.getBackground();
if (original == null) return null;
StateColorFilteredDrawable colored = new StateColorFilteredDrawable(original);
view.setBackgroundDrawable(colored);
return colored;
}
/**
* Removes all states and ColorFilters.
*/
public void clearStates() {
states.clear();
}
/**
* Convenience for {@link #addState(int[], ColorFilter)} which constructs PorterDuffColorFilter.
*/
public void addState(int[] stateSet, int color, PorterDuff.Mode mode) {
addState(stateSet, new PorterDuffColorFilter(color, mode));
}
/**
* Add a ColorFilter for a specific state.
* @param stateSet - An array of resource Ids to associate with the image.
* Switch to this image by calling setState().
* @param colorFilter - The ColorFilter to apply in this state.
*/
public void addState(int[] stateSet, @Nullable ColorFilter colorFilter) {
ColorFilterState cfs = new ColorFilterState();
cfs.colorFilter = colorFilter;
cfs.state = stateSet;
states.add(cfs);
}
@Override
protected boolean onStateChange(int[] states) {
boolean stateFound = false;
for (ColorFilterState state : this.states) {
if (StateSet.stateSetMatches(state.state, states)) {
super.setColorFilter(state.colorFilter);
stateFound = true;
break;
}
}
if (!stateFound) {
super.setColorFilter(null);
}
super.onStateChange(states);
invalidateSelf();
return true;
}
@Override
public boolean isStateful() {
return true;
}
}