264

I have a linear layout which consists of imageview and textview , one below another in a linear layout.

<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>

Some rules might be missing , this is to give an idea , how layout looks. I want another small text view of say 50dip in length and width , placed over the imageview, by "over" I meant z-index more than imageview , I want to place this , in the center and above(overlapping) the imageview.

I want to know how can we place one view above the other, with varying z-index (preferably in linear layout) ?

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
sat
  • 40,138
  • 28
  • 93
  • 102

13 Answers13

319

You can't use a LinearLayout for this, but you can use a FrameLayout. In a FrameLayout, the z-index is defined by the order in which the items are added, for example:

<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>

In this instance, the TextView would be drawn on top of the ImageView, along the bottom center of the image.

Brenton Scott
  • 153
  • 3
  • 13
Kevin Coppock
  • 133,643
  • 45
  • 263
  • 274
  • Yeah I did it using frame layout itself after going through documentation. Thank you. – sat Jan 14 '11 at 06:00
  • 11
    Is there an advantage of using FrameLayout instead RelativeLayout for this? – User Jun 12 '12 at 16:53
  • 13
    FrameLayout doesn't relate to other views in the group, it just layers them in regard to the parent. RelativeLayout is more for positioning relative to other items. – Kevin Coppock Jun 12 '12 at 17:22
  • by using http://stackoverflow.com/questions/13914609/viewpager-with-previous-and-next-page-boundaries,I am able to show part of previous and next page as shown in image above,but now I don't want to show sharp edges on images.I want them to blur towards edges..please guide me on how can i use z-index for achieving the same – Shruti Jun 11 '13 at 14:47
  • use match_parent, not fill_parent – basickarl Oct 22 '13 at 21:42
  • FrameLayout is the key. I've even used this to draw things over map views and in some situations, though I have no idea why, it works where a relative layout does not. – Rarw Mar 29 '14 at 21:46
  • 3
    Hi kcoppock my mobile (hdpi) doesn't respect z-order in frame layout, but i have an other (xhdpi) and respect this order, do you know why do this? Thanks in advance! :D – Merlí Escarpenter Pérez Feb 17 '15 at 10:53
  • And it is applicable for the elements in the DrawerLayout also. This will be helpful while using the two list views in the Navigation Drawer. – prodeveloper Feb 25 '15 at 13:37
  • 2
    As of API 21 / KitKat you can now use setZ and translationZ; the FrameLayout hack is no longer needed (finally!). This should be the preferred answer for modern 5.0+ development: http://stackoverflow.com/a/29703860/358578 – pbristow Feb 24 '16 at 17:32
  • Not the actual solution for me, but with a little modification it solved my problem. Thanks! – reixa Feb 11 '19 at 10:40
201

Give a try to .bringToFront():

http://developer.android.com/reference/android/view/View.html#bringToFront%28%29

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
diyism
  • 12,477
  • 5
  • 46
  • 46
  • 3
    can we use this in xml files too? – Ads Jun 13 '12 at 08:25
  • 2
    I was looking for something such to bring my view to change itz zindex during translation animation. – DeltaCap019 Oct 25 '13 at 10:25
  • Could u find a solution for translation animation? – nAkhmedov Sep 15 '14 at 13:06
  • 9
    bringChildToFront() just changes the index of the View inside its parent – Zar E Ahmer Nov 26 '14 at 18:05
  • Take care about Nepster's comment. Because bringChildToFront() changes the index, the items' layout order is changed (changing x and y coordinates). It does not only change the z order, as one might think. – nyi May 05 '15 at 07:29
  • Saved my day. Thanks. This is especially required if we are toggling the views on and off programtically in between – Harish Sep 14 '15 at 14:39
  • killer answer, your unorthodox-ed and not like other fools out here, who gave the same conventional(copy cats) answers... your true legend – DJphy Jul 13 '16 at 18:22
  • 4
    this screws the whole layout order – Jemshit May 26 '17 at 15:05
  • bringToFront or make sure that the layer/frame you want to be drawn is added first at framelayout index: rootLayout.addView(shadow); rootLayout.addView(viewPager,0);//to make dropshadow effect visible , draw viewpager at least layer – Arthur Melo Aug 07 '18 at 04:15
71

RelativeLayout works the same way, the last image in the relative layout wins.

jt-gilkeson
  • 2,661
  • 1
  • 30
  • 40
  • 14
    Unless you use elevation, then you also need the largest value in that. – Sami Kuhmonen Jun 17 '16 at 15:52
  • 5
    This question was asked and answered before elevation existed. Using elevation won't fix the issue if your app has a minSdk less than Lollipop (on pre-lollipop phones the layout will look wrong if you rely on just elevation) - you still have to pay attention to the order of the layout. You are right though, that if you use elevation on the bottom component - you definitely need to have at least the same or higher on the top. – jt-gilkeson Sep 23 '16 at 18:39
30

Changing the draw order

