Android 带有阴影的自定义 ImageView

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3693234/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 11:24:22  来源:igfitidea点击:

Custom ImageView with drop shadow

androidoverridingimageviewdropshadow

提问by Kevin Coppock

Okay, I've been reading and searching around, and am now banging my head against the wall trying to figure this out. Here's what I have so far:

好吧,我一直在阅读和搜索,现在正用头撞墙试图弄清楚这一点。这是我到目前为止所拥有的:

package com.pockdroid.sandbox;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.widget.ImageView;

public class ShadowImageView extends ImageView {

private Rect mRect;
private Paint mPaint;

public ShadowImageView(Context context)
{
    super(context);
    mRect = new Rect();
    mPaint = new Paint();
    mPaint.setAntiAlias(true);
    mPaint.setShadowLayer(2f, 1f, 1f, Color.BLACK);
}

@Override
protected void onDraw(Canvas canvas) 
{
    Rect r = mRect;
    Paint paint = mPaint;

    canvas.drawRect(r, paint);
    super.onDraw(canvas);
}

@Override
protected void onMeasure(int w, int h)
{
    super.onMeasure(w,h);
    int mH, mW;
    mW = getSuggestedMinimumWidth() < getMeasuredWidth()? getMeasuredWidth() : getSuggestedMinimumWidth();
    mH = getSuggestedMinimumHeight() < getMeasuredHeight()? getMeasuredHeight() : getSuggestedMinimumHeight();
    setMeasuredDimension(mW + 5, mH + 5);
}

}

}

The "+5" in the measurements are there as temporary; From what I understand I'll need to do some math to determine the size that the drop shadow adds to the canvas, right?

测量中的“+5”是临时的;据我所知,我需要做一些数学运算来确定阴影添加到画布上的大小,对吗?

But when I use this:

但是当我使用这个时:

public View getView(int position, View convertView, ViewGroup parent) {
    ShadowImageView sImageView;
    if (convertView == null) {
        sImageView = new ShadowImageView(mContext);
        GridView.LayoutParams lp = new GridView.LayoutParams(85, 85);
        sImageView.setLayoutParams(lp);

        sImageView.setScaleType(ImageView.ScaleType.CENTER);
        sImageView.setPadding(5,5,5,5);
    } else {
        sImageView = (ShadowImageView) convertView;
    }

    sImageView.setImageBitmap(bitmapList.get(position));
    return sImageView;
}

in my ImageView, I still get just a normal ImageView when I run the program.

在我的 ImageView 中,当我运行程序时,我仍然只得到一个普通的 ImageView。

Any thoughts? Thanks.

有什么想法吗?谢谢。

EDIT: So I spoke with RomainGuy some in the IRC channel, and I have it working now for plain rectangular images with the below code. It still won't draw the shadow directly to my bitmap's transparency though, so I'm still working on that.

编辑:所以我在 IRC 频道中与 RomainGuy 进行了一些交谈,我现在可以使用以下代码处理普通矩形图像。尽管如此,它仍然不会直接将阴影绘制到我的位图的透明度上,所以我仍在努力。

@Override
protected void onDraw(Canvas canvas) 
{
    Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.omen);
    Paint paint = new Paint();
    paint.setAntiAlias(true);
    paint.setShadowLayer(5.5f, 6.0f, 6.0f, Color.BLACK);
    canvas.drawColor(Color.GRAY);
    canvas.drawRect(50, 50, 50 + bmp.getWidth(), 50 + bmp.getHeight(), paint);
    canvas.drawBitmap(bmp, 50, 50, null);       
}

回答by Kevin Coppock

Okay, I don't foresee any more answers on this one, so what I ended up going with for now is just a solution for rectangular images. I've used the following NinePatch:

alt text

along with the appropriate padding in XML:

好的,我没有预见到关于这个的更多答案,所以我现在最终选择的只是矩形图像的解决方案。我使用了以下 NinePatch:

替代文字

以及 XML 中的适当填充:

<ImageView
        android:id="@+id/image_test"
        android:background="@drawable/drop_shadow"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:paddingLeft="6px"
        android:paddingTop="4px"
        android:paddingRight="8px"
        android:paddingBottom="9px"
        android:src="@drawable/pic1"
        />

to get a fairly good result:

alt text

Not ideal, but it'll do.

获得相当好的结果:

替代文字

不理想,但可以。

回答by Paul Burke

This is taken from Romain Guy's presentation at Devoxx, pdf found here.

