Как нарисовать круг с радиальным градиентом в холсте?

Я создал кнопку круга, которая может изменить свой цвет при вызове функции. Я хочу создать еще один, который создает одну и ту же кнопку круга, но с радиальным градиентом, который начинается посередине с выбранным цветом и становится прозрачным, когда вы выходите из круга.

Я создал аналогичный код, используя тот, который был опубликован в разделе Как настроить стиль градиента для рисования объекта? Но не работает.

Код, который я пробовал, для этой породы:

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

Solutions Collecting From Web of "Как нарисовать круг с радиальным градиентом в холсте?"

Мы должны перенести это в окна ответов.

OP в основном получил его здесь, и на самом деле пересмотренный принцип OP блистателен.

Некоторые общие советы относительно первой попытки в вопросе:

1) В protected void onSizeChanged(int w, int h, int oldw, int oldh) :

  • width = w; Нет причин, по которым вы не можете вызвать getWidth() когда вам это нужно. Причина, по которой это целесообразно, заключается в том, что внутренняя ширина представления устанавливается довольно поздно после onMeasure . Следовательно, onDraw может быть в следующий раз, когда вы захотите получить самую последнюю версию, поэтому используйте getter там.
  • mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888); , Создание растрового изображения – это дорогостоящая операция с интенсивной памятью. Если вы не хотите писать растровое изображение в файл или отправлять его в BitmapDrawable для ImageView или что-то еще, вам не нужно это делать. Особенно с эффектами, нарисованными на пользовательском интерфейсе с graphics библиотекой Android.
  • mCanvas = new Canvas(mBitmap); А затем операцию рисования на новом холсте. Это никогда не нужно. И все же я видел это (не работает) во многих кодовых базах и попытках. Я думаю, что это ошибка старой записи переполнения стека, которая заставила людей делать это, чтобы они могли трансформировать холст на пользовательский вид, не делая рисунок на остальной части холста. Кстати, если вам это нужно, используйте .restore() и .save() . Если вы видите new Canvas , будьте подозрительными .

2) onDraw(...) :

  • Да , вам нужно избегать делать что-то в onDraw , например, создавать объекты или любую тяжелую обработку. Но вам все равно нужно делать вещи в onDraw вам нужно делать в onDraw !
  • Поэтому здесь вам просто нужно вызвать : canvas.drawCircle(float cx, float cy, float radius, Paint paint) с аргументами в соответствии с документами .
  • Это действительно не onDraw для onDraw . Если вы беспокоитесь о том, чтобы слишком много называть это , так как может быть, если вся ваша кнопка анимации по экрану, вам нужно использовать аппаратное ускорение, доступное в более поздних версиях API, как будет описано в статье « Оптимизация представления» ; Очень полезно читать, если вы используете множество пользовательских обращений.

3) Это надоедливый радиальный градиент . Следующий вопрос, который у вас был, – это то, что вы правильно создали свою краску в методе init чтобы создание объекта не было выполнено. Но тогда, вполне правильно, у вас будет IllegalArgumentException ed (я думаю), потому что на этом этапе getHeight() представления getHeight() 0. Вы пробовали передавать небольшие значения пикселей – это не сработает, если вы не узнаете какую-либо магию о размерах экрана ,

Это не ваша проблема, как раздражающий цикл просмотра, лежащий в основе шаблонов проектирования Android. Исправить это достаточно просто: просто используйте последующую часть процесса рисования вида после вызова onMeasure чтобы установить фильтр краски.

Но есть некоторые проблемы с получением этого права, а именно, что иногда, досадно, onDraw до того момента, когда вы ожидаете этого. В результате ваша краска будет нулевой, и вы не получите желаемого поведения.

Я нашел более надежное решение – просто сделать нахальную и озорную маленькую нулевую проверку в onDraw а затем только построить там объект рисования. Это не является строго оптимальным, но с учетом сложного способа, с помощью которого объекты 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 и height/2 arguments.

Удачи!