ImageView personnalisé avec drop shadow
OK, j'ai lu et cherché partout, et je suis en train de frapper ma tête contre le mur pour essayer de comprendre ça. Voici ce que j'ai jusqu'à présent:
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);
}
}
le "+5 " dans les mesures sont là comme temporaire; D'après ce que je comprends, je vais avoir besoin de faire quelques calculs pour déterminer la taille que l'ombre goutte ajoute à la toile, droit?
mais quand j'utilise ceci:
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;
}
dans mon ImageView, j'obtiens toujours une ImageView normale quand j'exécute le programme.
une idée? Grâce.
EDIT: donc J'ai parlé avec RomainGuy certains dans le canal IRC, et je l'ai travailler maintenant pour des images rectangulaires simples avec le code ci-dessous. Il ne dessinera toujours pas l'ombre directement sur la transparence de mon bitmap, donc je travaille encore là-dessus.
@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);
}
10 réponses
OK, je ne prévois pas d'autres réponses sur celui-ci, donc ce que j'ai fini par aller avec pour l'instant est juste une solution pour les images rectangulaires. J'ai utilisé le NinePatch suivant:
avec le rembourrage approprié en 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"
/>
pour obtenir un assez bon résultat:
pas idéal, mais ça ira.
extrait de la présentation de Romain Guy à Devoxx, pdf trouvé ici .
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);
Espérons que cette aide.
NOTES
- N'oubliez pas pour Honeycomb et au-dessus de vous devez invoquer
setLayerType(LAYER_TYPE_SOFTWARE, mShadow)
, sinon vous ne verrez pas votre ombre! (@Dmitriy_Boichenko) -
SetShadowLayer
n' ne pas travailler avec l'accélération matérielle malheureusement si il a grandement réduit les performances (@Matt Wear) [1] [2]
je crois cette réponse de 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>
Ma solution sale:
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;
}
résultat:
vous voilà. Définissez la source D'ImageView statiquement en xml ou dynamiquement en code.
L'ombre est ici blanche.
<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>
j'arrive à appliquer la bordure de pente en utilisant ce 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;
}
espérons que cette aide !
ça me va ...
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;
} ... }
Voici la mise en œuvre de Paul Burke S réponse:
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:
n'exécutez setLayerType(LAYER_TYPE_SOFTWARE, mShadow);
que si le niveau de L'API est > 10
j'ai construit sur la réponse ci-dessus - https://stackoverflow.com/a/11155031/2060486 - pour créer une ombre autour de tous les côtés..
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;
}
changez la couleur de la const comme bon vous semble.
utilisez cette classe pour dessiner l'ombre sur 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;
}
}