Custom ImageView พร้อมเงาตก


97

โอเคฉันอ่านและค้นหาไปรอบ ๆ และตอนนี้ฉันกำลังเอาหัวพิงกำแพงพยายามคิดออก นี่คือสิ่งที่ฉันมีจนถึงตอนนี้:

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

"มันใช้งานได้แล้วสำหรับรูปภาพสี่เหลี่ยมธรรมดา" ... ดังนั้นมันจึงใช้ไม่ได้กับรูปภาพที่ไม่ใช่รูปสี่เหลี่ยมผืนผ้าแล้วฉันก็คิดว่ามันใช้ไม่ได้กับภาพ 9patch ด้วยใช่ไหม ในขณะเดียวกันคุณทำให้มันใช้งานได้หรือไม่? สาเหตุที่วิธีนี้ของ Romain Guy ใช้ไม่ได้ผลสำหรับฉันในการทดสอบของฉัน
Mathias Conradt

อืมคำถามที่น่าสนใจ ฉันคิดว่าคุณน่าจะใช้ View ของคุณที่ใช้แพทช์ 9 ตัวแล้วรวมไว้ใน FrameLayout และให้ FrameLayout เป็นเงาพื้นหลัง 9 แพทช์ แต่ใช่มันใช้ได้กับภาพสี่เหลี่ยมเท่านั้นเพราะไม่มีทางที่ 9-patch จะทำตามรูปทรงโปร่งใสได้ น่าเสียดายที่ฉันไม่พบวิธีแก้ปัญหาที่ดีกว่าอย่างไรก็ตามฉันไม่ได้ลองอีกครั้งตั้งแต่นั้นมา
Kevin Coppock

คำตอบ:


121

โอเคฉันไม่คาดการณ์คำตอบเกี่ยวกับเรื่องนี้อีกแล้วดังนั้นสิ่งที่ฉันลงเอยด้วยตอนนี้เป็นเพียงวิธีแก้ปัญหาสำหรับภาพสี่เหลี่ยม ฉันใช้ NinePatch ต่อไปนี้

ข้อความแสดงแทน

พร้อมกับช่องว่างภายในที่เหมาะสมใน 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"
        />

เพื่อให้ได้ผลลัพธ์ที่ค่อนข้างดี:

ข้อความแสดงแทน

ไม่เหมาะ แต่จะทำ


17
อย่าลืมเซฟ png เป็น drop_shadow.9.png
NPike

1
ว้าวฉันใช้เวลามากเกินไปในการพยายามทำโค้ดนี้ วิธีแก้ปัญหาที่หรูหรากว่ามาก!
Nik Reiman

แล้วภาพที่ต้องปรับขอบเขตมุมมองล่ะ?
StackOverflowed

ไม่จำเป็นต้องใช้เครื่องมือ draw9patch หากคุณทำอย่างถูกต้อง
Kevin Coppock

2
ดูบทช่วยสอนทีละขั้นตอนเพิ่มเติมได้ที่นี่: sapandiwakar.in/…
Bart Burg

104

นี้นำมาจากการนำเสนอ Romain ผู้ชายที่ Devoxx, PDF พบที่นี่

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

หวังว่านี่จะช่วยได้

หมายเหตุ

  1. อย่าลืมสำหรับ Honeycomb ขึ้นไปคุณต้องเรียกใช้ setLayerType(LAYER_TYPE_SOFTWARE, mShadow)มิฉะนั้นคุณจะไม่เห็นเงาของคุณ! (@Dmitriy_Boichenko)
  2. SetShadowLayerไม่สามารถทำงานกับการเร่งฮาร์ดแวร์ได้อย่างน่าเสียดายจึงลดประสิทธิภาพลงอย่างมาก (@Matt Wear) [1] [2]

แน่นอนฉันจะลองในภายหลัง! ขอบคุณสำหรับ PDF เช่นกันดูเหมือนสิ่งที่น่าสนใจ วิดีโอนำเสนอนี้มีให้ชมทางออนไลน์หรือไม่
Kevin Coppock

14
อย่าลืมสำหรับ Honeycomb ขึ้นไปเรียกใช้ setLayerType (LAYER_TYPE_SOFTWARE, mShadow) ถ้าไม่คุณจะไม่เห็นเงาของคุณ
Dmytro Boichenko

5
setShadowLayer ไม่ทำงานกับการเร่งฮาร์ดแวร์อย่างน่าเสียดาย
Matt Wear

คุณใช้สีกับรูปร่างด้วยเช่นกันเลเยอร์เงาจึงเหมาะกับพวกเขา อย่างไรก็ตามจะไม่ใช้กับ 'รู' ด้านในที่มีรูปร่างอย่างไรก็ตาม
straya

5
ลิงค์ Devoxx ตายแล้ว
joao2fast4u

14

ฉันเชื่อคำตอบนี้จาก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

วิธีแก้ปัญหาสกปรกของฉัน:

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

ผลลัพธ์: ป้อนคำอธิบายภาพที่นี่


ให้เงาสีน้ำเงินเสมอ!
Animesh Mangla

9

อยู่นี่ไง. ตั้งค่าแหล่งที่มาของ ImageView แบบคงที่ใน xml หรือแบบไดนามิกในโค้ด

เงาที่นี่เป็นสีขาว

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

ไม่ใช่สิ่งที่ฉันกำลังมองหาอย่างแน่นอน ฉันต้องการเงาตกกระทบจริงที่จางลงจนโปร่งใส นั่นจะทำให้ฉันมีกล่องสีขาวด้านหลังรูปของฉัน
Kevin Coppock

9
@kcoppock - ไม่จำเป็นต้องลงคะแนนคำตอบนี้ - มันไม่ได้แย่ขนาดนั้นจริงๆ
Caspar Harmer

4

ฉันจัดการใช้เส้นขอบไล่ระดับโดยใช้รหัสนี้ ..

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

หวังว่านี่จะช่วยได้!


2

นี่ใช้ได้กับฉัน ...

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

นี่คือการดำเนินการตามคำตอบของ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);
        }

    };

}

สิ่งที่ต้องทำ: ดำเนินการsetLayerType(LAYER_TYPE_SOFTWARE, mShadow);เฉพาะเมื่อระดับ API> 10


ขอโทษวงที่ไม่มีที่สิ้นสุดถูกเรียกโดยคลาส Realm
RunLoop

ฉันทำไม่ได้จนกว่าจะแก้ไขคำถาม เพียงทำการเปลี่ยนแปลงเล็กน้อยแจ้งให้เราทราบแล้วฉันจะโหวตให้คะแนนอีกครั้ง
RunLoop

0

ฉันสร้างตามคำตอบข้างบน - https://stackoverflow.com/a/11155031/2060486 - เพื่อสร้างเงารอบด้าน ..

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

เปลี่ยนสีใน const ตามที่เห็นสมควร


0

ใช้คลาสนี้เพื่อวาดเงาบนบิตแมป

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

}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.