18

我创建了一个圆形按钮,当我调用一个函数时可以改变他的颜色。我想要创建另一个,它创建相同的圆形按钮,但具有从中间开始并选择颜色的径向渐变,并且当您离开圆圈时变为透明。

我使用如何设置渐变样式以绘制对象?但没有奏效。

我试过的代码是这个porpuse:

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();
}
}
4

1 回答 1

21

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

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);. 创建位图是一项昂贵且占用大量内存的操作。除非您想将位图写入文件,或将其发送到 aBitmapDrawableImageView其他东西,否则您不需要这样做。graphics尤其是使用 android 的库在 UI 上绘制的效果。
  • mCanvas = new Canvas(mBitmap);然后在新画布上进行绘制操作。这是永远不需要的。然而我已经在许多代码库和尝试中看到它(不起作用)。我认为这是一个旧的堆栈溢出帖子的错,它让人们这样做,以便他们可以在自定义视图上转换画布而不影响画布其余部分的绘图。顺便说一句,如果您需要这个,请使用.restore()and.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在您期望的点之前被调用。结果将是您的绘画为空,并且您不会获得所需的行为。

我发现一个更强大的解决方案是简单地做一个厚脸皮和顽皮的小空检查,onDraw然后在那里构造油漆对象。严格来说,这并不是最优的,但考虑到对象与 Android 的图形原生层连接的复杂方式,Paint比试图跨越许多经常调用的地方的绘制配置和构造要好。它使代码更清晰。

这看起来像(修改你的要点):

        @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/2height/2参数。

祝你好运!

于 2013-10-17T16:49:03.653 回答