ilomambo
ilomambo

Reputation: 8350

custom attribute on custom Button does not show

I extended the Button widget to be able to apply several custom attributes.
One of the attributes is a color filter I try to apply to its background when the button is created. It does not work. (See the screen shots and code below)
I tried to directly set the background color, on the same code place, and it does change the background color, but it is not what I need, since I am using my own button PNGs.

there are 2 problems so far:

  1. The color filter is not applied
  2. The custom button is offset, clipped and it is not clickable

The second button uses the normal button, and it is positioned as expected and it is clickable. The second screenshot shows that the correct color is indeed chosen, and that it is possible to change the button background color at that point in the code.

Code:

public class MyButton extends Button {
    private int backGroundColor;

    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        TypedArray a = context.getTheme().obtainStyledAttributes(
            attrs, 
            R.styleable.MyButton, 
            defStyle, 
            0);

        try {
            Resources res = getResources();
            switch( a.getInteger(R.styleable.MyButton_type, 0) ) {
            case 0: 
                backGroundColor = res.getColor(R.color.Black); break;
            case 1: 
                backGroundColor = res.getColor(R.color.Red); break;
            case 2: 
                backGroundColor = res.getColor(R.color.DimGray); break;
            }
            getBackground().setColorFilter(backGroundColor, Mode.MULTIPLY);
            //setBackgroundColor(backGroundColor)
        } finally {
            a.recycle();
        }
    }
    public MyButton(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }
    public MyButton(Context context) {
        this(context, null, 0);
    }
}

The XML I used:

<LinearLayout 
    android:layout_width="match_parent"
    android:layout_height="wrap_content">
    <com.example.test.MyButton
        android:id="@+id/btn1"
        android:text="BTN1"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="14sp"
        android:textColor="@color/Blue"
        android:padding="2dp"
        android:layout_margin="4dp"
        android:background="@drawable/key_selector"
        app:type="RedButton"/>
    <Button
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:textSize="14sp"
        android:textColor="@color/Blue"
        android:padding="2dp"
        android:layout_margin="4dp"
        android:background="@drawable/key_selector"
        android:id="@+id/btn2"
        android:text="BTN2"/>
</LinearLayout>

Screenshot of setColorFilter() outcome enter image description here

Screenshot of setBackgroundColor() outcome enter image description here

EDIT This is the selector XML I use for the normal and pressed states.

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >

    <item
        android:drawable="@drawable/key1_pressed"
        android:state_pressed="true"/>

    <item 
        android:drawable="@drawable/key1"/>


</selector>

Upvotes: 0

Views: 1027

Answers (1)

user
user

Reputation: 87064

The custom button is offset, clipped and it is not clickable

That's due to your use of the constructors. The Button class does chain between its constructors but it doesn't pass 0 to the last constructor as the style(from the second constructor which is the one used), it passes an internal style(what makes a visual Button in the end). If you were to pass:

this(context, attrs, android.R.attr.buttonStyle);

the Button should be ok.

The color filter is not applied

The code for setting the color filter should work with no problems after you make the correction above. When you'll set the filter you'll see that both buttons will have the filter applied to them(as they have the same bitmap(I'm assuming you use an image)). This is happening because the drawables of the same type share a constant state. You can read more from Romain Guy's explanation here:

getBackground().mutate().setColorFilter(backGroundColor, Mode.MULTIPLY);

Let me know if this solves the problem(from what I understood):

public static class MyButton extends Button {

    private int backGroundColor;
    private StateListDrawable mSld;
    private PorterDuffColorFilter mColorFilter;
    private boolean mHandled = false;

    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        // ...
        try {
            //...
            mSld = (StateListDrawable) getBackground();
            mColorFilter = new PorterDuffColorFilter(backGroundColor,
                    Mode.MULTIPLY);
            mSld.setColorFilter(mColorFilter);
        } finally {
            a.recycle();
        }
    }

    @Override
    protected void onDraw(Canvas canvas) {
        if (!mHandled) {
            final Drawable current = mSld.getCurrent();
            current.mutate();
            current.setColorFilter(mColorFilter);
            mHandled = true;
        }
        super.onDraw(canvas);
    }

}

Upvotes: 1

Related Questions