如何将视图置于后面?如何通过编程控制 Z 轴顺序?

70

我有一个问题,想把一个视图置于底部。在Android中,我们有一个类似于bringToFront()的方法,可以将视图放在另一个视图的顶部。就像这样,我想将视图放在之前的图片下面。

在Android中是否有类似于sendToBack()bringToBack()的方法?如果有,能否帮忙提供一下。

注意:我不想通过布局中项目的放置顺序来控制z-order,我想以编程方式控制z-order。

我不想隐藏前面的视图,我只想让它们在移动的视图后面。

13个回答

67

我意识到其他答案中已经暗示了这一点,但是没有人发布过代码。我将以下代码保留在一个实用程序类中,在那里我有用于处理视图的“helper”函数:

public static void sendViewToBack(final View child) {
    final ViewGroup parent = (ViewGroup)child.getParent();
    if (null != parent) {
        parent.removeView(child);
        parent.addView(child, 0);
    }
}

这样做不会覆盖之前在 slot 0 中的任何内容吗?我认为您必须将每个子项向下移动一个插槽,因此在删除子项后,获取子项计数,对于它们中的每一个检索子项,用先前的子项替换它(从已删除的子项开始),然后继续下一个子项。完成后,将最后一个子项添加回去,就完成了...或者我认为如此。我没有看到 addView 的文档中有任何提示表明它会取代该位置上现有的子项。 - James
3
不,它会为你移动它们(类似于ArrayList中的add())。如果您想尝试,请随意。或者通过查看源代码来确认它……它在内部使用addInArray()方法。但是您关于文档不清楚是正确的。因此,如果您使用它,可能应该在将来的API更新中注意此问题,以防他们决定更改此未记录的行为。好观点。 - Turix
1
感谢您提供这段代码。唯一需要修改的是将“null != parent”改为“parent != null”,因为这样更易读。 - clocksmith
6
@clocksmith 谢谢你的建议。由于我年纪大了,我倾向于在比较中始终将 null 放在左边,以防止意外将 null 赋值(例如如果你在 "parent = null" 中忘记第二个 '=' 字符)。我知道这现在并不是必需的,因为编译器会有很好的警告,但这已经成了一个习惯,而且一如既往地难以改掉,就像人们常说的那样…积习难改。 - Turix
请注意,如果parent是一个LinearLayout,则无论之前的顺序如何,child都将成为第一个;在Marshmallow 6.0.1上进行了尝试。 - Attacktive
它刚刚消失了。 - user924

46

没有sendToBack()方法。但是,如果您在低位置视图上调用bringToFront(),几乎可以获得相同的效果。


这是一个有些模糊的回答,没有提供示例,并且没有记录在调用后必须使视图无效。与其请求得分,为什么不改进答案以赢得它们呢?至少在@noob正确发布之前,这将是处理它的方法。 - Abandoned Cart
1
你的意思是在需要的视图之前获取所有视图,然后调用sendToBack吗?这太疯狂了。 - user924
很有道理,不需要再解释了 ;-) - PYK

37

据我所知,这方面没有内置的解决方案。我猜你是想修改 FrameLayout 中的 Z 轴顺序或者类似的东西。

然而,我认为你可以修改布局中包含的子元素的顺序。removeChild...() 和 addView() 方法可以带有位置值,因此你可以交换子元素的位置,从而修改 Z 轴顺序。但是这似乎是一种比较 hacky 的解决方案。

或者考虑修改子视图的可见性属性,你可能会得到类似的效果,并且更加清晰简洁。

编辑:

在新版本的 Android(即 L Developer Preview)中,似乎终于可以轻松地更改 View 的 Z 轴顺序了。'Elevation' 和 'TranslationZ' 属性来拯救: https://developer.android.com/preview/material/views-shadows.html


1
我同意。我已经看到了,确实不能为具有子视图的视图调用 bringToFront()。首先,你必须使用 removeAllViews()setVisibility(View.GONE) 或类似的方法。然后才能使用 addView()setVisibility(View.Visible) 将子视图带回来。 - AlexAndro
你能帮我实现这个吗? - MMakati
这并不是我所面临的问题,但是它给了我一个解决问题的想法(移除和添加视图解决了我的问题)。谢谢。 - avijendr

