在安卓系统中,如何将一个视图放置/重叠(z-index)在另一个视图之上?

264

我有一个线性布局,其中包含一个imageview和textview,一个在另一个下面。

<LinearLayout android:orientation="horizontal" ... >
 <ImageView 
     android:id="@+id/thumbnail"
     android:layout_weight="0.8" 
     android:layout_width="0dip"
     android:layout_height="fill_parent">
 </ImageView>
 <TextView 
    android:id="@+id/description"
    android:layout_weight="0.2"
    android:layout_width="0dip"
    android:layout_height="wrap_content">
 </TextView>

有些规则可能缺失,这只是为了给您一个布局的概念。我想要另一个长度和宽度为50dip的小文本视图,放置在ImageView上方。当我说“上方”时,我指的是z-index更高于ImageView, 我想将其置于中心并覆盖(重叠)在ImageView上方。

我想知道如何将一个视图放置在另一个视图上方,并具有不同的z-index值(最好在线性布局中)?

13个回答

319

你不能使用LinearLayout来完成此任务,但是可以使用FrameLayout。在FrameLayout中,z-index的定义是由添加顺序决定的,例如:

<FrameLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/my_drawable"
        android:scaleType="fitCenter"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="bottom|center"
        android:padding="5dp"
        android:text="My Label"
        />
</FrameLayout>
在这种情况下,TextView 将会被绘制在 ImageView 的顶部,沿着图片的底部中心。

11
针对这个问题,使用FrameLayout相比RelativeLayout,是否有优势? - User
13
FrameLayout不与组内的其他视图相关,它只是按照父级容器来层叠它们。RelativeLayout更多用于相对于其他项的位置定位。 - Kevin Coppock
请使用match_parent,而不是fill_parent。 - basickarl
3
嗨,kcoppock,我的手机(hdpi)在框架布局中没有遵守z-order顺序,但我的另一个手机(xhdpi)却遵守了这个顺序,你知道为什么会这样吗?先谢谢啦! :D - Merlí Escarpenter Pérez
2
从API 21 / KitKat开始,您现在可以使用setZ和translationZ;FrameLayout hack不再需要(终于!)。这应该是现代5.0+开发的首选答案:https://dev59.com/hm855IYBdhLWcg3wuG0M#29703860。 - pbristow
显示剩余5条评论

201

3
我们可以在 XML 文件中使用这个吗? - Ads
2
我正在寻找一些东西,以便在翻译动画期间将我的视图带到更改它的zindex。 - DeltaCap019
你能找到一个翻译动画的解决方案吗? - nAkhmedov
9
bringChildToFront()方法仅仅改变了一个View在其父容器中的索引。 - Zar E Ahmer
4
这会破坏整个布局顺序。 - Jemshit Iskenderov
显示剩余4条评论

71

RelativeLayout的工作方式类似,相对布局中最后一个图像占据上风。


14
除非你使用海拔高度,否则你还需要其中的最大值。 - Sami Kuhmonen
5
在引入“elevation”属性之前,就有人提出并回答了这个问题。如果你的应用程序的minSdk小于棒棒糖(在棒棒糖以下的手机上,只依赖于“elevation”的布局将看起来不正确),使用“elevation”不能解决这个问题 - 你仍然需要注意布局的顺序。不过你说得没错,如果你在底部组件上使用了“elevation”,那么你肯定需要在顶部至少使用相同或更高的值。 - jt-gilkeson

30

更改绘制顺序

另一种方法是通过更改父视图中视图的绘制顺序来实现。您可以通过调用 ViewGroup 的 setChildrenDrawingOrderEnabled(true) 方法并重写 getChildDrawingOrder(int childCount, int i) 方法启用此功能。

示例:

/**
 * Example Layout that changes draw order of a FrameLayout
 */
public class OrderLayout extends FrameLayout {

    private static final int[][] DRAW_ORDERS = new int[][]{
            {0, 1, 2},
            {2, 1, 0},
            {1, 2, 0}
    };

    private int currentOrder;

    public OrderLayout(Context context) {
        super(context);
        setChildrenDrawingOrderEnabled(true);
    }

