Как нарисовать круг с радиальным градиентом на холсте?
Я создал кнопку круга, которая может изменить его цвет при вызове функции. Я хочу создать еще один, который создает ту же кнопку круга, но с радиальным градиентом, который начинается посередине с выбранным цветом и который становится прозрачным, когда вы выходите из круга.
Я создал аналогичный код, используя тот, который размещен в Как установить стиль градиента для рисования объекта? но не работал.
код, который я пробовал, к этому 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();
}
}
1 ответов
мы должны перенести это в таблицу.
OP в основном получил его здесь - и на самом деле op пересмотрен суть блестящая.
некоторые общие советы относительно первой попытки в вопрос:
1) в protected void onSizeChanged(int w, int h, int oldw, int oldh)
:
-
width = w;
нет причин, почему вы не можете позвонитьgetWidth()
когда вы требуете этого. Почему желательно, потому чтоView
внутренняя ширина установлена довольно поздно послеonMeasure
. Следовательно,onDraw
может быть в следующий раз, когда вы хотите самую свежую версию, так использовать геттер есть. -
mBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
. Создание растрового изображения является дорогостоящей и интенсивной операцией памяти. Если вы не хотите записать растровое изображение в файл или отправить его вBitmapDrawable
наImageView
или что-то еще, вам не нужно это делать. Особенно с эффектами, нарисованными на пользовательском интерфейсе с androidgraphics
библиотека. -
mCanvas = new Canvas(mBitmap);
следует рисовать на новом холсте. Этот никогда не нужен. И все же я видел это (не работает) во многих кодовых базах и попытках. Я!--46-->думаю это вина Старого сообщения переполнения стека, которое заставило людей делать это, чтобы они могли преобразовать холст на пользовательском представлении, не влияя на рисунок на остальную часть холста. Кстати, если вам это нужно, используйте.restore()
и.save()
вместо. Если вы видитеnew Canvas
, подозрительно.
2) onDraw(...)
:
-
да, вы должны избегать делать вещи в
onDraw
, например, создание объектов или любая тяжелая обработка. но вы все еще должны делать вещи вonDraw
вам нужно сделать вonDraw
! -
так что здесь вам просто нужно позвонить :
canvas.drawCircle(float cx, float cy, float radius, Paint paint)
с аргументами согласно docs. - это действительно не так грешно
onDraw
. если вы беспокоитесь о вызове этого тоже много, как может быть, если вся ваша кнопка анимируется по экрану, вам нужно использовать аппаратное ускорение доступно в более поздних версиях API, как будет подробно описано в статье под названием оптимизация вида; очень полезное чтение, Если вы используете много пользовательских нарисованных видов.
3) этот надоедливый радиальный градиент. Следующая проблема у вас была в том, что вы совершенно правильно создали свою краску в init
метод так, что создание объекта было снято с розыгрыша. Но тогда совершенно справедливо он будет иметь IllegalArgumentException
Эд (я думаю) на тебя, потому что на этом этапе 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
аргументов.
удачи!