Android 带有阴影的自定义ImageView

Android 带有阴影的自定义ImageView,android,overriding,imageview,dropshadow,Android,Overriding,Imageview,Dropshadow,好吧,我一直在阅读和四处搜索,现在我的头撞在墙上试图弄明白这一点。以下是我目前掌握的情况: 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.w

好吧,我一直在阅读和四处搜索,现在我的头撞在墙上试图弄明白这一点。以下是我目前掌握的情况:

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);
}
在我的ImageView中,当我运行程序时,我仍然得到一个普通的ImageView

有什么想法吗?谢谢


编辑:所以我在IRC频道和Romanguy谈了一些,我现在用下面的代码处理普通的矩形图像。它仍然不会直接将阴影绘制到位图的透明度上,所以我仍在努力

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

给你。以xml静态或代码动态设置ImageView的源

这里的影子是白色的

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

这是罗曼·盖伊在Devxx的演讲,pdf格式

希望这有帮助

注释


  • 别忘了你需要调用的蜂巢和更高版本
    setLayerType(LAYER\u TYPE\u软件,mShadow)
    ,否则你将看不到你的影子!(@Dmitriy_Boichenko)
  • SetShadowLayer
    不支持 不幸的是,它不能与硬件加速一起工作,因此会大大降低速度 降低性能(@Matt-Wear)

  • 好吧,我不认为这一个会有更多的答案,所以我现在要做的只是一个矩形图像的解决方案。我使用了以下九个匹配:



    以及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"
            />
    
    
    
    要获得相当好的结果:




    不太理想,但它可以。

    我使用这段代码成功地应用了渐变边框

    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;
    }
    
    希望这有帮助

    这对我有用

    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;
    } ... }
    
    我的肮脏解决方案:

    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;
    }
    
    结果:
    我相信这个答案来自


    以下是s答案的实现:

    待办事项:
    执行
    setLayerType(图层类型软件,mShadow)仅当API级别>10时

    我已在上面的答案基础上构建--在所有方面创建阴影

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

    根据需要更改常量中的颜色。

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

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

    如果您想使用自定义imageView,我建议您使用

    视图看起来很完美,不要使用任何九路径图像



    绝对不是我想要的。我想要一个实际渲染的阴影,逐渐变为透明;这只会在我的图片后面给我一个白色的框。@kcopock-没有必要否决这个答案-它真的没那么糟糕。我以后一定会尝试!谢谢你的PDF,看起来很有趣。此演示视频是否可在线观看?不要忘记蜂巢及以上版本调用setLayerType(LAYER_TYPE_软件,mShadow)。否则,您将看不到您的阴影。不幸的是,setShadowLayer不能与硬件加速一起工作。您也可以对形状应用绘画,因此阴影层对它们有效。但是,它不会应用于形状中的内部“孔”。Devxx链接已经失效“它现在正在为普通矩形图像工作”。。。所以它不适用于非矩形图像,然后我假设它也不适用于9patch图像,对吗?你让它工作了吗?因为Romain Guy的方法在我的测试中还不适用。嗯,有趣的问题。我想你可能会采取你的观点,使用9面片,把它包装成一个框架布局,给框架布局以阴影9面片的背景。但是是的,它只适用于矩形图像,因为9面片无法跟随透明轮廓。不幸的是,我还没有找到更好的解决方案,但是,从那以后我就再也没有尝试过。别忘了将png保存为drop_shadow.9.png.Wow,我花了太多时间在代码中尝试。更优雅的解决方案!那需要调整视图边界的图像呢?如果你做得正确,就不需要使用draw9patch工具。可以在这里找到更多的分步教程:抱歉,在问题被编辑之前,一个领域类调用了无限循环。只要做一些小的改变,让我知道,然后我会再次投票。它总是给蓝影!
    <?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>
    
    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);
            }
    
        };
    
    }
    
     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;
    }
    
    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;
        }
    
    }