我创建了一个圆形按钮,可以在调用函数时更改颜色。我想要的是创建另一个,创建相同的圆形按钮,但径向渐变从中间开始选择颜色,当你离开圆圈时,它会变为透明。
我使用How to set gradient style to paint object?发布的代码创建了一个类似的代码但是没有用。
我试过的代码就是这个例子:
mPaint.setShader(new RadialGradient(0, 0, height/3, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.MIRROR));
以下类是我为圆形按钮创建的类。
public class ColorGradientCircleButton extends View{
private Paint mPaint;
private Paint mBitmapPaint;
private Bitmap mBitmap;
private Canvas mCanvas;
private int width, height;
public ColorGradientCircleButton(Context context) {
super(context);
init();
}
public ColorGradientCircleButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public ColorGradientCircleButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mBitmapPaint = new Paint(Paint.DITHER_FLAG);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
width = w;
height = h;
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
mCanvas = new Canvas(mBitmap);
mCanvas.drawCircle(w/2, h/2, h/3, mPaint);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawBitmap(mBitmap, 0, 0, mBitmapPaint);
}
public void changeColor(int color){
mPaint.setColor(color);
mCanvas.drawCircle(width/2, height/2, height/3, mPaint);
invalidate();
}
}
答案 0 :(得分:20)
我们应该将其迁移到答案框。
OP已经基本上得到了它 - 事实上OP的修订版gist非常出色。关于问题中第一次尝试的一些一般性提示:
1)在protected void onSizeChanged(int w, int h, int oldw, int oldh)
:
width = w;
没有理由要求您在需要时拨打getWidth()
。之所以这样做是因为View
的内部宽度设置在onMeasure
之后很晚。因此,onDraw
可能是您下次想要最新版本的时候,因此请在那里使用getter。mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
。创建位图是一项昂贵且占用大量内存的操作。除非您想将位图写入文件,或将其发送到BitmapDrawable
或ImageView
或其他内容,否则您无需执行此操作。特别是使用android的graphics
库将效果绘制到UI上。mCanvas = new Canvas(mBitmap);
然后在新画布上进行绘制操作。这是永远不需要的。然而,我在许多代码库和尝试中都看到过它(不起作用)。我认为这是一个旧的堆栈溢出帖子的错,让人们这样做,以便他们可以在自定义视图上转换画布,而不会影响画布的其余部分。顺便提一下,如果您需要,请改用.restore()
和.save()
。如果您看到new Canvas
,可疑。 2)onDraw(...)
:
onDraw
中执行操作,例如创建对象或任何繁重的处理。 但是你仍然需要在onDraw
onDraw
中完成所需的事情! canvas.drawCircle(float cx, float cy, float radius, Paint paint)
。onDraw
来说,这真的不是那么有罪。 如果您担心过多地调用,如果您的整个按钮在屏幕上设置了动画效果,则需要使用以后的API版本中提供的hardware acceleration,详见一篇名为Optimizing the View的文章;如果您使用大量自定义绘制的视图,则非常有用。 3)那令人讨厌的径向渐变。你遇到的下一个问题是你在init
方法中正确地创建了你的绘画,以便创建对象。但是非常正确的是它会IllegalArgumentException
编辑(我认为)在你身上,因为在那个阶段,视图的getHeight()
是0.你尝试传递小的像素值 - 除非你了解屏幕尺寸的一些魔力。
这不是你的问题,而是Android设计模式核心的烦人视图周期。虽然修复很简单:只需在onMeasure
调用之后使用视图绘制过程的后续部分来设置绘制过滤器。
但是有一些问题需要做到这一点,即有时候,令人烦恼的是,onDraw
会在您预期的时间点之前被调用。结果将是您的paint为null并且您将无法获得所需的行为。
我发现一个更强大的解决方案就是在onDraw
中进行一次厚颜无耻的淘气小检查,然后只需一次在那里构建paint对象。它并不是严格来说是最优的,但考虑到Paint
对象与Android的图形原生层相结合的复杂方式,而不是试图跨越许多频繁调用的地方的绘画配置和构造。它使得代码更加清晰。
这看起来像是(修改你的要点):
@Override
protected void onDraw(final Canvas canvas) {
super.onDraw(canvas);
if (mPaint == null) {
mPaint = new Paint();
mPaint.setColor(Color.BLACK);
mPaint.setStrokeWidth(1);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mPaint.setShader(new RadialGradient(getWidth() / 2, getHeight() / 2,
getHeight() / 3, Color.TRANSPARENT, Color.BLACK, TileMode.MIRROR));
}
width = getWidth();
height = getHeight();
canvas.drawCircle(width / 2, height / 2, height / 3, mPaint);
}
所以请注意一些变化 - 我想从你的描述中你想要在参数中交换两种颜色,也不要忘记在你的视图中将渐变的中心居中:width/2
和{{1参数。
祝你好运!