19

调用bringToFront()方法使要置于前台的视图处于最前面,然后调用所有视图(包括要置于前台的视图)的invalidate()方法。对所有监听器重复此过程。

因此,当另一个视图的监听器被调用时,先前的视图将被使无效并移到后台。


请查看此帖子以获取更多信息:- http://abhishek347.wordpress.com/2012/02/22/android-development-bringtofront-and-sendtoback-on-view/ - 0xC0DED00D
有什么区别? - gumuruh

9
这是我使用的方法将View发送到后台(与bringToFront相反,有点像sendToBack):
    private void moveToBack(View myCurrentView) 
    {
        ViewGroup myViewGroup = ((ViewGroup) myCurrentView.getParent());
        int index = myViewGroup.indexOfChild(myCurrentView);
        for(int i = 0; i<index; i++)
        {
            myViewGroup.bringChildToFront(myViewGroup.getChildAt(i));
        }
    }

希望这能帮到你!

这里有一个 bug,假设我有三张图片。如果我将第二张图片移到后面,它可以正常工作。但是如果我移动第三张图片(在第二张图片之后),第三张图片会被移到后面,但第二张图片会被移到前面。 - MMakati
这太疯狂了,它会导致所有的布局/视图设计崩溃。 - user924

2

它像bringToBack()一样正常工作。

public void moveToBack(ViewGroup viewGroup, View v) {
    int s = 1;
    for (int i = 1; i < viewGroup.getChildCount(); i++) {
        if (viewGroup.getChildAt(1) == v) {
            s = 2;
        } else {
            viewGroup.getChildAt(s).bringToFront();
        }
    }
}

你能详细描述一下你的代码是如何工作的吗? - Stevoisiak

2
在您的XML文件中,必须按顺序编写以下内容:
<FrameLayout
android:id="@+id/frameLayoutXML"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
<ImageView
 android:id="@+id/imageViewXML"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<android.support.design.widget.FloatingActionButton
android:id="@+id/floatB_XML"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</FrameLayout>

在这种情况下,imageView 将位于 FloatingActionButton 的后面。
如果您想进行一些更改,并且您将通过编程实现,则:
public class SomeActivity extends AppCompatActivity {

    //declare variables
    ImageView imageView;
    FrameLayout frameLayout;
    FloatingActionButton floatingActionButtonNew;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        //Here is to call what already exists at XML file
        frameLayout=findViewById(R.id.frameLayoutXML);   
        imageView = findViewById(R.id.imageViewXML);
        //Here is to declare a new Element to add to the view, but is not at XML File
        floatingActionButtonNew=New FloatingActionButton(this);
        frameLayout.removeAllViews();  //here you remove all views.
        frameLayout.addView(imageView);//And you add again the imageView first
        floatingActionButtonNew.setLayoutParams(new 
         RelativeLayout.LayoutParams(
          RelativeLayout.LayoutParams.WRAP_CONTENT,
          RelativeLayout.LayoutParams.WRAP_CONTENT
        )); //Then you can give some format to your floatingButtonNew

        //And finally  add to the frameLayout the new View in this case the floatingButtonNew
        frameLayout.addView(floatingActionButtonNew);
    }
}

在这些视图声明的顺序中,您将再次从浮动按钮获得图像。因此,首先声明的视图将回到第二个声明的视图,而实现顺序的关键是使用FrameLayoutLinearLayoutremoveAllViews方法,然后在编程添加视图时再次添加这些视图。


2
如果您正在使用appCompat库,那么在低于21 API级别的设备上更改Z顺序还有另一种方法:
ViewCompat.setTranslationZ(view, zValue);

2
请注意,从 API level 21 开始,您可以使用 view.setZ(float)。您可以在这里找到更多信息。

1

您还可以尝试使用ViewGroup.addViewInLayout()方法的功能,该方法接受一个索引,确定视图应该位于列表的末尾还是开头


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