【问题标题】:Android Custom View doesn't handle transparency/alpha the right wayAndroid 自定义视图不能以正确的方式处理透明度/alpha
【发布时间】:2013-11-27 22:46:13
【问题描述】:

我正在绘制一个自定义视图。在这个视图中,我使用两个不同的绘画和路径对象来绘画到画布上。我基本上是在画两个重叠的形状。添加 alpha 后,重叠的视图部分比图像的其余部分更暗。这是不希望的,但我不知道如何解决它。

这是我的代码剪辑,以显示我如何在 NewButtonView.java 中使用 alpha

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

进入这个Google I/O video 大约 31 分钟...他们显示了我想要的效果。

他们基本上展示了这张图片:

添加透明度并获得此图像:不想要的结果

他们最终得到了这样的结果:期望的结果

有人知道如何获得这种想要的效果吗?

【问题讨论】:

  • 您确定要更改的是 alpha 版本吗?看起来你想要的效果仍然是零透明度,它只是变亮了。
  • 是的,我需要透明度,因为我使用的是必须看到的带纹理的背景。
  • 我最好的想法是看看是否有一种方法可以将重叠的形状绘制为一个形状(或在绘制后将它们组合为一个)。向复合形状添加 alpha 应该会产生您想要的结果
  • 是的,只是我画了两个圆圈,相互重叠,而我只能画两个圆圈时,似乎没有必要画那种“双眼形状”。
  • 我可能找错了树,但你不应该在容器上设置 alpha 而不是按钮吗?

标签: java android android-custom-view alpha


【解决方案1】:

如视频中所述,您可以为此使用 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(....) 中提供的alpha 值被应用到离屏位图上。

(我认为)以下是在不使用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:Link

并且,作为SampleView背景的drawable:

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

取自:here

【讨论】:

  • 您还可以创建具有 100% 不透明度的 cavanas 绘制图像,然后降低 cavanas 不透明度。
  • @MarcosEusebi 你将如何改变画布的透明度/不透明度?
  • 对不起,我不介意。将图像加入位图,然后将位图绘制到 cavnas 并设置位图的不透明度。
  • 请注意,文档建议对 View 使用硬件层而不是 saveLayer* 方法。
【解决方案2】:

您可以在位图中使用完整的 alpha 绘制所有内容,然后更改位图的 alpha

(对不起,这更像是一个评论而不是一个答案,但堆栈溢出不允许我发布 cmets)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2015-02-05
    • 1970-01-01
    • 2011-02-19
    • 2012-05-02
    • 2015-02-03
    • 1970-01-01
    • 1970-01-01
    • 2011-08-10
    相关资源
    最近更新 更多