如何在所有Android版本上显示支持库的ActionBar和Toolbar的阴影?

15
这是一个简单的问题:
我使用新的支持库,可以拥有Toolbar实例并将其设置为活动的ActionBar(或使用默认ActionBar)。
如何自定义由Toolbar(或默认ActionBar)产生的阴影?
我尝试使用"setElevation"(在两者上),但似乎在Android Lollipop上没有任何作用。
而且,我找不到如何自定义早期版本中的阴影。也许有API可以实现这一点?或者至少有可绘制的图像(尽管我已经尝试过)?
OK,我已经找到了以下内容:
对于Lollipop和pre-Lollipop,当您的活动使用普通的AppCompat主题(例如"Theme.AppCompat.Light")时,阴影应该看起来很好。
但是,当您使用"setSupportActionBar"(并使用适当的主题,例如"Theme.AppCompat.Light.NoActionBar")时,您将在阴影方面遇到一些问题。
对于pre-Lollipop,您可以在工具栏下方放置一个FrameLayout,它看起来与用于正常主题的FrameLayout完全相同,如下所示:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    <FrameLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:foreground="?android:windowContentOverlay"
        android:visibility="@integer/action_bar_shadow_background_visibility" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:text="@string/hello_world" />

</RelativeLayout>

您可以将FrameLayout用作activity的内容,或使用我做的方法。您还可以通过像我所做的那样将整数0放置在pre-Lollipop上,将2放置在Lollipop及以上版本上来设置其可见性,如“action_bar_shadow_background_visibility”中所示。

这是一个丑陋的解决方案,但它却非常有效,但仅适用于Pre-Lollipop版本。

无论如何,我仍然找不到一种方法来很好地显示Lollipop版本的阴影,以防我使用“setSupportActionBar”。我尝试在工具栏和操作栏上使用“setElevation”(在调用“setSupportActionBar”后立即使用“getSupportActionBar”)。

我也不知道如何获得操作栏的标准高度,因为我注意到很多教程都说要为工具栏使用“minHeight”。对此,我会非常感激帮助。

有谁能帮我解决这个问题吗?

再次强调,这是我的需求:

  • 对于Pre-Lollipop,请显示与Toolbar和ActionBar支持库中使用的相同阴影。
  • 对于Lollipop,使用Lollipop自带的阴影。
  • 额外功能:在Lollipop上自定义阴影使其更加“精致”(并可能在所有版本上进行自定义)。
  • 额外功能:为什么工具栏的minHeight获得的是操作栏高度的值,而不是本身的高度?

编辑:好的,这是我迄今为止为了模仿Lollipop阴影所做的工作:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical" >

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:minHeight="?attr/actionBarSize"
        app:theme="@style/ThemeOverlay.AppCompat.ActionBar" />

    <include
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        layout="@layout/toolbar_action_bar_shadow" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@+id/toolbar"
        android:text="@string/hello_world" />

</RelativeLayout>

res/layout/toolbar_action_bar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:foreground="?android:windowContentOverlay" />

res/layout-v21/toolbar_action_bar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:src="@drawable/msl__action_bar_shadow" />

res/drawable-v21/res/layout/msl__action_bar_shadow.xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >

    <!-- intended for lollipop and above -->

    <item>
        <shape
            android:dither="true"
            android:shape="rectangle" >
            <gradient
                android:angle="270"
                android:endColor="#00000000"
                android:startColor="#33000000" />

            <size android:height="10dp" />
        </shape>
    </item>

</layer-list>

虽然不完全相同,但是它们很相似。


编辑:我仍然不知道为什么它会发生。我认为我在问题上编写的代码是具有误导性的,因为所有需要做的就是设置一个背景。但是在我测试的应用程序中仍然没有帮助。我尝试修复的应用程序在导航抽屉上方有工具栏。

一度我以为可能是因为我做了一个自定义的工具栏(继承自Toolbar),并且根据导航抽屉改变了其Alpha值,但即使我使用正常的工具栏并禁用与Alpha相关的所有内容,它仍然无法正常工作。此外,不仅如此,在一个全新的项目上,我尝试为工具栏设置半透明的背景,结果根据“setElevation”方法产生了阴影。

现在对我来说更难找到这个问题的原因了,因为似乎不是由于透明度或自定义Toolbar类造成的...

这里是具有工具栏的活动的布局,如果可以的话,请参考一下:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <android.support.v4.widget.DrawerLayout
        android:id="@+id/activity_main__drawerLayout"
        android:layout_width="match_parent"
        android:layout_height="match_parent" >

        <FrameLayout
            android:id="@+id/activity_main__fragmentContainer"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />

        <include
            android:layout_width="240dp"
            android:layout_height="match_parent"
            android:layout_gravity="left"
            android:layout_marginTop="?attr/actionBarSize"
            layout="@layout/activity_main__sliding_menu" />
    </android.support.v4.widget.DrawerLayout>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical" >

        <com.app.ui.Toolbar
            android:id="@+id/activity_main__toolbar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimary"
            android:layoutDirection="ltr"
            android:minHeight="?attr/actionBarSize"
            app:theme="@style/ThemeOverlay.AppCompat.ActionBar"
            tools:ignore="UnusedAttribute" />

        <!-- <include -->
        <!-- android:layout_width="match_parent" -->
        <!-- android:layout_height="wrap_content" -->
        <!-- layout="@layout/toolbar_action_bar_shadow" /> -->
    </LinearLayout>

