Как рассчитать цвет из RadialGradient - PullRequest
0 голосов
/ 28 июня 2018

Некоторое время назад я нашел эту замечательную палитру цветов от Петра Адамса, которую я больше не могу найти в Git, но она все еще на этой странице: https://www.programcreek.com/java-api-examples/index.php?source_dir=Random-Penis-master/app/src/main/java/com/osacky/penis/picker/ColorPicker.java

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

Проблема в том, что я не совсем понимаю, как это работает. Теперь я хочу создать RadialGradient с разными цветами. Но используемая логика не работает, когда я генерирую RadialGradient с другими цветами.

Вот код, который генерирует RadialGradient:

private Bitmap createColorWheelBitmap(int width, int height) {
    Bitmap bitmap = Bitmap.createBitmap(width, height, Config.ARGB_8888);

    int colorCount = 12;
    int colorAngleStep = 360 / 12;
    int colors[] = new int[colorCount + 1];
    float hsv[] = new float[]{0f, 1f, 1f};
    for (int i = 0; i < colors.length; i++) {
        hsv[0] = (i * colorAngleStep + 180) % 360;
        colors[i] = Color.HSVToColor(hsv);
    }
    colors[colorCount] = colors[0];

    SweepGradient sweepGradient = new SweepGradient(width / 2, height / 2, colors, null);
    RadialGradient radialGradient = new RadialGradient(width / 2, height / 2, colorWheelRadius, 0xFFFFFFFF, 0x00FFFFFF, TileMode.CLAMP);
    ComposeShader composeShader = new ComposeShader(sweepGradient, radialGradient, PorterDuff.Mode.SRC_OVER);

    colorWheelPaint.setShader(composeShader);

    Canvas canvas = new Canvas(bitmap);
    canvas.drawCircle(width / 2, height / 2, colorWheelRadius, colorWheelPaint);

    return bitmap;
}

Код для прослушивания изменений средства выбора, поэтому он рассчитывает цвет на основе позиции:

@Override
public boolean onTouchEvent(MotionEvent event) {
    int action = event.getAction();
    switch (action) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:
            int x = (int) event.getX();
            int y = (int) event.getY();
            int cx = x - getWidth() / 2;
            int cy = y - getHeight() / 2;
            double d = Math.sqrt(cx * cx + cy * cy);

            if (d <= colorWheelRadius) {
                colorHSV[0] = (float) (Math.toDegrees(Math.atan2(cy, cx)) + 180f);
                colorHSV[1] = Math.max(0f, Math.min(1f, (float) (d / colorWheelRadius)));
                selectedPointer.setColor(Color.HSVToColor(colorHSV));
                notifyListeners();
                invalidate();
            }

            return true;
        case MotionEvent.ACTION_BUTTON_PRESS:

    }

    return super.onTouchEvent(event);
}

Наконец, код, который вычисляет позицию на основе цвета:

 // drawing color wheel pointer 
    float hueAngle = (float) Math.toRadians(colorHSV[0]); 
    int colorPointX = (int) (-Math.cos(hueAngle) * colorHSV[1] * colorWheelRadius) + centerX; 
    int colorPointY = (int) (-Math.sin(hueAngle) * colorHSV[1] * colorWheelRadius) + centerY; 

    float pointerRadius = 0.075f * colorWheelRadius; 
    int pointerX = (int) (colorPointX - pointerRadius / 2); 
    int pointerY = (int) (colorPointY - pointerRadius / 2); 

    colorPointerCoords.set(pointerX, pointerY, pointerX + pointerRadius, pointerY + pointerRadius); 
    canvas.drawOval(colorPointerCoords, colorPointerPaint); 

Итак, мой вопрос: как я могу, например, изменить RadialGradient, чтобы он включал только 2 цвета, не нарушая вычисления для получения цвета? Даже объяснение того, как это работает, было бы замечательно!

1 Ответ

0 голосов
/ 02 августа 2018

Здесь есть отличное учебное пособие: http://tekeye.uk/android/examples/ui/android-color-picker-tutorial (не мое). Я не знаю много о теории, стоящей за этим, но вы можете использовать этот код для расчета цвета на основе позиции.

// Calculate channel based on 2 surrounding colors and p angle.
private int ave(int s, int d, float p) {
    return s + java.lang.Math.round(p * (d - s));
}

// Calculate color based on drawn colors and angle based on x and y position.
private int interpColor(int colors[], float unit) {
    if (unit <= 0) {
        return colors[0];
    }
    if (unit >= 1) {
        return colors[colors.length - 1];
    }

    // Adjust the angle (unit) based on how many colors there are in the list.
    float p = unit * (colors.length - 1);
    // Get starting color position in the array.
    int i = (int)p;
    p -= i;

    // Now p is just the fractional part [0...1) and i is the index.
    // Get two composite colors for calculations.
    int c0 = colors[i];
    int c1 = colors[i+1];
    // Calculate color channels.
    int a = ave(Color.alpha(c0), Color.alpha(c1), p);
    int r = ave(Color.red(c0), Color.red(c1), p);
    int g = ave(Color.green(c0), Color.green(c1), p);
    int b = ave(Color.blue(c0), Color.blue(c1), p);

    // And finally create the color from the channels.
    return Color.argb(a, r, g, b);
}

Например, вы можете вызвать такую ​​функцию интерпретации.

@Override
public boolean onTouchEvent(MotionEvent event) {
    float x = event.getX() - CENTER_X;
    float y = event.getY() - CENTER_Y; 

    switch (event.getAction()) {
        case MotionEvent.ACTION_DOWN:
        case MotionEvent.ACTION_MOVE:   
            // Calculate the angle based on x and y positions clicked.      
            float angle = (float)java.lang.Math.atan2(y, x);
            // need to turn angle [-PI ... PI] into unit [0....1]
            float unit = angle/(2*PI);
            if (unit < 0) {
                unit += 1;
            }
            // mColors is your list with colors so int[].
            int color = interpColor(mColors, unit);
        break;
    }
}

Я уже пробовал это в своем проекте, и он работает как шарм. Надеюсь, вам это тоже поможет. :)

EDIT:

О, так что мои цвета настроены так.

mColors = intArrayOf(-0x10000, -0xff01, -0xffff01, -0xff0001, -0xff0100, -0x100, -0x10000)

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

...