这摘自 Romain Guy 在 Devoxx 的演讲,pdf 在这里找到。

Paint mShadow = new Paint(); 
// radius=10, y-offset=2, color=black 
mShadow.setShadowLayer(10.0f, 0.0f, 2.0f, 0xFF000000); 
// in onDraw(Canvas) 
canvas.drawBitmap(bitmap, 0.0f, 0.0f, mShadow);

Hope this helps.

希望这可以帮助。

NOTES

笔记

  1. Don't forget for Honeycomb and above you need to invoke setLayerType(LAYER_TYPE_SOFTWARE, mShadow), otherwise you will not see your shadow! (@Dmitriy_Boichenko)
  2. SetShadowLayerdoes not work with hardware acceleration unfortunately so it greatly reduces performances (@Matt Wear) [1][2]
  1. 不要忘记为 Honeycomb 及以上你需要调用 setLayerType(LAYER_TYPE_SOFTWARE, mShadow),否则你将看不到你的影子!(@Dmitriy_Boichenko)
  2. SetShadowLayer不幸的是,它不适用于硬件加速,因此它大大降低了性能 (@Matt Wear) [1] [2]

回答by Soyoes

I believe this answer from UIFuel

我相信UIFuel 的这个答案

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

     <!-- Drop Shadow Stack -->
     <item>
        <shape>
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#00CCCCCC" />
        </shape>
    </item>
     <item>
        <shape>
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#10CCCCCC" />
        </shape>
    </item>
     <item>
        <shape>
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#20CCCCCC" />
        </shape>
    </item>
     <item>
        <shape>
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#30CCCCCC" />
        </shape>
    </item>
    <item>
        <shape>
            <padding android:top="1dp" android:right="1dp" android:bottom="1dp" android:left="1dp" />
            <solid android:color="#50CCCCCC" />
        </shape>
    </item>

    <!-- Background -->
    <item>
    <shape>
            <solid android:color="@color/white" />
        <corners android:radius="3dp" />
    </shape>
    </item>
</layer-list>

回答by recoilme

My dirty solution:

我肮脏的解决方案:

