Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Creating StateListDrawables for multiple components of a custom ViewGroup from a common resource

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?

like image 411
Sound Conception Avatar asked Dec 16 '22 06:12

Sound Conception


2 Answers

you cannot share one instance of SLD with multiple Views, you need one SLD per View, sorry

like image 125
pskink Avatar answered Mar 08 '23 23:03

pskink


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);
like image 26
user Avatar answered Mar 09 '23 00:03

user