当将一个ShapeDrawable作为ActionBar项的图标时,该项未显示出来。

4

我正在进行一个简单的项目,希望尽可能避免使用PNG图片。我需要一个“+”(添加)按钮,我使用了下面的代码通过ShapeDrawable创建它。

res/drawable/plus_icon_drawable.xml

<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="line">
            <stroke android:color="#000"
                android:width="3dp"/>
        </shape>
    </item>
    <item>
        <rotate
            android:fromDegrees="90"
            android:toDegrees="90">
            <shape android:shape="line">
                <stroke android:color="#000"
                    android:width="3dp"/>
            </shape>
        </rotate>
    </item>
</layer-list>

当我将此代码作为按钮或ImageButton的背景添加时,它可以正常工作,但是当我将其作为ActionBar项目的图标添加时,该项目不会显示。

当我将PNG图像设置为图标时,它可以正常工作。是否有任何限制ShapeDrawables阻止其在ActionBar上呈现?

这是我的ActiobBar代码

res/menu/action_my_actions.xml

<menu xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:id="@+id/action_some_id"
        android:icon="@drawable/plus_icon_drawable"
        android:title="My Action"
        android:showAsAction="always"/>
</menu>
更新(2014年7月8日):已经测试过原生和AppCompat两种情况。实际上该项存在,但ShapeDrawable图像未被渲染。操作项可以响应点击事件。

我在这里缺少什么?


我在一个旧的ActionBarSherlock应用程序中也使用了那些xml可绘制对象。你使用哪种ActionBar实现?原生的?兼容的?还是ActionBarSherlock? - rekire
@rekire 使用支持库进行本地化。 - gnuanu
1个回答

3
有什么规定限制了 ShapeDrawables 在 ActionBar 上的渲染吗?
没有这样的规定。只是事情就是这样。以下是你看到这种情况的原因:
Layer/ShapeDrawables 没有默认大小。换句话说,一个 ShapeDrawable 宽度和高度都为零是完全可以的。边框宽度参数也无法影响任何内容。
当我将此代码作为 Button 或 ImageButton 的背景时,代码可以正常工作,但当我将其作为 ActionBar 项的图标添加时,该项目不会显示。
当然可以。ImageButtons/Buttons 有填充、最小宽度和最小高度的概念。这些允许 ShapeDrawable 有一些空间来呼吸。例如,使用 Widget.Holo.Button 样式的按钮将设置 minWidth 和 minHeight:
<item name="android:minHeight">48dip</item>
<item name="android:minWidth">64dip</item>

一个有效的测试如下:
定义一个如下所示的ImageView:
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/add" />

</RelativeLayout>
ImageView在上面的定义中没有尺寸指导,我们使用wrap_content。什么也不会显示。另一方面,如果您使用png drawable,则它本身定义了大小。在上面的示例中,使用显式大小(例如50dp)将使ShapeDrawable visible
这是ActionMenuItemView设置drawable的方式:
public void setIcon(Drawable icon) {
    mIcon = icon;
    if (icon != null) {

        // Zero in case you don't define the <size /> 
        int width = icon.getIntrinsicWidth();
        int height = icon.getIntrinsicHeight();
        if (width > mMaxIconSize) {
            final float scale = (float) mMaxIconSize / width;
            width = mMaxIconSize;
            height *= scale;
        }
        if (height > mMaxIconSize) {
            final float scale = (float) mMaxIconSize / height;
            height = mMaxIconSize;
            width *= scale;
        }
        icon.setBounds(0, 0, width, height);
    }
    setCompoundDrawables(icon, null, null, null);

    updateTextButtonVisibility();
}

ShapeDrawable#inflate(...) 方法的片段:

 setIntrinsicWidth((int)
            a.getDimension(com.android.internal.R.styleable.ShapeDrawable_width, 0f));
 setIntrinsicHeight((int)
            a.getDimension(com.android.internal.R.styleable.ShapeDrawable_height, 0f));

没有定义ShapeDrawable_widthShapeDrawable_height,就会使固有宽度和高度设置为零。这意味着从setIcon(Drawable)方法中的语句icon.setBounds(0, 0, width, height);实际上是icon.setBounds(0, 0, 0, 0);
因此,看起来我们确实要处理没有大小信息的ShapeDrawables
一种可能的解决方法:
<shape android:shape="line">
    <size android:width="?android:attr/actionBarSize"
          android:height="?android:attr/actionBarSize"/>
    <stroke android:color="#000"
            android:width="3dp"/>
</shape>

但这样做行不通 - 出于某种原因,?android:attr/actionBarSize 不被视为尺寸。这很奇怪,因为在布局的情况下,?android:attr/actionBarSize 在提供给 android:layout_width 时可以正常工作。
解决方法是定义一个新的尺寸,比如 actionBarSizeDp 并设置它:
res/values/dimens.xml 中:
<dimen name="actionBarSizeDp">48dp</dimen>

res/values-land/dimens.xml文件中:
<dimen name="actionBarSizeDp">40dp</dimen>

这些值是在Android自己的res/values-XX/dimens.xml中定义的默认操作栏大小值。
最后,在plus_icon_drawable.xml中使用actionBarSizeDp:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item>
        <shape android:shape="line">
            <size android:width="@dimen/actionBarSizeDp"
                  android:height="@dimen/actionBarSizeDp"/>
            <stroke android:color="#000"
                    android:width="3dp"/>
        </shape>
    </item>
    <item>
        <rotate
            android:fromDegrees="90"
            android:toDegrees="90">
            <size android:width="@dimen/actionBarSizeDp"
                  android:height="@dimen/actionBarSizeDp"/>
            <shape android:shape="line">
                <stroke android:color="#000"
                        android:width="3dp"/>
            </shape>
        </rotate>
    </item>
</layer-list>

事实上,只需在其中一个LayerDrawables中定义size即可。其他图层会相应地进行绘制。

1
太棒了...真是帮了大忙。像魔法一样顺利地运行了。 - gnuanu
1
@gnuanu 对不起,我发了一篇很长的帖子。但是我想解释清楚问题,而不仅仅是发布一个解决方案。 - Vikram
完全没有问题。了解内部工作原理真的很有趣。 - gnuanu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接