private static Bitmap getDropShadow3(Bitmap bitmap) {

    if (bitmap==null) return null;
    int think = 6;
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int newW = w - (think);
    int newH = h - (think);

    Bitmap.Config conf = Bitmap.Config.ARGB_8888;
    Bitmap bmp = Bitmap.createBitmap(w, h, conf);
    Bitmap sbmp = Bitmap.createScaledBitmap(bitmap, newW, newH, false);

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Canvas c = new Canvas(bmp);

    // Right
    Shader rshader = new LinearGradient(newW, 0, w, 0, Color.GRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
    paint.setShader(rshader);
    c.drawRect(newW, think, w, newH, paint);

    // Bottom
    Shader bshader = new LinearGradient(0, newH, 0, h, Color.GRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
    paint.setShader(bshader);
    c.drawRect(think, newH, newW  , h, paint);

    //Corner
    Shader cchader = new LinearGradient(0, newH, 0, h, Color.LTGRAY, Color.LTGRAY, Shader.TileMode.CLAMP);
    paint.setShader(cchader);
    c.drawRect(newW, newH, w  , h, paint);


    c.drawBitmap(sbmp, 0, 0, null);

    return bmp;
}

result: enter image description here

结果: 在此处输入图片说明

回答by plugmind

Here you are. Set source of ImageView statically in xml or dynamically in code.

这个给你。在 xml 中静态设置 ImageView 的源或在代码中动态设置。

Shadow is here white.

阴影在这里是白色的。

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="wrap_content" android:layout_height="wrap_content">

    <View android:layout_width="wrap_content" android:layout_height="wrap_content"
        android:background="@android:color/white" android:layout_alignLeft="@+id/image"
        android:layout_alignRight="@id/image" android:layout_alignTop="@id/image"
        android:layout_alignBottom="@id/image" android:layout_marginLeft="10dp"
        android:layout_marginBottom="10dp" />

    <ImageView android:id="@id/image" android:layout_width="wrap_content"
        android:layout_height="wrap_content" android:src="..."
        android:padding="5dp" />

</RelativeLayout>

回答by Rukmal Dias

I manage to apply gradient border using this code..

我设法使用此代码应用渐变边框..

public static Bitmap drawShadow(Bitmap bitmap, int leftRightThk, int bottomThk, int padTop) {
    int w = bitmap.getWidth();
    int h = bitmap.getHeight();

    int newW = w - (leftRightThk * 2);
    int newH = h - (bottomThk + padTop);

    Bitmap.Config conf = Bitmap.Config.ARGB_8888;
    Bitmap bmp = Bitmap.createBitmap(w, h, conf);
    Bitmap sbmp = Bitmap.createScaledBitmap(bitmap, newW, newH, false);

    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Canvas c = new Canvas(bmp);

    // Left
    int leftMargin = (leftRightThk + 7)/2;
    Shader lshader = new LinearGradient(0, 0, leftMargin, 0, Color.TRANSPARENT, Color.BLACK, TileMode.CLAMP);
    paint.setShader(lshader);
    c.drawRect(0, padTop, leftMargin, newH, paint); 

    // Right
    Shader rshader = new LinearGradient(w - leftMargin, 0, w, 0, Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP);
    paint.setShader(rshader);
    c.drawRect(newW, padTop, w, newH, paint);

    // Bottom
    Shader bshader = new LinearGradient(0, newH, 0, bitmap.getHeight(), Color.BLACK, Color.TRANSPARENT, TileMode.CLAMP);
    paint.setShader(bshader);
    c.drawRect(leftMargin -3, newH, newW + leftMargin + 3, bitmap.getHeight(), paint);
    c.drawBitmap(sbmp, leftRightThk, 0, null);

    return bmp;
}

hope this helps !

希望这可以帮助 !

回答by Miguel S. Mendoza

This works for me ...

这对我有用...

public class ShadowImage extends Drawable {

Bitmap bm;

@Override
public void draw(Canvas canvas) {

    Paint mShadow = new Paint();
    Rect rect = new Rect(0,0,bm.getWidth(), bm.getHeight());

    mShadow.setAntiAlias(true);
    mShadow.setShadowLayer(5.5f, 4.0f, 4.0f, Color.BLACK);

    canvas.drawRect(rect, mShadow);
    canvas.drawBitmap(bm, 0.0f, 0.0f, null);

}

public ShadowImage(Bitmap bitmap) {
    super();
    this.bm = bitmap;
} ... }

回答by Murmel

Here the Implementation of Paul Burkes answer:

这里是Paul Burke回答的实现:

public class ShadowImageView extends ImageView {

    public ShadowImageView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    public ShadowImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public ShadowImageView(Context context) {
        super(context);
    }

    private Paint createShadow() {
        Paint mShadow = new Paint();

        float radius = 10.0f;
        float xOffset = 0.0f;
        float yOffset = 2.0f;

        // color=black
        int color = 0xFF000000;
        mShadow.setShadowLayer(radius, xOffset, yOffset, color);


        return mShadow;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        Paint mShadow = createShadow();
        Drawable d = getDrawable();
        if (d != null){
            setLayerType(LAYER_TYPE_SOFTWARE, mShadow);
            Bitmap bitmap = ((BitmapDrawable) getDrawable()).getBitmap();
            canvas.drawBitmap(bitmap, 0.0f, 0.0f, mShadow);
        } else {
            super.onDraw(canvas);
        }

    };

}

TODO: execute setLayerType(LAYER_TYPE_SOFTWARE, mShadow);only if API Level is > 10

TODO:setLayerType(LAYER_TYPE_SOFTWARE, mShadow);仅在 API 级别 > 10 时执行

回答by peresisUser

I've built upon the answer above - https://stackoverflow.com/a/11155031/2060486- to create a shadow around ALL sides..

我已经建立在上面的答案 - https://stackoverflow.com/a/11155031/2060486- 在所有方面创建阴影..

 private static final int GRAY_COLOR_FOR_SHADE = Color.argb(50, 79, 79, 79);

// this method takes a bitmap and draws around it 4 rectangles with gradient to create a
// shadow effect.
public static Bitmap addShadowToBitmap(Bitmap origBitmap) {
    int shadowThickness = 13; // can be adjusted as needed
    int bmpOriginalWidth = origBitmap.getWidth();
    int bmpOriginalHeight = origBitmap.getHeight();
    int bigW = bmpOriginalWidth + shadowThickness * 2; // getting dimensions for a bigger bitmap with margins
    int bigH = bmpOriginalHeight + shadowThickness * 2;
    Bitmap containerBitmap = Bitmap.createBitmap(bigW, bigH, Bitmap.Config.ARGB_8888);
    Bitmap copyOfOrigBitmap = Bitmap.createScaledBitmap(origBitmap, bmpOriginalWidth, bmpOriginalHeight, false);
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
    Canvas canvas = new Canvas(containerBitmap); // drawing the shades on the bigger bitmap
    //right shade - direction of gradient is positive x (width)
    Shader rightShader = new LinearGradient(bmpOriginalWidth, 0, bigW, 0, GRAY_COLOR_FOR_SHADE,
            Color.TRANSPARENT, Shader.TileMode.CLAMP);
    paint.setShader(rightShader);
    canvas.drawRect(bigW - shadowThickness, shadowThickness, bigW, bigH - shadowThickness, paint);
    //bottom shade - direction is positive y (height)
    Shader bottomShader = new LinearGradient(0, bmpOriginalHeight, 0, bigH, GRAY_COLOR_FOR_SHADE,
            Color.TRANSPARENT, Shader.TileMode.CLAMP);
    paint.setShader(bottomShader);
    canvas.drawRect(shadowThickness, bigH - shadowThickness, bigW - shadowThickness, bigH, paint);
    //left shade - direction is negative x
    Shader leftShader = new LinearGradient(shadowThickness, 0, 0, 0, GRAY_COLOR_FOR_SHADE,
            Color.TRANSPARENT, Shader.TileMode.CLAMP);
    paint.setShader(leftShader);
    canvas.drawRect(0, shadowThickness, shadowThickness, bigH - shadowThickness, paint);
    //top shade - direction is negative y
    Shader topShader = new LinearGradient(0, shadowThickness, 0, 0, GRAY_COLOR_FOR_SHADE,
            Color.TRANSPARENT, Shader.TileMode.CLAMP);
    paint.setShader(topShader);
    canvas.drawRect(shadowThickness, 0, bigW - shadowThickness, shadowThickness, paint);
    // starting to draw bitmap not from 0,0 to get margins for shade rectangles
    canvas.drawBitmap(copyOfOrigBitmap, shadowThickness, shadowThickness, null);
    return containerBitmap;
}

Change the color in the const as you see fit.

根据需要更改 const 中的颜色。

回答by Ali Imran

Use this class to draw shadow on bitmaps

使用此类在位图上绘制阴影

public class ShadowGenerator {

    // Percent of actual icon size
    private static final float HALF_DISTANCE = 0.5f;
    public static final float BLUR_FACTOR = 0.5f/48;

    // Percent of actual icon size
    private static final float KEY_SHADOW_DISTANCE = 1f/48;
    public static final int KEY_SHADOW_ALPHA = 61;

    public static final int AMBIENT_SHADOW_ALPHA = 30;

    private static final Object LOCK = new Object();
    // Singleton object guarded by {@link #LOCK}
    private static ShadowGenerator sShadowGenerator;

    private  int mIconSize;

    private final Canvas mCanvas;
    private final Paint mBlurPaint;
    private final Paint mDrawPaint;
    private final Context mContext;

    private ShadowGenerator(Context context) {
        mContext = context;
        mIconSize = Utils.convertDpToPixel(context,63);
        mCanvas = new Canvas();
        mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
        mBlurPaint.setMaskFilter(new BlurMaskFilter(mIconSize * BLUR_FACTOR, Blur.NORMAL));
        mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG);
    }

    public synchronized Bitmap recreateIcon(Bitmap icon) {
        mIconSize = Utils.convertDpToPixel(mContext,3)+icon.getWidth();
        int[] offset = new int[2];
        Bitmap shadow = icon.extractAlpha(mBlurPaint, offset);
        Bitmap result = Bitmap.createBitmap(mIconSize, mIconSize, Config.ARGB_8888);
        mCanvas.setBitmap(result);

        // Draw ambient shadow
        mDrawPaint.setAlpha(AMBIENT_SHADOW_ALPHA);
        mCanvas.drawBitmap(shadow, offset[0], offset[1], mDrawPaint);

        // Draw key shadow
        mDrawPaint.setAlpha(KEY_SHADOW_ALPHA);
        mCanvas.drawBitmap(shadow, offset[0], offset[1] + KEY_SHADOW_DISTANCE * mIconSize, mDrawPaint);

        // Draw the icon
        mDrawPaint.setAlpha(255);
        mCanvas.drawBitmap(icon, 0, 0, mDrawPaint);

        mCanvas.setBitmap(null);
        return result;
    }



    public static ShadowGenerator getInstance(Context context) {

        synchronized (LOCK) {
            if (sShadowGenerator == null) {
                sShadowGenerator = new ShadowGenerator(context);
            }
        }
        return sShadowGenerator;
    }

}