ImageView personalizzato con ombreggiatura


98

Ok, ho letto e cercato in giro e ora sbatto la testa contro il muro cercando di capirlo. Ecco cosa ho finora:

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

}

Il "+5" nelle misurazioni è temporaneo; Da quello che ho capito dovrò fare un po 'di matematica per determinare la dimensione che l'ombra esterna aggiunge alla tela, giusto?

Ma quando uso questo:

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

nel mio ImageView, ottengo ancora solo un normale ImageView quando eseguo il programma.

qualche idea? Grazie.

EDIT: Così ho parlato con RomainGuy alcuni nel canale IRC, e ora funziona per semplici immagini rettangolari con il codice seguente. Tuttavia, non disegnerà ancora l'ombra direttamente sulla trasparenza della mia bitmap, quindi ci sto ancora lavorando.

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

"ora funziona per semplici immagini rettangolari" ... quindi non funziona per immagini non rettangolari, e quindi presumo che non funzioni anche per 9patch-images, giusto? Nel frattempo l'hai fatto funzionare? Perché questo approccio di Romain Guy non funziona ancora per me nel mio test.
Mathias Conradt

Hmm, domanda interessante. Penso che potresti probabilmente prendere la tua vista che utilizza la patch 9 e avvolgerla in un FrameLayout e dare al FrameLayout lo sfondo a 9 patch dell'ombra esterna. Ma sì, funziona solo per le immagini rettangolari, perché non c'è modo per un 9 patch di seguire i contorni della trasparenza. Sfortunatamente non ho trovato una soluzione migliore, tuttavia, da allora non ho davvero riprovato.
Kevin Coppock

Risposte:


122

Ok, non prevedo altre risposte su questo, quindi quello che ho deciso per ora è solo una soluzione per le immagini rettangolari. Ho usato il seguente NinePatch:

testo alternativo

insieme al riempimento appropriato 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"
        />

per ottenere un risultato abbastanza buono:

testo alternativo

non l'ideale, ma andrà bene.


17
Non dimenticare di salvare il png come drop_shadow.9.png.
NPike

1
Wow, ho passato troppo tempo a provare a farlo in codice. Soluzione molto più elegante!
Nik Reiman

E le immagini che devono regolare i limiti della vista?
StackOverflowed

Non è necessario utilizzare lo strumento draw9patch se lo fai correttamente.
Kevin Coppock

2
Un po 'più di tutorial passo passo può essere trovato qui: sapandiwakar.in/…
Bart Burg

105

Questo è tratto dalla presentazione di Romain Guy a Devoxx, pdf trovato qui .

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

Spero che questo ti aiuti.

APPUNTI

  1. Non dimenticare per Honeycomb e soprattutto devi invocare setLayerType(LAYER_TYPE_SOFTWARE, mShadow), altrimenti non vedrai la tua ombra! (@Dmitriy_Boichenko)
  2. SetShadowLayerpurtroppo non funziona con l'accelerazione hardware, quindi riduce notevolmente le prestazioni (@Matt Wear) [1] [2]

Lo proverò sicuramente più tardi! Grazie anche per il PDF, sembra roba interessante. Questo video di presentazione è disponibile online per essere guardato?
Kevin Coppock

14
Non dimenticare per Honeycomb e versioni successive invocare setLayerType (LAYER_TYPE_SOFTWARE, mShadow). Altrimenti non vedrai la tua ombra.
Dmytro Boichenko

5
setShadowLayer non funziona con l'accelerazione hardware sfortunatamente.
Matt Wear

applichi un Paint anche alle forme, quindi il livello ombra funziona per loro. Tuttavia, non verrà applicato ai "fori" interni in una forma.
straya

4
Il collegamento Devoxx è morto
joao2fast4u

14

Credo questa risposta da 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>

12

La mia soluzione sporca:

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

risultato: inserisci qui la descrizione dell'immagine


dà sempre l'ombra blu!
Animesh Mangla

8

Ecco qui. Imposta l'origine di ImageView staticamente in xml o dinamicamente nel codice.

L'ombra è qui bianca.

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

Sicuramente non è quello che sto cercando. Voglio un'ombra esterna renderizzata reale che sfuma in trasparente; questo mi darà solo una scatola bianca dietro la mia immagine.
Kevin Coppock

9
@kcoppock - non c'è bisogno di sottovalutare questa risposta - non era poi così male.
Caspar Harmer

4

Riesco ad applicare il bordo sfumato usando questo codice ..

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

spero che questo ti aiuti !


2

Questo funziona per 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;
} ... }

2

Ecco l'implementazione della risposta di 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: eseguire setLayerType(LAYER_TYPE_SOFTWARE, mShadow);solo se il livello API è> 10



Non posso finché la domanda non viene modificata. Fai solo qualche piccola modifica, fammelo sapere e poi voterò di nuovo.
RunLoop

0

Ho costruito sulla risposta sopra - https://stackoverflow.com/a/11155031/2060486 - per creare un'ombra attorno a TUTTI i lati ..

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

Cambia il colore nella cost come meglio credi.


0

Usa questa classe per disegnare l'ombra sulle bitmap

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

}

-1

Se vuoi usare l'immagine personalizzata, ti suggerisco di usare questa

La vista sembra perfetta e non utilizzare nessuna immagine a nove percorsi

inserisci qui la descrizione dell'immagine

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.