ImageView Kustom dengan drop shadow

98

Oke, saya telah membaca dan mencari-cari, dan sekarang saya membenturkan kepala ke dinding mencoba mencari tahu. Inilah yang saya miliki sejauh ini:

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

}

Tanda "+5" dalam pengukuran di sana bersifat sementara; Dari apa yang saya pahami, saya perlu melakukan beberapa matematika untuk menentukan ukuran yang ditambahkan drop shadow ke kanvas, bukan?

Tetapi ketika saya menggunakan ini:

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

di ImageView saya, saya masih mendapatkan ImageView biasa saat menjalankan program.

Ada pemikiran? Terima kasih.

EDIT: Jadi saya berbicara dengan RomainGuy beberapa di saluran IRC, dan saya sudah membuatnya berfungsi untuk gambar persegi panjang biasa dengan kode di bawah ini. Itu masih tidak akan menarik bayangan langsung ke transparansi bitmap saya, jadi saya masih mengerjakannya.

@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);       
}
Kevin Coppock
sumber
"Ini berfungsi sekarang untuk gambar persegi panjang biasa" ... jadi tidak berfungsi untuk gambar non-persegi panjang, lalu saya berasumsi ini juga tidak berfungsi untuk gambar 9patch, benar? Apakah Anda sementara itu membuatnya berfungsi? Penyebab pendekatan oleh Romain Guy ini belum berhasil untuk saya dalam pengujian saya.
Mathias Conradt
Hmm, pertanyaan menarik. Saya akan berpikir Anda mungkin bisa mengambil View Anda yang menggunakan 9-patch, dan membungkusnya dalam FrameLayout, dan memberikan FrameLayout latar belakang 9-patch drop shadow. Tapi ya, ini hanya berfungsi untuk gambar persegi panjang, karena tidak ada cara untuk 9-patch mengikuti kontur transparansi. Sayangnya saya belum menemukan solusi yang lebih baik, namun saya belum benar-benar mencoba lagi sejak itu.
Kevin Coppock

Jawaban:

122

Oke, saya tidak melihat ada jawaban lagi untuk yang satu ini, jadi apa yang akhirnya saya lakukan untuk saat ini hanyalah solusi untuk gambar persegi panjang. Saya telah menggunakan NinePatch berikut:

teks alt

bersama dengan padding yang sesuai di 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"
        />

untuk mendapatkan hasil yang cukup baik:

teks alt

Tidak ideal, tetapi cukup.

Kevin Coppock
sumber
17
Jangan lupa untuk menyimpan png sebagai drop_shadow.9.png.
NPike
1
Wow, saya menghabiskan terlalu banyak waktu untuk mencoba melakukan ini dalam kode. Solusi yang jauh lebih elegan!
Nik Reiman
Bagaimana dengan gambar yang perlu menyesuaikan batas tampilan?
StackOverflow pada
Tidak perlu menggunakan alat draw9patch jika Anda membuatnya dengan benar.
Kevin Coppock
2
Sedikit lagi tutorial langkah demi langkah dapat ditemukan di sini: sapandiwakar.in/…
Bart Burg
105

Ini diambil dari presentasi Romain Guy di Devoxx, pdf yang ditemukan di sini .

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

Semoga ini membantu.

CATATAN

  1. Jangan lupa untuk Honeycomb dan di atasnya Anda perlu memanggil setLayerType(LAYER_TYPE_SOFTWARE, mShadow), jika tidak, Anda tidak akan melihat bayangan Anda! (@Kontolindonesia)
  2. SetShadowLayersayangnya tidak bekerja dengan akselerasi perangkat keras sehingga sangat mengurangi kinerja (@Matt Wear) [1] [2]
Paul Burke
sumber
Saya pasti akan mencobanya nanti! Terima kasih untuk PDFnya juga, sepertinya hal yang menarik. Apakah video presentasi ini tersedia online untuk ditonton?
Kevin Coppock
14
Jangan lupa untuk Honeycomb dan yang lebih baru, panggil setLayerType (LAYER_TYPE_SOFTWARE, mShadow). Jika tidak, Anda tidak akan melihat bayangan Anda.
Dmytro Boichenko
5
Sayangnya, setShadowLayer tidak berfungsi dengan akselerasi perangkat keras.
Matt Wear
Anda juga menerapkan Paint ke bentuk, jadi lapisan bayangan berfungsi untuk mereka. Namun, itu tidak akan diterapkan pada 'lubang' dalam suatu bentuk.
straya
4
Tautan Devoxx sudah mati
joao2fast4u
14

Saya percaya jawaban ini dari 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>
Kedelai
sumber
12

Solusi kotor saya:

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

hasil: masukkan deskripsi gambar di sini

mundur
sumber
itu memberi bayangan biru selalu!
Animesh Mangla
8

Ini dia. Setel sumber ImageView secara statis dalam xml atau secara dinamis dalam kode.

Bayangan di sini berwarna putih.

<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>
plugmind
sumber
Jelas bukan yang saya cari. Saya menginginkan drop shadow yang benar-benar berubah menjadi transparan; itu hanya akan memberi saya kotak putih di belakang gambar saya.
Kevin Coppock
9
@kcoppock - tidak perlu meremehkan jawaban ini - tidak terlalu buruk.
Caspar Harmer
4

Saya berhasil menerapkan batas gradien menggunakan kode ini ..

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

semoga ini membantu !

Rukmal Dias
sumber
2

Ini bekerja untuk saya ...

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;
} ... }
Miguel S. Mendoza
sumber
2

Berikut Implementasi dari jawaban 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: jalankan setLayerType(LAYER_TYPE_SOFTWARE, mShadow);hanya jika API Level> 10

Murmel
sumber
Maaf, loop tak terbatas dipanggil oleh kelas Realm
RunLoop
Saya tidak bisa sampai pertanyaannya diedit. Buat perubahan kecil apa pun, beri tahu saya, lalu saya akan memberi suara positif lagi.
RunLoop
0

Saya telah membangun jawaban di atas - https://stackoverflow.com/a/11155031/2060486 - untuk membuat bayangan di sekitar SEMUA sisi ..

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

Ubah warna di const sesuai keinginan Anda.

peresisUser
sumber
0

Gunakan kelas ini untuk menggambar bayangan pada 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;
    }

}
Ali Imran
sumber
-1

Jika Anda ingin menggunakan imageView kustom, saya sarankan Anda menggunakan yang ini

Tampilan terlihat sempurna dan tidak menggunakan gambar sembilan jalur apa pun

masukkan deskripsi gambar di sini

Thiện Kopites
sumber