    public void setDrawOrder(int order) {
        currentOrder = order;
        invalidate();
    }

    @Override
    protected int getChildDrawingOrder(int childCount, int i) {
        return DRAW_ORDERS[currentOrder][i];
    }
}

输出:

调用OrderLayout#setDrawOrder(int)并传入0-1-2将导致以下结果:

enter image description here enter image description here enter image description here


你在activity的onCreate()中调用setDrawOrder(i)吗?如果我的xml中有6个小部件,我是否需要用一个包含6个数字(0-5)的数组来初始化DRAW_ORDERS的每一行? - John Ward
@JohnWard 上面展示的布局仅为示例目的,您可以在getChildDrawingOrder()中定义自己的行为。 - Tobrun
这里有一篇相关的帖子,其中包含更清晰的代码:https://dev59.com/k3rZa4cB1Zd3GeqPyyMR - Robert

27

我通过在你想要覆盖另一个视图的视图中添加 android:elevation="1dp" 来解决了同样的问题。但它不能在5.0以下显示,并且会有一点阴影,如果你可以接受,那就没问题了。

因此,最正确的解决方案是 @kcoppock 提到的。


不知道为什么这个答案被低估了。 - Anuj

26
您可以从 API 21 开始使用 view.setZ(float) 。您可以在这里找到更多信息。

3
这现在应该是API 21 / KitKat / 5.0或更高版本的首选答案。旧的frameLayout hack不再需要了。个人而言,我不理解setZ和translationZ之间的区别,但后者是一个属性,对我非常有效。谢谢! - pbristow
对于之前的API,您也可以使用ViewCompat#setZ()。 - Ali Yucel Akgul
@VadimKotov,我已经在我的应用程序中尝试过了,它完全可行。 - Ali Yucel Akgul
@AliYucelAkgul 嗯,那很奇怪。也许是文档中的错误?你确定它在 API < 21 上工作了吗? 我有一个想法,它可以改变之前 API 的视图顺序,但没有像高度等花哨的东西。 - Vadim Kotov
我会认为translationZ会从当前的z索引进行翻译,即:z索引=3,因此translationZ=4使z=7,而setZ只是设置z索引。 - Trevor Hart
显示剩余3条评论

8

在RelativeLayout中尝试以下内容:

ImageView image = new ImageView(this);
image.SetZ(float z);

这对我来说很有效。


2
您应该更好地解释一下。例如,您会将此代码放在哪里? - DevB2F
好的,我把它放在OnCreate()方法中。每个添加的图像都有一个优先级的z索引,比之前添加的更高。我的意思是,通过z索引,我可以改变下一个图像相对于前一个图像的位置。 - Rizzo

6

有一种使用LinearLayout的方法。只需将前一个元素的marginTop设置为相应的负值,并确保您想要置于顶部的元素在您的XML中位于您想要置于底部的元素之后。

<linearLayout android:orientation="horizontal" ... >
<ImageView 
 android:id="@+id/thumbnail"
 android:layout_weight="0.8" 
 android:layout_width="0dip"
 android:layout_height="fill_parent"
>
</ImageView>
<TextView 
android:id="@+id/description"
android:layout_marginTop="-20dip"
android:layout_weight="0.2"
android:layout_width="0dip"
android:layout_height="wrap_content"
>
</TextView>

4
据我所知,您无法使用线性布局来实现它,您需要选择RelativeLayout布局。

RelativeLayout并不继承FrameLayout,你确定它能正常工作吗? - ekatz
可以。您可以使用FrameLayout或RelativeLayout中的任何一个。它们各自有不同的放置子视图的方式。请参阅文档以了解有关这些布局的更多信息。 - Vincent Mimoun-Prat
这就是正确的方式。 - Albert James Teddy

2

如果您只想在需要时将一个视图置于前台,可以使用以下方法:

containerView.bringChildToFront(topView);

containerView 是要排序的视图容器,topView 是我想要置于容器顶部的视图。

如果要排列多个视图,可以使用 setChildrenDrawingOrderEnabled(true) 并重写 getChildDrawingOrder(int childCount, int i),如上所述。


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