An alternative is to change the order in which the views are drawn by the parent. You can enable this feature from ViewGroup by calling setChildrenDrawingOrderEnabled(true) and overriding getChildDrawingOrder(int childCount, int i).

Example:

/**
 * 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];
    }
}

Output:

Calling OrderLayout#setDrawOrder(int) with 0-1-2 results in:

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

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
Tobrun
  • 18,291
  • 10
  • 66
  • 81
  • Do you call setDrawOrder(i) in the activity's onCreate()? If I have 6 widgets in my xml, do I need to initialize each row of DRAW_ORDERS with an array of 6 numbers (0-5)? – John Ward Jan 10 '17 at 21:36
  • @JohnWard the layout shown above is solely for example purposes, you can define your own behaviour in getChildDrawingOrder(). – Tobrun Jan 22 '17 at 14:27
  • here's related post with much clear code: https://stackoverflow.com/questions/20524162/framelayout-in-reverse – Robert Nov 23 '18 at 04:04
27

I solved the same problem by add android:elevation="1dp" to which view you want it over another. But it can't display below 5.0, and it will have a little shadow, if you can accept it, it's OK.

So, the most correct solution which is @kcoppock said.

thirtyyuan
  • 603
  • 2
  • 8
  • 13
26

You can use view.setZ(float) starting from API level 21. Here you can find more info.

Vadim Kotov
  • 8,084
  • 8
  • 48
  • 62
  • 3
    This should now be the preferred answer for API 21 / KitKat / 5.0 or higher. The old frameLayout hack is thankfully no longer required. Personally I don't understand the difference between setZ and translationZ, but the latter is an attribute and worked beautifully for me. Thanks! – pbristow Feb 24 '16 at 17:36
  • For the previous apis you can use ViewCompat#setZ() too. – Ali Yucel Akgul Oct 04 '16 at 12:14
  • @AliYucelAkgul if you look at the docs [here](https://developer.android.com/reference/android/support/v4/view/ViewCompat.html#setZ%28android.view.View,%20float%29), you can see `Compatibility: API < 21: No-op `. So I think it will not work – Vadim Kotov Oct 04 '16 at 14:39
  • @VadimKotov, I have tried in my app and it just works. – Ali Yucel Akgul Oct 04 '16 at 14:41
  • @AliYucelAkgul well, that is strange. Maybe a mistake in the docs? Are you 100% sure it is working for API < 21? I have an idea that it can change order of views for the previous APIs, but no fancy stuff like elevation, etc.. – Vadim Kotov Oct 04 '16 at 14:45
  • @VadimKotov, I just pushed my code but let me check it again. – Ali Yucel Akgul Oct 04 '16 at 14:46
  • I would assume translationZ would translate from the current z index, ie: z index = 3, so translationZ = 4 makes z = 7, whereas setZ just sets the z index. – Trevor Hart Jan 30 '17 at 06:52
  • since `setTranslationZ()` and `setZ()` were added in API 21, I'm gonna have to wait on using them *for a few years* because the current min API level is 17 for the app (and I suspect for most apps) I'm working on. – Someone Somewhere Jul 26 '17 at 13:14
8

Try this in a RelativeLayout:

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

It works for me.

DevB2F
  • 4,674
  • 4
  • 36
  • 60
Rizzo
  • 81
  • 1
  • 2
  • 2
    You should explain a bit better. For instance where would you put this code? – DevB2F Nov 09 '17 at 23:04
  • Well, I put it in OnCreate() method. Each image added has an z index of preference over the ones added before. I mean, through the z index I can change the position of the next behind the previous. – Rizzo Dec 29 '17 at 22:14
6

There is a way to use LinearLayout. Just set the marginTop of your previous element to the corresponding negative value, and make sure the element you want on top is after the element you want below in your 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>
Tim
  • 741
  • 1
  • 10
  • 16
4

AFAIK you cannot do it with linear layouts, you'll have to go for a RelativeLayout.

Vincent Mimoun-Prat
  • 28,208
  • 16
  • 81
  • 124
2

I use this, if you want only one view to be bring to front when needed:

containerView.bringChildToFront(topView);

containerView is container of views to be sorted, topView is view which i want to have as top most in container.

for multiple views to arrange is about to use setChildrenDrawingOrderEnabled(true) and overriding getChildDrawingOrder(int childCount, int i) as mentioned above.

Milan Jurkulak
  • 557
  • 3
  • 6
1

If you are adding the View programmatically, you can use yourLayout.addView(view, 1);

where 1 is the index.

Alexander Maslew
  • 408
  • 5
  • 16
1

Try this in a RelativeLayout:

example XML:

<ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:contentDescription="@string/todo"
        app:srcCompat="@drawable/ic_offer_close_outline"
        app:tint="@color/colorWhite"
        android:translationZ="999dp" />

example java:

ImageView image = new ImageView(this);
image.SetZ(float z);
MD.Riyaz
  • 412
  • 3
  • 9
0

Use android:transformZ. It works also with LinearLayout.