扩展Android View类以添加阴影

问题描述:

我想扩展LinearLayout,以便在绘制布局时在其下面添加阴影。我玩过覆盖onDraw的方法,但我有点失落。任何帮助,甚至图书馆的建议,将不胜感激!扩展Android View类以添加阴影

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

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

enter image description here

感谢

杰克

+0

我想你可以绘制一个黑色矩形具有视图的大小,然后在它上面画出你的看法,但翻译一点点的向下和向右(或者向下和向左,不管你想要什么)。 – 2013-02-18 07:10:10

+0

请你详细说明一下如何做到这一点? – JackMahoney 2013-02-19 04:07:58

+0

'这是一个例子。我无法使用9贴片“ 为什么不能?这是最好和最简单的方法。 – JanithaR 2013-02-20 04:34:27

我同意你的问题的意见:方案阴影效果的影响是一个不错的选择,你可以实现一个简单的9patch(或一组人)相同的效果就像说here

顺便说一句,我太好奇了,下班后我就结束了黑客解决方案。

给出的代码是一个测试,并应旨在作为一个简单证明了概念(所以请不要downvote)。有些所示的操作是相当昂贵,并可能在表演严重影响(有很多例子身边,看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,所以你需要将它转换成一个Bitmap(1st hack)。此外,看起来阴影图层无法在ARGB_8888图像中正常工作,所以为了获得适当的阴影,我发现的唯一方法是绘制给定的NinePatchDrawable(第2个黑客)的alpha蒙版,它位于其下方。

这里有一对夫妇sshots的(在Android [email protected][email protected]测试) enter image description hereenter image description here

编辑:只是要彻底,我连着在测试中使用的9patch (放置在res/drawable/mdpienter image description here

+0

感谢您的回答我会尝试一下。但我不同意9Patch。 9Patch的非伸缩区域对于每个屏幕密度都不会缩放,因此它们在高密度屏幕上会显得更小 - 因此,阴影区域会更小,我不知道在哪里放置容器的儿童填充物 – JackMahoney 2013-02-21 00:58:50

+2

@JackMahoney对不起但你错了。只要看看上面的截图:有2个不同的**真实设备**:320x480 | mdpi,720x1280 | xhdpi。在这两种情况下使用相同的9patch。您可以在每个sshot中以像素为单位来测量气球的圆角,并且您会注意到在xhdpi上的宽度比在mdpi上宽2倍。还有填充物(在9patch中定义)相应地放大。 – 2013-02-21 10:33:32

+0

@ a.bertucci你能为我添加一个示例项目吗? – linker 2013-08-27 17:45:10

尝试这种技术。

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背景:背景=“@ drawable/container_dropshadow“

`

+0

嗯,这看起来有点烦躁。你能解释一下这背后的想法吗?看起来你刚刚堆叠了一系列相互重叠的盒子。这不会创建渐变。 – JackMahoney 2013-02-19 04:07:38

+1

我觉得问题是你的一面,迈克尔还提出了一个逻辑。但你告诉了请详细说明。我也提出了一个建议方式。但你告诉同样的事情请解释。首先你在谷歌搜索。很多东西在那里。如果你找不到确切的解决方案,那么来堆栈溢出,然后提出你的问题。我们无法发送完整的代码片段来完成您的工作。感谢您的理解。所有最好的:) – AndroidEnthusiastic 2013-02-19 06:16:05

+0

答案是错误的。每个项目的填充是相同的,所以它创建一个纯色而不是一个渐变 – JackMahoney 2013-02-20 09:19:25

这是丢弃阴影的任何东西,或者布局,按钮等 background.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" 
/> 
+0

喜欢这个解决方案!对(参考http://www.google.com/design/spec/style/color.html#color-color-palette)和我可以模仿谷歌播放的按钮。 – Milanor 2014-11-21 09:47:29

+0

这也不创建一个渐变,只有一个纯色。 – 2016-04-22 15:27:49