Android Custom View неправильно обрабатывает прозрачность / альфа

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

Это фрагмент моего кода, чтобы показать, как я использую альфа в моем NewButtonView.java

Paint paint = new Paint();
int color = 0x33ffffff;
int borderColor = 0xFF000000;

paint.setColor(color);
paint.setAntiAlias(true);
paint.setStrokeWidth(strokeWidth);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStyle(Paint.Style.FILL);

Примерно через 31 минуту в этом видео Google I/O... они показывают мой желаемый эффект.

Они в основном показывают это изображение:

Добавьте прозрачность и получите это изображение: НЕОБХОДИМЫЙ РЕЗУЛЬТАТ

Они в конечном итоге с этим: желаемый результат

У кого-нибудь есть идеи о том, как получить этот желаемый эффект?

2 ответа

Как упоминалось в видео, вы бы использовали Canvas#saveLayerAlpha(....) за это. Вы также можете получить аналогичный эффект, не используя его. Я буду обсуждать это позже.

Давайте создадим примерное представление:

public class SampleView extends View {

    // Declare Paint objects
    Paint paintColor, paintBorder;

    public SampleView(Context context) {
        super(context);

        // Initialize and set up Paint objects
        paintColor = new Paint();
        paintBorder = new Paint();

        paintColor.setAntiAlias(true);
        paintBorder.setAntiAlias(true);

        paintBorder.setColor(Color.BLACK);
        paintBorder.setStyle(Style.STROKE);
        paintBorder.setStrokeWidth(10);

        // Just a random image to 'see' the difference
        setBackground(getResources().getDrawable(R.drawable.hor_lines));
    }

    @Override 
    protected void onDraw(Canvas canvas) {

        // Save layer alpha for Rect that covers the view : alpha is 90 / 255
        canvas.saveLayerAlpha(0, 0, getWidth(), getHeight(), 90, 
                                             Canvas.HAS_ALPHA_LAYER_SAVE_FLAG);

        // Draw first circle, and then the border
        paintColor.setColor(Color.RED);
        canvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);

        canvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

        // Draw second circle, and then the border
        paintColor.setColor(Color.BLUE);
        canvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);
        canvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

        // Finally, restore the canvas
        canvas.restore();
    }
}

Что просходит:

  1. Вне экрана растровое изображение выделяется, когда saveLayerAlpha(....) называется.

  2. Все операции рисования происходят на этом растровом изображении.

  3. когда canvas.restore() Это растровое изображение передается на экранную основу, а альфа-значение, которое мы указали в saveLayerAlpha(....) применяется к закадровому растровому изображению.

(Я думаю) Ниже приведен эквивалентный способ создания этого эффекта без использования saveLayerAlpha(....):

public class SView extends View {

    Paint paintColor, paintBorder, paintAlpha;

    Bitmap toDrawOn;

    public SView(Context context) {
        super(context);

        paintAlpha = new Paint();

        paintAlpha.setColor(Color.parseColor("#90FFFFFF"));
        paintAlpha.setAntiAlias(true);

        ....
        ....

    }

    @Override
    protected void onDraw(Canvas canvas) {

        if (toDrawOn == null) {

            // Create a new Bitmap
            toDrawOn = Bitmap.createBitmap(getWidth(), getHeight(), 
                                                    Config.ARGB_8888);

            // Create a new Canvas; drawing operations 
            // will happen on 'toDrawOn'
            Canvas offScreen = new Canvas(toDrawOn);

            // First circle
            paintColor.setColor(Color.RED);
            offScreenCanvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);
            offScreenCanvas.drawCircle(getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

            // Second circle
            paintColor.setColor(Color.BLUE);
            offScreenCanvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 20, paintColor);
            offScreenCanvas.drawCircle(2 * getWidth() / 3, getHeight() / 2, 
                                           getWidth() / 4 - 15, paintBorder);

            // Draw bitmap 'toDrawOn' to canvas using 'paintAlpha'
            canvas.drawBitmap(toDrawOn, 0, 0, paintAlpha);

        } else {

            // 'toDrawOn' is not null; draw it
            canvas.drawBitmap(toDrawOn, 0, 0, paintAlpha);
        }
    }
}

Выход:

Просто для справки, базовый контейнер на изображении выше является LinearLayout с фоном, установленным на этот JPEG: Ссылка.

И, drawable используется в качестве фона SampleView:

// Just a random image to 'see' the difference
setBackground(getResources().getDrawable(R.drawable.hor_lines));

взято из: здесь.

Вы можете нарисовать все в растровом изображении с полной альфа, а затем изменить альфа растрового изображения

(Извините, это больше комментарий, чем ответ, но переполнение стека не позволит мне оставлять комментарии)

Другие вопросы по тегам