Using DrawableCompat class to apply a tintList

后端 未结 1 657
忘了有多久
忘了有多久 2021-01-13 22:47

Decided to try the new DrawableCompat class. Following instructions from a reliable source, I\'m calling:

Button b = (Button) findViewById(R.id.         


        
相关标签:
1条回答
  • 2021-01-13 23:29

    d = DrawableCompat.wrap(d); creates a new instance if it's not already DrawableWrapper so you tint this new instance but the original which is stored in the button remains the same.

    The whole code would look something like this

    Button b = (Button) findViewById(R.id.button);
    Drawable d = b.getBackground();
    d = DrawableCompat.wrap(d);
    DrawableCompat.setTintList(d, getResources().getColorStateList(...));
    b.setBackground(d); // or setBackgroundDrawable on older platforms
    

    So yeah, I'd go with the second approach you described because it abstracts the hard work from you.

    EDIT:

    Just took a dive into appcompat code and found out that the AppCompatButton tints iself and not the drawable unlike Lollipop native (but only if the background is on the whitelist, e.g. default appcompat button drawable). So you have to clear tint from the button itself first.

    Button b = (Button) findViewById(R.id.button);
    
    if (b instanceof AppCompatButton) {
        ((AppCompatButton)b).setSupportBackgroundTintList(null);
    }
    
    Drawable d = b.getBackground();
    d = DrawableCompat.wrap(d);
    DrawableCompat.setTintList(d, getResources().getColorStateList(...));
    b.setBackground(d); // or setBackgroundDrawable on older platforms
    

    EDIT 2:

    The above code will throw a NullPointerException when you try to reset the button's tint list. I'm currently filing a bug report.

    In the meantime I suggest you inflate the button with a custom background (non-whitelisted for tinting by appcompat) directly or with @null background and resolving the default button background by

    TypedArray ta = context.obtainStyledAttributes(null, new int[]{android.R.attr.background}, R.attr.buttonStyle, R.style.Widget_AppCompat_Button);
    Drawable d = ta.getDrawable(0);
    ta.recycle();
    

    Final solution

    So as all this looks pretty fugly, the easiest (and only working and foolproof, yet hidious as well) solution for you now is this:

    Button b = (Button) findViewById(R.id.button);
    ColorStateList c = getResources().getColorStateList(...);
    Drawable d = b.getBackground();
    if (b instanceof AppCompatButton) {
        // appcompat button replaces tint of its drawable background
        ((AppCompatButton)b).setSupportBackgroundTintList(c);
    } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
        // Lollipop button replaces tint of its drawable background
        // however it is not equal to d.setTintList(c)
        b.setBackgroundTintList(c);
    } else {
        // this should only happen if 
        // * manually creating a Button instead of AppCompatButton
        // * LayoutInflater did not translate a Button to AppCompatButton
        d = DrawableCompat.wrap(d);
        DrawableCompat.setTintList(d, c);
        b.setBackgroundDrawable(d);
    }
    

    You should put this monstrosity away in a utility class.

    0 讨论(0)
提交回复
热议问题