【问题标题】:Custom ImageView with drop shadow带有阴影的自定义 ImageView
【发布时间】:2011-04-11 05:31:41
【问题描述】:

好的,我一直在阅读和搜索,现在我正用头撞墙试图弄清楚这一点。到目前为止,这是我所拥有的:

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。

有什么想法吗?谢谢。

编辑:所以我在 IRC 频道中与 RomainGuy 进行了交谈,我现在使用以下代码处理普通矩形图像。它仍然不会直接将阴影绘制到我的位图的透明度上,所以我仍在努力。

@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 的这种方法在我的测试中还不适合我。
  • 嗯,有趣的问题。我认为您可能可以使用使用 9-patch 的 View,并将其包装在 FrameLayout 中,并为 FrameLayout 提供阴影 9-patch 背景。但是,是的,它只适用于矩形图像,因为 9-patch 无法遵循透明度轮廓。不幸的是,我还没有找到更好的解决方案,但是,从那以后我再也没有真正尝试过。

标签: android overriding imageview dropshadow


【解决方案1】:

如果你想使用自定义的imageView,我建议你使用this one

视图看起来很完美,不要使用任何九路径图像

【讨论】:

    【解决方案2】:

    使用这个类在位图上绘制阴影

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

    【讨论】:

      【解决方案3】:

      我在上面的答案 - 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 中的颜色。

      【讨论】:

        【解决方案4】:

        这里实现Paul Burkes的回答:

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

        待办事项: 仅当 API 级别 > 10 时才执行 setLayerType(LAYER_TYPE_SOFTWARE, mShadow);

        【讨论】:

        • 抱歉,Realm 类调用了无限循环
        • 在编辑问题之前我不能。只需进行任何小的更改,让我知道,然后我会再次投票。
        【解决方案5】:

        我相信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>
        

        【讨论】:

          【解决方案6】:

          本文摘自 Romain Guy 在 Devoxx 的演讲,pdf 找到 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);
          

          希望这会有所帮助。

          注意事项

          1. 不要忘记对于 Honeycomb 及更高版本需要调用 setLayerType(LAYER_TYPE_SOFTWARE, mShadow),不然看不到你的影子! (@Dmitriy_Boichenko)
          2. SetShadowLayer 确实 不幸的是,它不能与硬件加速一起工作,所以它很大 降低性能(@Matt Wear)[1] [2]

          【讨论】:

          • 以后一定会试试的!也感谢 PDF,看起来很有趣。这个演示视频可以在线观看吗?
          • 不要忘记对于 Honeycomb 及更高版本调用 setLayerType(LAYER_TYPE_SOFTWARE, mShadow)。否则,您将看不到自己的影子。
          • 很遗憾,setShadowLayer 不适用于硬件加速。
          • 您也将 Paint 应用于形状,因此阴影层适用于它们。但是,它不会应用于形状的内部“孔”。
          • Devoxx 链接已失效
          【解决方案7】:

          我的肮脏解决方案:

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

          结果:

          【讨论】:

          • 它总是给出蓝色阴影!
          【解决方案8】:

          这对我有用...

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

          【讨论】:

            【解决方案9】:

            我设法使用此代码应用渐变边框..

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

            希望这会有所帮助!

            【讨论】:

              【解决方案10】:

              好的,我预计这个问题不会有更多答案,所以我现在最终选择的只是矩形图像的解决方案。我使用了以下 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"
                      />
              

              要获得一个相当好的结果:



              不理想,但它会做。

              【讨论】:

              • 不要忘记将 png 保存为 drop_shadow.9.png。
              • 哇,我花了太多时间尝试在代码中执行此操作。更优雅的解决方案!
              • 图片需要调整视图边界怎么办?
              • 如果你做对了,就不需要使用draw9patch工具了。
              • 更多分步教程可以在这里找到:sapandiwakar.in/…
              【解决方案11】:

              给你。 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>
              

              【讨论】:

              • 绝对不是我想要的。我想要一个渐变为透明的实际渲染阴影;这只会在我的图像后面给我一个白框。
              • @kcoppock - 没有必要否决这个答案 - 它并没有那么糟糕。
              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2014-10-24
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多