Ответ 1
Я бы использовал маски для ваших первых двух проблем.
1. Создайте плавный градиент
Первым шагом будет рисование двух прямоугольников с линейным градиентом. Первый прямоугольник содержит цвета синий и зеленый, а второй прямоугольник - зеленый и красный, как показано на следующем рисунке. Я отметил линию, в которой оба прямоугольника касаются друг друга черный, чтобы уточнить, что они представляют два разных прямоугольника.
Это можно сделать, используя следующий код (выдержка):
// Both color gradients
private Shader shader1 = new LinearGradient(0, 400, 0, 500, Color.rgb(59, 242, 174), Color.rgb(101, 172, 242), Shader.TileMode.CLAMP);
private Shader shader2 = new LinearGradient(0, 400, 0, 500, Color.rgb(59, 242, 174), Color.rgb(255, 31, 101), Shader.TileMode.CLAMP);
private Paint paint = new Paint();
// ...
@Override
protected void onDraw(Canvas canvas) {
float width = 800;
float height = 800;
float radius = width / 3;
// Arc Image
Bitmap.Config conf = Bitmap.Config.ARGB_8888; // See other config types
Bitmap mImage = Bitmap.createBitmap(800, 800, conf); // This creates a mutable bitmap
Canvas imageCanvas = new Canvas(mImage);
// Draw both rectangles
paint.setShader(shader1);
imageCanvas.drawRect(0, 0, 400, 800, paint);
paint.setShader(shader2);
imageCanvas.drawRect(400, 0, 800, 800, paint);
// /Arc Image
// Draw the rectangle image
canvas.save();
canvas.drawBitmap(mImage, 0, 0, null);
canvas.restore();
}
Поскольку ваша цель - иметь цветную дугу с закругленными крышками, нам нужно определить область оба прямоугольника, которые должны быть видны пользователю. Это означает, что большинство обоих прямоугольников будут скрыты и, следовательно, не видны. Вместо этого остается только область дуги.
Результат должен выглядеть так:
Для достижения необходимого поведения мы определяем маску, которая только показывает область дуги внутри
прямоугольники. Для этого мы интенсивно используем метод setXfermode
Paint
. Как аргумент
мы используем разные экземпляры PorterDuffXfermode
.
private Paint maskPaint;
private Paint imagePaint;
// ...
// To be called within all constructors
private void init() {
// I encourage you to research what this does in detail for a better understanding
maskPaint = new Paint();
maskPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
imagePaint = new Paint();
imagePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OVER));
}
@Override
protected void onDraw(Canvas canvas) {
// @step1
// Mask
Bitmap mMask = Bitmap.createBitmap(800, 800, conf);
Canvas maskCanvas = new Canvas(mMask);
paint.setColor(Color.WHITE);
paint.setShader(null);
paint.setStrokeWidth(70);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAntiAlias(true);
final RectF oval = new RectF();
center_x = 400;
center_y = 400;
oval.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
maskCanvas.drawArc(oval, 135, 270, false, paint);
// /Mask
canvas.save();
// This is new compared to step 1
canvas.drawBitmap(mMask, 0, 0, maskPaint);
canvas.drawBitmap(mImage, 0, 0, imagePaint); // Notice the imagePaint instead of null
canvas.restore();
}
2. Создайте оверлейный белый палец
Это решает вашу первую проблему. Второе может быть достигнуто с помощью маски снова, хотя это мы хотим достичь чего-то другого. Раньше мы хотели показать только определенную область (дугу) фонового изображения (являющегося двумя прямоугольниками). На этот раз мы хотим сделать обратное: Мы определяем фоновое изображение (большой палец) и маскируем его внутреннее содержимое, так что только кажется, что инсульт остается. При нанесении на изображение дуги большой палец накладывает цветную дугу на прозрачная область содержимого.
Итак, первым шагом будет рисование большого пальца. Для этого мы используем дугу с тем же радиусом, что и фоновая дуга, но разные углы, что приводит к значительно меньшей дуге. Но поскольку большой палец должен "окружать" фоновую дугу, ширина его хода должна быть больше, чем фоновая дуга.
@Override
protected void onDraw(Canvas canvas) {
// @step1
// @step2
// Thumb Image
mImage = Bitmap.createBitmap(800, 800, conf);
imageCanvas = new Canvas(mImage);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(120);
final RectF oval2 = new RectF();
center_x = 400;
center_y = 400;
oval2.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
imageCanvas.drawArc(oval2, 270, 45, false, paint);
// /Thumb Image
canvas.save();
canvas.drawBitmap(RotateBitmap(mImage, 90f), 0, 0, null);
canvas.restore();
}
public static Bitmap RotateBitmap(Bitmap source, float angle)
{
Matrix matrix = new Matrix();
matrix.postRotate(angle);
return Bitmap.createBitmap(source, 0, 0, source.getWidth(), source.getHeight(), matrix, true);
}
Результат кода показан ниже.
Итак, теперь, когда у нас есть большой палец, наложенный на фоновую дугу, нам нужно определить маску который удаляет внутреннюю часть большого пальца, так что фоновая дуга снова становится видимой.
Для этого мы в основном используем те же параметры, что и раньше, для создания другой дуги, но на этот раз ширина хода должна быть идентична ширине, используемой для фоновой дуги, так как это отмечает область, которую мы хотим удалить внутри большого пальца.
Используя следующий код, результирующее изображение показано на рисунке 4.
@Override
protected void onDraw(Canvas canvas) {
// @step1
// @step2
// Thumb Image
// ...
// /Thumb Image
// Thumb Mask
mMask = Bitmap.createBitmap(800, 800, conf);
maskCanvas = new Canvas(mMask);
paint.setColor(Color.WHITE);
paint.setStrokeWidth(70);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
final RectF oval3 = new RectF();
center_x = 400;
center_y = 400;
oval3.set(center_x - radius,
center_y - radius,
center_x + radius,
center_y + radius);
maskCanvas.drawBitmap(mImage, 0, 0, null);
maskCanvas.drawArc(oval3, 270, 45, false, paint);
// /Thumb Mask
canvas.save();
canvas.drawBitmap(RotateBitmap(mMask, 90f), 0, 0, null); // Notice mImage changed to mMask
canvas.restore();
}
3. Анимировать белый палец
Последняя часть вашего вопроса будет оживлять движение дуги. У меня нет твердого решение для этого, но, возможно, может помочь вам в полезном направлении. Я бы попробовал следующее:
Сначала определите большой палец как ImageView
, который является частью вашего всего диаграммы дуги. При изменении
выбранные значения вашего графика, вы поворачиваете изображение большого пальца вокруг центра фона
дуга. Поскольку мы хотим оживить движение, просто установив поворот большого пальца
не будет адекватным. Вместо этого мы используем следующий вид RotateAnimation
:
final RotateAnimation animRotate = new RotateAnimation(0.0f, -90.0f, // You have to replace these values with your calculated angles
RotateAnimation.RELATIVE_TO_SELF, // This may be a tricky part. You probably have to change this to RELATIVE_TO_PARENT
0.5f, // x pivot
RotateAnimation.RELATIVE_TO_SELF,
0.5f); // y pivot
animRotate.setDuration(1500);
animRotate.setFillAfter(true);
animSet.addAnimation(animRotate);
thumbView.startAnimation(animSet);
Это далеко не последнее, я думаю, но это очень хорошо поможет вам в поиске необходимого решение. Очень важно, чтобы ваши значения поворота указывали на центр вашего фоновая дуга, так как это то, что ваше изображение большого пальца должно вращаться вокруг.
Я тестировал свой (полный) код с API уровня 16 и 22, 23, поэтому я надеюсь, что этот ответ хотя бы дает вам новые идеи о том, как решить ваши проблемы.
Обратите внимание, что операции выделения в методе onDraw
являются плохой идеей и должны
избегать. Для простоты я не смог следовать этому совету. Также код должен использоваться как
руководство в правильном направлении, а не просто копировать и вставлять, поскольку оно делает тяжелым
использование магических чисел и, как правило, не соответствует хорошим стандартам кодирования.