扩展 Android View 类以添加阴影 [英] Extending Android View class to add a dropshadow

查看:16
本文介绍了扩展 Android View 类以添加阴影的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想扩展 LinearLayout 以便在绘制布局时在其下方添加阴影.我尝试过重写 onDraw 方法,但我有点迷茫.任何有关此甚至图书馆建议的帮助将不胜感激!

I want to extend LinearLayout so that when my layout is drawn a drop shadow is added underneath it. I've played around overriding the onDraw method but I'm a bit lost. Any help with this or even library suggestions would be greatly appreciated!

这是我试图实现的阴影视图示例.我不相信我可以在这里使用九个补丁,因为我需要视图的内容在白框中.这意味着我需要知道边界和 PNG 末端之间的距离.但是我相信不同的屏幕密度意味着这个距离总是相同的 PX 但不相同的 DP.

Here's an example of the drop shadow view I am trying to achieve. I don't believe I can use a nine patch here because I need the contents of the view to be within the white box. This would mean I would need to know the distance between the border and the end of the PNG. However I believe different screen densities mean that this distance will always be the same PX but not the same DP.

所以要清楚,我需要一种方法来扩展 View 类,以便在将其添加到布局时在其下绘制阴影.请不要使用 XML 或 9Patch 解决方案.

So to be clear I need a way to extend the View class so that a drop shadow is drawn under it when it is added to a layout. No XML or 9Patch solutions please.

谢谢

杰克

推荐答案

我同意你的问题的评论:程序化的阴影效果是一个糟糕的选择,你可以用一个简单的 9patch(或一组他们)如此处所述.

I agree with the comments on your question: programmatic dropshadow effect is a bad choice, and you could achieve the same effect with a simple 9patch (or a set of them) like stated here.

顺便说一句,我太好奇了,最后我在下班后破解了一个解决方案.

BTW I was too curious, and I ended with hacking a solution after work.

提供的代码是一个测试,应该作为一个简单的概念验证(所以请不要投反对票).显示的一些操作相当昂贵,并且可能严重影响性能(周围有很多例子,看看此处此处 以获得一个想法).它应该是最后的解决方案,仅适用于偶尔显示的组件.

The code presented is a test, and should be intended as a simple proof-of-concept (so please don't downvote). Some of the operations shown are quite expensive, and may seriously impact on the performances (There are many examples around, look here, here to get an idea). It should be a last resort solution only for a component shown once-in-a-while.

public class BalloonView extends TextView {

  protected NinePatchDrawable bg;
  protected Paint paint;
  protected Rect padding = new Rect();
  protected Bitmap bmp;

  public BalloonView(Context context) {
    super(context);
init();
  }

  public BalloonView(Context context, AttributeSet attrs) {
    super(context, attrs);
init();
  }

  public BalloonView(Context context, AttributeSet attrs, int defStyle) {
    super(context, attrs, defStyle);
    init();
  }

  @SuppressLint("NewApi")
  protected void init() {
    // decode the 9patch drawable
    bg = (NinePatchDrawable) getResources().getDrawable(R.drawable.balloon);

    // get paddings from the 9patch and apply them to the View
    bg.getPadding(padding);
    setPadding(padding.left, padding.top, padding.right, padding.bottom);

    // prepare the Paint to use below
    paint = new Paint();
    paint.setAntiAlias(true);
    paint.setColor(Color.rgb(255,255,255));
    paint.setStyle(Style.FILL);

    // this check is needed in order to get this code
    // working if target SDK>=11
    if( Build.VERSION.SDK_INT >= 11 )
      setLayerType(View.LAYER_TYPE_SOFTWARE, paint);

    // set the shadowLayer
    paint.setShadowLayer(
      padding.left * .2f, // radius
      0f, // blurX
      padding.left * .1f, // blurY
      Color.argb(128, 0, 0, 0) // shadow color
    );
  }

  @Override
  protected void onDraw(Canvas canvas) {
    int w = getMeasuredWidth();
    int h = getMeasuredHeight();

    // set 9patch bounds according to view measurement
    // NOTE: if not set, the drawable will not be drawn
    bg.setBounds(0, 0, w, h);

    // this code looks expensive: let's do once
    if( bmp == null ) {

      // it seems like shadowLayer doesn't take into account
      // alpha channel in ARGB_8888 sources...
      bmp = Bitmap.createBitmap(w, h, Config.ARGB_8888);

      // draw the given 9patch on the brand new bitmap
      Canvas tmp = new Canvas(bmp);
      bg.draw(tmp);

      // extract only the alpha channel
      bmp = bmp.extractAlpha();
    }

    // this "alpha mask" has the same shape of the starting 9patch,
    // but filled in white and **with the dropshadow**!!!!
    canvas.drawBitmap(bmp, 0, 0, paint);

    // let's paint the 9patch over...
    bg.draw(canvas);

    super.onDraw(canvas);
  }
}

首先,为了获得程序化的阴影,您必须像声明一样处理 Paint.setShadowLayer(...) 此处.基本上,您应该为用于在自定义视图的 Canvas 上绘制的 Paint 对象定义一个 阴影层.不幸的是,您不能使用 Paint 对象来绘制 NinePatchDrawable,因此您需要将其转换为位图(第一个技巧).此外,似乎阴影层无法与 ARGB_8888 图像一起正常工作,因此我找到的获得正确阴影的唯一方法是在其自身下方绘制给定的 NinePatchDrawable(第二次黑客)的 alpha 蒙版.

First of all in order to get programmatic drop shadow you have to deal with Paint.setShadowLayer(...) like stated here. Basically you should define a shadow layer for the Paint object used to draw on the Canvas of your custom view. Unfortunately you cannot use a Paint object to draw a NinePatchDrawable, so you need to convert it into a Bitmap (1st hack). Furthermore it seems like shadow layers can't work properly with ARGB_8888 images, so the only way I found in order to get a proper shadow has been to draw the alpha mask of the given NinePatchDrawable (2nd hack) just below itself.

这里有几个 sshots(在 Android 2.3.3@mdpi 和 4.2.2@xhdpi 上测试)

Here's a couple of sshots (tested on Android 2.3.3@mdpi and 4.2.2@xhdpi)

为了彻底,我附上了测试中使用的9patch(放置在res/drawable/mdpi)

just to be thorough, I attached the 9patch used in the test (placed in res/drawable/mdpi)

这篇关于扩展 Android View 类以添加阴影的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