【问题标题】:Extending Android View class to add a dropshadow扩展 Android View 类以添加阴影
【发布时间】:2013-02-18 06:57:06
【问题描述】:

我想扩展LinearLayout 以便在绘制布局时在其下方添加一个投影。我玩过覆盖onDraw 方法,但我有点迷路了。对此甚至图书馆建议的任何帮助将不胜感激!

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

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

谢谢

杰克

【问题讨论】:

  • 我认为您可以绘制一个具有视图大小的黑色矩形,然后在其顶部绘制您的视图,但稍微向下和向右(或向下和向左,无论您想要)。
  • 能否请您详细说明如何做到这一点?
  • Here's an example. I can't use a 9-Patch either 为什么不呢?这是最好和最简单的方法。
  • @JanithaR - 我需要确保视图的内容不会流出白框边框。所以我需要为容器添加填充。但是,如果 9-Patch 被拉伸成不同的尺寸,我怎么知道这个边界在哪里?
  • 我不明白你刚才说什么,但相信我 9-patch 是要走的路。因为我已经做到了。 Here 就是一个例子。只需确保正确定义了哪些部分不需要拉伸以及哪些区域包含内容。

标签: android android-layout inheritance view android-view


【解决方案1】:

我同意 cmets 关于您的问题:程序化阴影效果是一个糟糕的选择,您可以使用简单的 9patch(或其中一组)达到相同的效果,如 here 所述。

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

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

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(...),如here 所述。基本上你应该为Paint 对象定义一个阴影层,用于在你的自定义视图的Canvas 上绘图。不幸的是,您不能使用Paint 对象来绘制 NinePatchDrawable,因此您需要将其转换为位图(第一次破解)。此外,阴影层似乎无法与 ARGB_8888 图像一起正常工作,所以我发现为了获得适当阴影的唯一方法是在其自身下方绘制给定 NinePatchDrawable(第 2 次 hack)的 alpha 蒙版。

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

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

【讨论】:

  • 谢谢你的回答我会试试看。但我不同意 9Patch。 9Patch 的未拉伸区域不会针对每个屏幕密度进行缩放,因此它们在高密度屏幕上会显得更小 - 因此阴影区域会更小,我不知道在哪里放置带有填充的容器的子项
  • @JackMahoney 对不起,你错了。看看上面的截图:有 2 种不同的真实设备:320x480|mdpi、720x1280|xhdpi。在这两种情况下,都使用相同的 9patch。您可以在每个镜头中以像素为单位测量气球的圆角,您会注意到 xhdpi 上的宽度是 mdpi 上的 2 倍。填充(在 9patch 中定义)也会相应地扩大。
  • @a.bertucci 你能帮我添加一个示例项目吗?
【解决方案2】:

这是给布局、按钮等任何东西投下阴影的最简单方法。 背景.xml

    <?xml version="1.0" encoding="utf-8"?>
    <layer-list xmlns:android="http://schemas.android.com/apk/res/android">
        <item >
            <shape 
                android:shape="rectangle">
            <solid android:color="@android:color/darker_gray" />
            <corners android:radius="5dp"/>
            </shape>
        </item>
        <item android:right="1dp" android:left="1dp" android:bottom="2dp">
            <shape 
                android:shape="rectangle">
            <solid android:color="@android:color/white"/>
            <corners android:radius="5dp"/>
            </shape>
        </item>
    </layer-list>

在你的 main.xml 中

<LineaLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/background"
/>

【讨论】:

【解决方案3】:

试试这个技巧。

container_dropshadow.xml

`

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

然后你可以将它作为背景应用到 XML 布局中

LinearLayout android:background="@drawable/container_dropshadow"

`

【讨论】:

  • 嗯,这看起来有点烦躁。你能解释一下这背后的想法是什么。看起来您刚刚将一系列盒装产品堆叠在一起。这不会产生渐变。
  • 我认为问题出在你这边,Michael 也提出了一种逻辑。但你告诉请详细说明。还向我展示了一种建议方式。但是你说了同样的话,请解释一下。首先你有谷歌搜索。有很多东西。如果您找不到确切的解决方案,请来堆栈溢出,然后提出您的问题。我们无法发送完整的 sn-p 来完成您的工作。感谢您的理解。一切顺利:)
  • 答案是错误的。每个项目的填充是相同的,因此它创建的是纯色而不是渐变
猜你喜欢
  • 2014-03-10
  • 2014-03-27
  • 2017-04-14
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多