以编程方式设置Android形状颜色

230

我正在编辑问题,希望这有助于准确回答。

假设我有以下椭圆形

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
    <solid android:angle="270"
           android:color="#FFFF0000"/>
    <stroke android:width="3dp"
            android:color="#FFAA0055"/>
</shape>

我该如何在一个活动类中编程设置颜色?


你将这个drawable设置为什么? - Vikram
可绘制对象是一个“椭圆形”,并且是ImageView的背景。 - Cote Mounyo
如果这个问题太难了,有没有一种方法可以在画布上绘制多个图像,并将分层的最终产品设置为视图的背景? - Cote Mounyo
您可以通过扩展View类并将其用作允许小部件重叠的布局中的base视图来实现此目标(RelativeLayoutFrameLayout)。在这个扩展的View类中,您可以在画布上绘制多个图像。但是,在此之前,请查看此链接--> Link(如果您还没有)。 - Vikram
19个回答

310
注意: 回答已更新,以涵盖backgroundColorDrawable实例的情况。感谢Tyler Pfaff指出这一点。

这个可绘制对象是椭圆形并且是 ImageView 的背景

使用getBackground()imageView获取Drawable

Drawable background = imageView.getBackground();

检查常见嫌疑人:

if (background instanceof ShapeDrawable) {
    // cast to 'ShapeDrawable'
    ShapeDrawable shapeDrawable = (ShapeDrawable) background;
    shapeDrawable.getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    // cast to 'GradientDrawable'
    GradientDrawable gradientDrawable = (GradientDrawable) background;
    gradientDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    // alpha value may need to be set again after this call
    ColorDrawable colorDrawable = (ColorDrawable) background;
    colorDrawable.setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

简洁版:

Drawable background = imageView.getBackground();
if (background instanceof ShapeDrawable) {
    ((ShapeDrawable)background).getPaint().setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof GradientDrawable) {
    ((GradientDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
} else if (background instanceof ColorDrawable) {
    ((ColorDrawable)background).setColor(ContextCompat.getColor(mContext,R.color.colorToSet));
}

请注意无需进行空值检查。

但是,如果绘制对象在其他地方使用,则在修改之前应该对其使用mutate()。 (默认情况下,从XML加载的可绘制对象共享相同的状态。)


4
谢谢回答。 (+1)。我的代码还有其他的bug,所以很难测试。但这可能会设置形状的“solid”部分。那么“stroke”部分呢? - Cote Mounyo
3
android.graphics.drawable.GradientDrawable 无法转换为 android.graphics.drawable.ShapeDrawable。 转换失败。 - John
3
如果你的ImageView的背景设置为GradientDrawable,那么getBackground()将不会返回ShapeDrawable。相反,使用返回的GradientDrawableGradientDrawable gradientDrawable = (GradientDrawable)imageView.getBackground();... gradientDrawable.setColors(new int[] { color1, color2 });来设置颜色。 - Vikram
1
@Vikram,我解决了上面描述的问题。你必须调用mutate()来避免为任何与你正在着色的那个视图共享drawable的视图着色。 :) - Tyler Pfaff
2
谢谢..拯救了我的世界。 - sid_09
显示剩余12条评论

78

现如今,更简单的解决方案是将您的形状用作背景,然后通过编程方式更改其颜色:

view.background.setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP)

查看PorterDuff.Mode以获取可用选项。

更新(API 29):

以上方法已自API 29起过时,替换为以下方法:

view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP)

请参见BlendMode,以查看可用选项。


7
正确的代码应该是:view.getBackground().setColorFilter(Color.parseColor("#343434"), PorterDuff.Mode.SRC_ATOP);,因为背景可能带有边框或圆角。 - Berkay Turancı
1
不错的 @BerkayTurancı,我的形状确实有圆角。我可以省略 getBackground() 调用。我的 imageview.src 包含了一个形状,我使用了:imageIndicator.setColorFilter(toggleColor, PorterDuff.Mode.SRC_ATOP); 其中 toggleColor 只是一个之前从 getColor() 中获取结果的整数。 - Someone Somewhere
2
BlendModeColorFilter中使用PorterDuff.Mode将无法编译,因为它需要BlendMode。因此,在API 29中,应该这样写:view.background.colorFilter = BlendModeColorFilter(Color.parseColor("#343434"), BlendMode.SRC_ATOP) - Onik
很好的发现@Onik。我已经相应地更新了答案。谢谢! - Georgios
@Onik但BlendMode.SRC_ATOP无法保留描边! - Shikhar

