自定义带阴影的 ImageView

好吧,我一直在阅读和寻找,现在我的头撞在墙上,试图找出这一点。以下是我目前掌握的信息:

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

}

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

但是当我用这个的时候:

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

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

有什么想法吗? 谢谢。

编辑: 我和 RomainGuy 在 IRC 频道聊过,我现在用下面的代码来处理纯矩形图像。它仍然不会直接绘制阴影到我的位图的透明度,所以我还在努力。

@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);
}
123677 次浏览

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

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>

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

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. SetShadowLayer does not work with hardware acceleration unfortunately so it greatly reduces performances (@Matt Wear) [1] [2]

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:

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

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 !

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

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

I believe this answer from 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>

Here the Implementation of Paul Burkes answer:

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

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

 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.

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


}

If you want to use the custom imageView, I suggest you use this one

View look perfect and dont't use any nine path image

enter image description here