如何将自定义选择器颜色应用于自定义Button子类中的自定义drawable?

时间:2018-02-22 15:36:12

标签: android android-layout android-drawable android-styles android-selector

我创建了一个自定义Button子类,它执行一些自定义绘图并添加了一些其他功能。

现在我想添加在Button上显示自定义drawable / image /图标的可能性。添加图像并不是什么大问题,但是如何根据当前状态为不同颜色的图标着色?

我尝试使用ColorStateList使用当前状态颜色对drawable进行着色,但它不起作用:drawable始终使用自己的未更改颜色绘制。

我假设在将drawable转换为位图时不使用应用的滤色器。它是否正确?我该如何解决这个问题?

这是我的代码:

布局

<com.example.UI.MyButton
     ...
     mc:iconSrc="@drawable/someIcon"
     mc:iconTintColor="@drawable/button_selector_colors"/>

attrs.xml

<declare-styleable name="MyButton">
    <attr name="iconSrc" format="reference" />
    <attr name="iconTintColor" format="reference" />
</declare-styleable>  

button_selector_colors.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true"  android:color="@color/myPressed"/>
    <item android:state_selected="true" android:color="@color/mySelected"/>
    <item android:color="@color/myDefault"/>
</selector>

MyButton.java

public class MyButton extends Button {
    private Drawable mIconDrawable;
    private ColorStateList mIconTintColor;

    public MyButton(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        init(context, attrs);
    }

    public MyButton(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyButton);

        for (int i = 0; i < array.getIndexCount(); ++i) {
            int attr = array.getIndex(i);
            switch (attr) {
                case R.styleable.MyButton_iconSrc:
                    mIconDrawable = array.getDrawable(attr);
                    break;
                case R.styleable.MyButton_iconTintColor:
                    mIconTintColor = array.getColorStateList(attr);
                    break;
                default:
                    break;
            }
        }

        array.recycle();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        ...

        if (mIconDrawable != null) {
            ... // calc position, etc.

            if (mIconTintColor != null) {
                mIconDrawable.mutate();
                int[] stateSet = getDrawableState();
                int defaultColor = Color.BLACK; //mIconTintColor.getDefaultColor();

                int stateColor = mIconTintColor.getColorForState(stateSet, defaultColor);

                mIconDrawable.setColorFilter(stateColor, PorterDuff.Mode.SRC_ATOP);
            }

            Bitmap icon = drawableToBitmap(mIconDrawable);
            canvas.drawBitmap(icon, ...);
        }
    }

    public static Bitmap drawableToBitmap (Drawable drawable) {
        if (drawable instanceof BitmapDrawable) {
            return ((BitmapDrawable)drawable).getBitmap();
        }

        Bitmap bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
        drawable.draw(canvas);

        return bitmap;
    }
}

1 个答案:

答案 0 :(得分:1)

您看到的错误“恢复比保存更多”意味着您调用的Canvas.restore()次数超过Canvas.save()。每次保存都必须有匹配的恢复。

您不会在代码中显示保存和恢复,但请确保它们平衡所有执行路径。

下溢情况得到治愈。我认为你有尺码/贴装问题。不要将按钮的图标转换为位图,而是尝试以下操作:

mIconDrawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
mIconDrawable.draw(canvas);

您可能会在转换中陷入困境。如果你真的想创建位图,我不认为你需要的内在宽度和高度。请尝试以下方法:

public static Bitmap drawableToBitmap(Drawable drawable, int width, int height) {
    if (drawable instanceof BitmapDrawable) {
        return ((BitmapDrawable) drawable).getBitmap();
    }

    Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bitmap);
    drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
    drawable.draw(canvas);

    return bitmap;
}

然后按如下方式调用并绘制图标:

Bitmap icon = drawableToBitmap(mIconDrawable, canvas.getWidth(), canvas.getHeight());
canvas.drawBitmap(icon, 0, 0, null);