The reason is actually very simple. :) We are so caught up thinking in 2D that we overlook the elevation
- in Z.
There is nothing wrong with your first layout. The Button
simply has a higher elevation
than the ImageView
- exactly 1dp
higher. Therefore, no matter how you arrange the two views, the Button
rises above.
A bit of proof:
A Button, by default gets the Widget.Material.Button
style:
<!-- Bordered ink button -->
<style name="Widget.Material.Button">
<item name="background">@drawable/btn_default_material</item>
<item name="textAppearance">?attr/textAppearanceButton</item>
<item name="minHeight">48dip</item>
<item name="minWidth">88dip</item>
<item name="stateListAnimator">@anim/button_state_list_anim_material</item>
<item name="focusable">true</item>
<item name="clickable">true</item>
<item name="gravity">center_vertical|center_horizontal</item>
</style>
The attribute that introduces this elevation is android:stateListAnimator
. StateListAnimator
is similar to StateListDrawable
, and provides state change animations. The complete xml is here: Link. But here's the base state of the button:
<!-- base state -->
<item android:state_enabled="true">
<set>
<objectAnimator android:propertyName="translationZ"
android:duration="@integer/button_pressed_animation_duration"
android:valueTo="0"
android:startDelay="@integer/button_pressed_animation_delay"
android:valueType="floatType"/>
<objectAnimator android:propertyName="elevation"
android:duration="0"
android:valueTo="@dimen/button_elevation_material"
android:valueType="floatType" />
</set>
</item>
As you can see, the elevation value for the button is set to @dimen/button_elevation_material
:
<dimen name="button_elevation_material">1dp</dimen>
And that's how the ImageView
ends up being behind/below the Button
.
So, what can we do?
A straight-forward solution would be to set the ImageView's
elevation to the same amount - 1dp
.
Another solution, which will require a bit of work, is to remove the Button's
elevation rather than change ImageView's
. Based on the default StateListAnimator
, we can create our own - and remove the elevation. Then, in your res/values-v21/styles.xml
, define a style that inherits from Widget.Material.Button
:
<style name="MyDepressedButtonStyle" parent="android:Widget.Material.Button">
<item name="android:stateListAnimator">@anim/customized_state_animator</item>
</style>
Now, set this style on your Button
:
<Button
style="@style/MyDepressedButtonStyle"
....
.... />
Edit:
Actually, we can apply the customized StateListAnimator
directly:
<Button
android:stateListAnimator="@anim/customized_state_animator"
....
.... />
No need to take the scenic
route!