I have created a class which extends ViewGroup. One of the functions of this MyCustomViewGroup class is acting as a container for a nested class MyButton which extends Button.
I setup the custom properties for MyCustomViewGroup from a custom AttributeSet in the normal manner. One of the attributes defines the StateListDrawable to use for the background of instances of the MyButton nested class. I store this in a Class variable mMyButtonBackground.
public class MyCustomViewGroup extends ViewGroup {
private Drawable mMyButtonBackground;
...
Each time I create a new instance of MyButton in MyCustomViewGroup I set it's background.
MyButton myButton = new MyButton(context);
myButton.setBackground(mMyButtonBackground);
At run time, the StateListDrawable only seems to be working for the most recently added instance of MyButton.
For example say I create 4 instances of MyButton in MyCustomViewGroup. If I click on MyButton number 4, it's background changes as defined in the StateListDrawable. If I click on MyButton 1 to 3, their backgrounds do not change but MyButton number 4's does.
Logically this would suggest it is a mutability problem. All the MyButton instances are sharing the same StateListDrawable stored in mMyButtonBackground. Considering this, I've tried:
MyButton myButton = new MyButton(context);
Drawable myButtonBackground = mMyButtonBackground.mutate();
myButton.setBackground(myButtonBackground);
This did not solve the issue though. I've also tried specifically casting it as a StateListDrawable:
MyButton myButton = new MyButton(context);
StateListDrawable myButtonBackground = (StateListDrawable)mMyButtonBackground.mutate();
myButton.setBackground(myButtonBackground);
This also did not solve the problem. In my research trying to solve this problem I have have read this article by Romain Guy on Drawable mutations. I would have thought that since a StateListDrawable is a subclass of Drawable, I should be able to apply the same approach, but I can't seem to get it working. What am I missing?
Following pskink answer, the problem is that you use the same Drawable
instance. When you set the Drawable
as the background, the View
will register itself as a listener for that Drawable
to receive events(to take in consideration the new stat of a Drawable
, also requiring a redraw of the View
). So, your single instance StateListDrawable
will always have as a callback the last View
for which is set as background. That's why it works for the last Button
, but it also redraws the same Button
when you act upon the other Buttons
as the Drawable
triggers a invalidate on its callback View
.
You could simply avoid this by creating a new StateListDrawable
for each Button
. In the wrapper container you could just pass an attribute with a String
representing the name of the StateListDrawable
to use as the background, store it and use it when creating new Buttons
:
String mBtnDrawable = ""; //initialize in the constructor
// creating new Buttons
MyButton myButton = new MyButton(context);
int drawableId = getContext().getResources().getIdentifier(mBtnDrawable, "drawable", getContext().getPackageName());
StateListDrawable bck = (StateListDrawable) getContext().getResources().getDrawable(drawableId); // maybe also mutate it?
myButton.setBackground(bck);