51

这样做:

    ImageView imgIcon = findViewById(R.id.imgIcon);
    GradientDrawable backgroundGradient = (GradientDrawable)imgIcon.getBackground();
    backgroundGradient.setColor(getResources().getColor(R.color.yellow));

1
@user3111850 你在调用 getBackground() 之前有在你的xml中添加了 android:background 或在activity中添加了 setBackground 吗?如果你已经这样做了,它应该可以工作。 - Lee Yi Hong
运行得非常顺利! - Taslim Oseni

26

虽然这个问题一段时间之前已经得到回答,但是可以通过将其重写为Kotlin扩展函数来现代化。

fun Drawable.overrideColor(@ColorInt colorInt: Int) {
    when (this) {
        is GradientDrawable -> setColor(colorInt)
        is ShapeDrawable -> paint.color = colorInt
        is ColorDrawable -> color = colorInt
    }
}

1
非常好的回答!谢谢你! - Eugene

16

试一试:

 public void setGradientColors(int bottomColor, int topColor) {
 GradientDrawable gradient = new GradientDrawable(Orientation.BOTTOM_TOP, new int[]  
 {bottomColor, topColor});
 gradient.setShape(GradientDrawable.RECTANGLE);
 gradient.setCornerRadius(10.f);
 this.setBackgroundDrawable(gradient);
 }

了解更多详情,请查看此链接

希望能有所帮助。


给这个链接点赞。但这不是我的问题的答案。 - Cote Mounyo

15

希望这能帮助到有同样问题的人

GradientDrawable gd = (GradientDrawable) YourImageView.getBackground();
//To shange the solid color
gd.setColor(yourColor)

//To change the stroke color
int width_px = (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, youStrokeWidth, getResources().getDisplayMetrics());
gd.setStroke(width_px, yourColor);

1
起初我无法让它正常工作,后来我发现必须像这样提供 yourColorgd.setStroke(width_px, Color.parseColor("#FF5722")); - pwnsauce

12

Vikram的回答基础上,如果你要给动态视图(如recycler视图项等)着色......那么在设置颜色之前,你可能需要调用mutate()。如果不这样做,所有具有公共drawable(例如背景)的视图也将更改颜色。

public static void setBackgroundColorAndRetainShape(final int color, final Drawable background) {

    if (background instanceof ShapeDrawable) {
        ((ShapeDrawable) background.mutate()).getPaint().setColor(color);
    } else if (background instanceof GradientDrawable) {
        ((GradientDrawable) background.mutate()).setColor(color);
    } else if (background instanceof ColorDrawable) {
        ((ColorDrawable) background.mutate()).setColor(color);
    }else{
        Log.w(TAG,"Not a valid background type");
    }

}

3
需要进行额外的检查和参数设置:如果背景是一个LayerDrawable实例,那么需要获取其中给定索引位置的Drawable。如果背景是一个ShapeDrawable实例,[...],以处理使用<layer-list ... <item ...的背景布局。 - Johny

7

这是对我有效的解决方案......我也在另一个问题中写了它: 如何动态更改形状颜色?

//get the image button by id
ImageButton myImg = (ImageButton) findViewById(R.id.some_id);

//get drawable from image button
GradientDrawable drawable = (GradientDrawable) myImg.getDrawable();

//set color as integer
//can use Color.parseColor(color) if color is a string
drawable.setColor(color)

7

除了在形状Drawable上设置着色,其他都对我无效。

 Drawable background = imageView.getBackground();
 background.setTint(getRandomColor())

要求Android 5.0 API 21或以上版本


1
优雅的解决方案。简单而且完美无缺。 - Vijay E
好的,简短的解决方案! - matdev

3

以下是基于上述答案的Kotlin扩展函数版本,带有Compat

fun Drawable.overrideColor_Ext(context: Context, colorInt: Int) {
    val muted = this.mutate()
    when (muted) {
        is GradientDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        is ShapeDrawable -> muted.paint.setColor(ContextCompat.getColor(context, colorInt))
        is ColorDrawable -> muted.setColor(ContextCompat.getColor(context, colorInt))
        else -> Log.d("Tag", "Not a valid background type")
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接