如何在画布中绘制具有径向渐变的圆?

Gab*_*ban 14 android android-canvas android-view

我创建了一个圆形按钮,可以在调用函数时更改颜色.我想要的是创建另一个,创建相同的圆形按钮,但径向渐变从中间开始选择颜色,当你离开圆圈时,它会变为透明.

我使用在如何设置渐变样式来绘制对象时发布了一个类似的代码但是没有用.

我尝试的代码是这个porpuse是:

mPaint.setShader(new RadialGradient(0, 0, height/3, Color.BLACK, Color.TRANSPARENT, Shader.TileMode.MIRROR));
Run Code Online (Sandbox Code Playgroud)

以下类是我为圆形按钮创建的类.

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();
}
}
Run Code Online (Sandbox Code Playgroud)

Tom*_*Tom 20

我们应该将其迁移到答案框.

OP基本上已经在这里 - 实际上OP的修改 要点 非常出色.

关于问题中第一次尝试的一些一般提示:

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版本中提供的硬件加速,这将在一篇名为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);
        }
Run Code Online (Sandbox Code Playgroud)

所以请注意一些变化 - 我想从你的描述中你想要在参数中交换两种颜色,也不要忘记在你的视图中将渐变的中心居中:width/2height/2参数.

祝你好运!