</FrameLayout>

可能是工具栏默认没有阴影?的重复问题。 - rds
2个回答

14
据我所知/所读,AppCompat库目前似乎不会渲染任何阴影。虽然调用似乎存在(例如ViewCompat.setElevation(View view, float elevation)),但它似乎没有起到作用。
因此,我想你现在需要自己添加阴影,至少在支持库实现阴影渲染之前是这样的。
查看Google IO 2014源代码,他们是这样实现的:
  • DrawShadowFrameLayout 可以将背景 drawable 渲染成阴影
  • bottom_shadow.9.png 代表 actionbar 下方的阴影
  • app:shadowDrawable 属性用于设置阴影 drawable
  • 在 DrawShadowFrameLayout 上调用 setShadowTopOffset() 将阴影向下移动,以便它出现在 actionbar 下面。 (需要自己设置)

更多关于此事的信息请参见Reddit Threadstackoverflow


为什么他们使用了"DrawShadowFrameLayout"?他们是不是在某些情况下隐藏了阴影? - android developer
确实如此,他们根据values/refs.xml中的引用选择阴影drawable,而对于API级别21,该引用为@null。 - Jeroen Mols
我的意思是,他们为什么要使用这个类?它有一个隐藏阴影的选项...他们可以根据资源设置可见性/可绘制。 - android developer
不确定为什么他们使用setShadowVisible方法,但我猜测这与在滚动后隐藏阴影有关...使用DrawShadowFrameLayout有两个优点:
  1. 他们可以将完全相同的布局用于棒棒糖和旧版API(DrawShadowFrameLayout整洁地隐藏了所有实现细节)
  2. 只需注入不同的9-patch,此类别可以潜在地用于更多类型的阴影(例如cardview->侧面+底部阴影)
- Jeroen Mols
我明白了。 我仍然不明白为什么在我的小测试中一切都运行良好,但在应用程序中却不是这样。 无论如何,你的建议是使用drawable就可以了,这也是我在解决问题时所做的,对吗? - android developer
显示剩余2条评论

3

这是我在一个应用程序中所做的事情,它显示了阴影(人工阴影):

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/mainActivityLinearLayout"
        android:layout_width="match_parent"
        android:orientation="vertical"
        android:layout_height="match_parent"
        android:background="@drawable/logo">

      <LinearLayout
        android:layout_height="?attr/actionBarSize"
        android:layout_width="match_parent"
        android:orientation="vertical">

        <!-- refer to your toolbar layout -->
        <include layout="@layout/toolbar"/>

      </LinearLayout>

      <!-- My Left Drawer -->
      <android.support.v4.widget.DrawerLayout
        android:id="@+id/drawer_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"    >

        <RelativeLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/relative_layout"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_marginStart="0dp"
            android:layout_marginLeft="0dp" >

           ....

           <!-- Lots of other stuffs -->             

           ....

           <!-- Shadow -->
          <View
           android:layout_width="match_parent"
           android:layout_below="@+id/toolbarShadow"
           android:layout_marginTop="0dp"
           android:layout_height="6dp"
           android:background="@drawable/toolbar_dropshadow" />

        </RelativeLayout>
     </android.support.v4.widget.DrawerLayout>


 </LinearLayout>

toolbar_dropshadow.xml :

<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item>
    <shape>
        <gradient
            android:angle="90"
            android:startColor="@android:color/transparent"
            android:endColor="#D6D6D6"
            android:type="linear" />
    </shape>
</item>
</selector>

我正在使用地图活动,所有这些都在我的地图视图上显示出美妙的阴影。

希望这可以帮助...


好的,我对我发布的代码也做了同样的事情。我希望能够看到如何在棒棒糖上使用正常的方法,并知道什么原因导致它出现问题。 - android developer
由于某些原因(可能是一个bug),如果您正在使用Lollipop设备上的set elevation,则请确保不要设置带有透明度的纯色,例如:#eeff0000,阴影不会显示出来,而应该使用:#ff0000进行正确的使用。此外,带有alpha的背景颜色似乎也无法正常工作。请使用没有alpha的背景颜色。如果仍然无法按预期工作,请发布您正在使用的Lollipop代码,我可能能够提供帮助。希望这可以帮助... - Abhinav Puri
看到我的编辑。可惜那不是它,而在一个新项目中,我实际上成功地使用了一个半透明的背景。现在对我来说更难找到原因了。 - android developer

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