绘制图像时绘制外部阴影

时间:2013-07-22 09:00:29

标签: android android-canvas

我目前通过绘制画布在我的应用中创建了一个圆形版本的图像。我想在图像周围画一个微弱的outershadow,但我不能完全正确。我有两个问题: 1.如何绘制外部阴影(我似乎只能绘制带有x或y偏移的阴影) 2.如何绘制阴影,使其没有附加图像中显示的工件。代码:

![public Bitmap getRoundedCornerBitmap(Bitmap bitmap, float cornerRadius) {
        Bitmap output = Bitmap.createBitmap(bitmap.getWidth()+6, bitmap.getHeight() +6, Config.ARGB_8888);
        Canvas canvas = new Canvas(output);

        final int color = 0xff424242;
        int shadowRadius = getDipsFromPixel(3);
        final Rect imageRect = new Rect(shadowRadius, shadowRadius, bitmap.getWidth(), bitmap.getHeight());
        final RectF rectF = new RectF(imageRect);

        // This does not achieve the desired effect
        Paint shadowPaint = new Paint();
        shadowPaint.setAntiAlias(true);
        shadowPaint.setColor(Color.BLACK);
        shadowPaint.setShadowLayer((float)shadowRadius, 2.0f, 2.0f,Color.BLACK);
        canvas.drawOval(rectF, shadowPaint);

        canvas.drawARGB(0, 0, 0, 0);
        final Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setColor(color);

        canvas.drawRoundRect(rectF, cornerRadius, cornerRadius, paint);

        paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
        canvas.drawBitmap(bitmap, imageRect, imageRect, paint);

        return output;
    }][1]

http://i.stack.imgur.com/d7DV6.png

这是我想要实现的效果的一个例子: enter image description here

5 个答案:

答案 0 :(得分:29)

我想要类似的效果,但是在AppWidget上,很遗憾我无法使用@ EvelioTarazona的解决方案。这就是我提出的,它应该适用于任何形状的位图。

    final Bitmap src = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
    final Bitmap shadow = addShadow(src, src.getHeight(), src.getWidth(), Color.BLACK, 3, 1, 3);
    final ImageView iv = (ImageView)findViewById(R.id.image);
    iv.setImageBitmap(shadow);

Example with parameters size=3, dx=1, dy=3, color=BLACK

 public Bitmap addShadow(final Bitmap bm, final int dstHeight, final int dstWidth, int color, int size, float dx, float dy) {
    final Bitmap mask = Bitmap.createBitmap(dstWidth, dstHeight, Config.ALPHA_8);

    final Matrix scaleToFit = new Matrix();
    final RectF src = new RectF(0, 0, bm.getWidth(), bm.getHeight());
    final RectF dst = new RectF(0, 0, dstWidth - dx, dstHeight - dy);
    scaleToFit.setRectToRect(src, dst, ScaleToFit.CENTER);

    final Matrix dropShadow = new Matrix(scaleToFit);
    dropShadow.postTranslate(dx, dy);

    final Canvas maskCanvas = new Canvas(mask);
    final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    maskCanvas.drawBitmap(bm, scaleToFit, paint);
    paint.setXfermode(new PorterDuffXfermode(Mode.SRC_OUT));
    maskCanvas.drawBitmap(bm, dropShadow, paint);

    final BlurMaskFilter filter = new BlurMaskFilter(size, Blur.NORMAL);
    paint.reset();
    paint.setAntiAlias(true);
    paint.setColor(color);
    paint.setMaskFilter(filter);
    paint.setFilterBitmap(true);

    final Bitmap ret = Bitmap.createBitmap(dstWidth, dstHeight, Config.ARGB_8888);
    final Canvas retCanvas = new Canvas(ret);
    retCanvas.drawBitmap(mask, 0,  0, paint);
    retCanvas.drawBitmap(bm, scaleToFit, null);
    mask.recycle();
    return ret;
}

答案 1 :(得分:14)

我们走了

http://i.imgur.com/cKi1ckX.png 是的,我仍在挖掘Nexus S

首先,请停止以这种方式屏蔽位图,您可以在不分配其他Bitmap,结帐this blog post about how to draw rounded (and actually any shape) images的情况下完成此操作。

第二次使用Drawable你可能会弄清楚如何添加你的影子,只要确保它不会被剪裁,在18岁以上你可以使用ViewOverlay来做到这一点,同时请记住there are several unsupported drawing operations for hardware accelerated layers,包括setShadowLayerBlurMaskFilter,如果效果不是您的问题,您可以一如既往地停用它:

if (SDK_INT >= HONEYCOMB) {
  view.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
}

并使用setShadowLayer,因为您已尝试过:

somePaint.setShadowLayer(shadowSize, deltaX, deltaY, shadowColor);

如需样本,请查看最后的链接。

如果您仍然希望硬件加速,则必须假装它有过度绘制的风险,您可以使用径向渐变或绘制另一个椭圆模糊它(如前所述,不能使用BlurMaskFilter)或使用预先模糊的Bitmap(更多遮蔽)。

对于这样一个微妙的阴影,如果需要表现,我宁愿单调,the full sauce is in the banana stand

更新:Starting L you can use real shadows

答案 2 :(得分:2)

在drawable文件夹中创建一个xml名称round_shape.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="oval">

    <stroke
        android:width="1dp"
        android:color="#bebebe" />
</shape>

将此round_shape设置为图像视图的背景,如下所示

<ImageView
     android:id="@+id/img_profile"
     android:layout_width="120dp"
     android:layout_height="120dp"
     android:padding="2dp"
     android:scaleType="fitXY"
     android:src="@drawable/camera" />

上面的代码在您对位图进行舍入后在图像视图周围创建一个薄层,下面的函数将执行此操作

public Bitmap roundBit(Bitmap bm) {

        Bitmap circleBitmap = Bitmap.createBitmap(bm.getWidth(),
                bm.getHeight(), Bitmap.Config.ARGB_8888);

        BitmapShader shader = new BitmapShader(bm, TileMode.CLAMP,
                TileMode.CLAMP);
        Paint paint = new Paint();
        paint.setShader(shader);
        paint.setAntiAlias(true);
        Canvas c = new Canvas(circleBitmap);
        c.drawCircle(bm.getWidth() / 2, bm.getHeight() / 2, bm.getWidth() / 2,
                paint);

        return circleBitmap;
}

答案 3 :(得分:2)

enter image description here

           ImageView imageView=findViewById(R.id.iv);
                    Bitmap icon = BitmapFactory.decodeResource(getResources(), R.drawable.images);
                    imageView.setImageBitmap(doHighlightImage(icon));   


           public static Bitmap doHighlightImage(Bitmap src) {
                    Bitmap bmOut = Bitmap.createBitmap(src.getWidth() + 96, src.getHeight() + 96, Bitmap.Config.ARGB_8888);
                    Canvas canvas = new Canvas(bmOut);
                    canvas.drawColor(0, Mode.CLEAR);
                    Paint ptBlur = new Paint();
                    ptBlur.setMaskFilter(new BlurMaskFilter(15, BlurMaskFilter.Blur.NORMAL));
                    int[] offsetXY = new int[2];
                    Bitmap bmAlpha = src.extractAlpha(ptBlur, offsetXY);
                    Paint ptAlphaColor = new Paint();
                    ptAlphaColor.setColor(Color.BLACK);
                    canvas.drawBitmap(bmAlpha, offsetXY[0], offsetXY[1], ptAlphaColor);
                    bmAlpha.recycle();
                    canvas.drawBitmap(src, 0, 0, null);
                    return bmOut;
                }

答案 4 :(得分:1)

这是我对 sharmitha 答案的修改。它是 Kotlin,它使用 bmAlpha 的大小作为结果位图,因此不会裁剪阴影。

private val metrics: DisplayMetrics by lazy {
    val metrics = DisplayMetrics()
    val manager = context.getSystemService(Context.WINDOW_SERVICE) as WindowManager
    manager.defaultDisplay.getMetrics(metrics)
    metrics
}

private fun withDensity(value: Float): Float = metrics.density * value

private fun addShadow(src: Bitmap, blurRadius: Float, @ColorInt shadowColor: Int): Bitmap {
    val offsetXY = IntArray(2)
    val bmAlpha = src.extractAlpha(
            Paint().apply { maskFilter = BlurMaskFilter(withDensity(blurRadius), BlurMaskFilter.Blur.NORMAL) },
            offsetXY
    )

    val bmOut = Bitmap.createBitmap(bmAlpha.width, bmAlpha.height, Bitmap.Config.ARGB_8888)

    Canvas(bmOut).apply {
        drawColor(0, PorterDuff.Mode.CLEAR)
        drawBitmap(
                bmAlpha,
                0f,
                0f,
                Paint().apply { color = shadowColor })
        drawBitmap(
                src,
                0f - offsetXY[0].toFloat(),
                0f - offsetXY[1].toFloat(),
                null)
    }

    bmAlpha.recycle()

    return bmOut
}