如何在ConstraintLayout中叠加视图?

32

我有一个登录活动的布局。我想要像使用 FrameLayout 那样,叠加 progressBar。如何使用 ConstraintLayout 实现这一点?

<layout>

    <data>

        <variable
            name="vm"
            type="com.app.android.login.vm" />
    </data>

    <ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fillViewport="true"
        tools:context="com.app.android.login.LoginActivity"
        tools:ignore="missingPrefix">

        <android.support.constraint.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="@dimen/default_view_margin_bottom_8dp">

            <android.support.design.widget.TextInputLayout
                android:id="@+id/til_login_email"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/default_view_margin_right_8dp"
                android:layout_marginStart="@dimen/default_view_margin_left_8dp"
                android:textColorHint="@color/colorSecondaryText"
                app:hintTextAppearance="@style/AppTheme.InputLayoutStyle"
                app:layout_constraintBottom_toTopOf="@+id/til_login_password"
                app:layout_constraintTop_toTopOf="parent"
                app:layout_constraintVertical_chainStyle="packed">

                <android.support.design.widget.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/login_email"
                    android:imeOptions="actionNext"
                    android:singleLine="true"
                    android:text="@={vm.emailField}"
                    android:textColor="@color/colorPrimaryText" />
            </android.support.design.widget.TextInputLayout>

            <android.support.design.widget.TextInputLayout
                android:id="@+id/til_login_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/default_view_margin_right_8dp"
                android:layout_marginStart="@dimen/default_view_margin_left_8dp"
                android:textColorHint="@color/colorSecondaryText"
                app:hintTextAppearance="@style/AppTheme.InputLayoutStyle"
                app:layout_constraintBottom_toTopOf="@+id/btn_login_login"
                app:layout_constraintTop_toBottomOf="@+id/til_login_email"
                app:layout_constraintVertical_chainStyle="packed">

                <android.support.design.widget.TextInputEditText
                    android:layout_width="match_parent"
                    android:layout_height="wrap_content"
                    android:hint="@string/login_password"
                    android:imeOptions="actionDone"
                    android:inputType="textPassword"
                    android:singleLine="true"
                    android:text="@={vm.passwordField}"
                    android:textColor="@color/colorPrimaryText" />
            </android.support.design.widget.TextInputLayout>

            <Button
                android:id="@+id/btn_login_login"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/default_view_margin_right_8dp"
                android:layout_marginStart="@dimen/default_view_margin_left_8dp"
                android:layout_marginTop="48dp"
                android:onClick="@{vm::login}"
                android:text="@string/login_btn_text"
                android:textColor="@color/colorWhite"
                app:layout_constraintBottom_toTopOf="@+id/textview_login_forgot_password"
                app:layout_constraintTop_toBottomOf="@+id/til_login_password"
                app:layout_constraintVertical_chainStyle="packed" />

            <TextView
                android:id="@+id/textview_login_forgot_password"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/default_view_margin_right_8dp"
                android:layout_marginStart="@dimen/default_view_margin_left_8dp"
                android:layout_marginTop="36dp"
                android:gravity="center"
                android:text="@string/login_forgot_password"
                app:layout_constraintBottom_toTopOf="@+id/btn_login_register"
                app:layout_constraintTop_toBottomOf="@+id/btn_login_login"
                app:layout_constraintVertical_chainStyle="packed" />

            <Button
                android:id="@+id/btn_login_register"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:layout_marginEnd="@dimen/default_view_margin_right_8dp"
                android:layout_marginStart="@dimen/default_view_margin_left_8dp"
                android:text="@string/login_sign_up"
                android:textColor="@color/colorWhite"
                app:layout_constraintBottom_toBottomOf="parent" />

            <ProgressBar
                android:id="@+id/progressBar"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:visibility="@{vm.progressVisibility}"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintLeft_toLeftOf="parent"
                app:layout_constraintRight_toRightOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        </android.support.constraint.ConstraintLayout>
    </ScrollView>
</layout>

看起来是这样的:

enter image description here

我正在寻找适用于API级别19+的解决方案。 我不想通过将ButtonProgressBar包装在ViewGroup中来增加我的布局层次结构。

14个回答

47

有两个选项,在每种情况下,您都需要将一个属性添加到您的<ProgressBar/>标签中。它可以是

android:translationZ="2dp"
或者
android:elevation="2dp"

API级别必须≥21。


@AjayS 是的,我添加了一个适用于API 19的解决方案。 - kalabalik
@AjayS 如需更多信息,请参考此链接https://dev59.com/HFcP5IYBdhLWcg3weJ1R#44378384 - JJ86
"android:stateListAnimator ="@null" 这需要API级别为21及更高" - Ajay S
没错。我漏了那个。我编辑了我的答案,会继续思考一个 API 级别为 19 的解决方案。 - kalabalik

19

如果您想要完全覆盖目标视图,请将覆盖视图的所有边缘限制为目标视图的相应边缘,并将覆盖视图的高度和宽度设置为0dp,如下所示:

    <View
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="@id/animation_view"
        app:layout_constraintLeft_toLeftOf="@id/animation_view"
        app:layout_constraintRight_toRightOf="@id/animation_view"
        app:layout_constraintTop_toTopOf="@id/animation_view"/>

以下是一个可工作的示例。在下面的图像中,将一个红色的遮罩放置在图像上方。XML跟随在图像之后。

输入图像描述

<android.support.constraint.ConstraintLayout 
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/animation_view"
        android:layout_width="250dp"
        android:layout_height="250dp"
        android:src="@mipmap/ic_launcher"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <View
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="#AAFF0000"
        app:layout_constraintBottom_toBottomOf="@id/animation_view"
        app:layout_constraintLeft_toLeftOf="@id/animation_view"
        app:layout_constraintRight_toRightOf="@id/animation_view"
        app:layout_constraintTop_toTopOf="@id/animation_view" />
</android.support.constraint.ConstraintLayout>

查看文档,了解更多有关ConstraintLayout的信息。


我按照你在这里建议的更改进行了修改 https://pastebin.com/842ma87r,但它没有覆盖 LottieAnimationView - Ajay S
谢谢。你能试试我的布局,看看为什么它与我的布局不兼容吗? - Ajay S
@AjayS 这是你在更新的答案中从pastebin复制的代码,除了我使用了ImageView而不是我没有的LottieAnimationView,并添加了一个蒙版以便我可以看到覆盖层。 - Cheticamp
我的意思是,你能否尝试使用我在问题中提供的代码。我相信你会遇到同样的问题。 - Ajay S
@AjayS 我不确定你想要的结果是什么。我几天内无法访问开发环境,所以无法进一步调查此事。 - Cheticamp
刚刚添加了一个高度到 View 以使其工作 android:elevation="2dp" - Shailendra Madda

6

ProgressBar上设置2dp的高程似乎有效。

android:elevation="2dp"

您还可以尝试设置translationZ,如答案中建议的那样,针对类似问题。对我来说,在运行API 17的模拟器上,这个方法有效,并且进度条按预期出现在顶部。如果出现任何警告,请检查您的构建版本。

5
在我观察中,Android会按照xml文件中视图出现的顺序沿z轴将它们“堆叠”,因此文件末尾处的视图将出现在z轴顶部,而文件开头处的视图将位于底部。当然,一旦您开始设置高度和zTranslation等参数,z轴堆栈顺序将受到影响... 因此,将进度条声明移动到ConstraintLayout的末尾应该使其显示在其他视图之上,这对我有效。

1
根据顺序进行编程似乎不够安全,因为 XML 可能会因为错误或 IDE 的操作而被重新排序。 - RamPrasadBismil

2
这是您的API 19解决方案。它在您的ConstraintLayout上方叠加了一个CircularProgressDrawable
它看起来像这样:

enter image description here

您需要做的是:
  1. Get rid of the XML ProgressBar.

  2. Give your XML ConstraintLayout an id, for example:

    android:id="@+id/cl"
    
  3. Add this code to your MainActivity:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
        boolean toggle;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            final ConstraintLayout cl = findViewById(R.id.cl);
            cl.setOnClickListener(this);
        }
    
        @Override
        public void onClick(final View view) {
            toggle ^= true;
            if (toggle) {
                startProgress();
            } else {
                stopProgress();
            }
        }
    
        void startProgress() {
            final ConstraintLayout cl = findViewById(R.id.cl);
            final CircularProgressDrawable progressDrawable = new CircularProgressDrawable(this);
            progressDrawable.setColorSchemeColors(Color.MAGENTA);
            progressDrawable.setCenterRadius(50f);
            progressDrawable.setStrokeWidth(12f);
            progressDrawable.setStrokeCap(Paint.Cap.BUTT);
            cl.post(new Runnable() {
                @Override
                public void run() {
                    progressDrawable.setBounds(0, 0, cl.getWidth(), cl.getHeight());
                    cl.getOverlay().add(progressDrawable);
                }
            });
            progressDrawable.start();
        }
    
        void stopProgress() {
            final ConstraintLayout cl = findViewById(R.id.cl);
            final CircularProgressDrawable progressDrawable = new CircularProgressDrawable(this);
            progressDrawable.setColorSchemeColors(Color.MAGENTA);
            progressDrawable.setCenterRadius(50f);
            progressDrawable.setStrokeWidth(12f);
            progressDrawable.setStrokeCap(Paint.Cap.BUTT);
            cl.post(new Runnable() {
                @Override
                public void run() {
                    cl.getOverlay().clear();
                }
            });
            progressDrawable.stop();
        }
    }
    

1
我希望为您提供一种替代XML解决方案。
您还可以通过编程方式向根视图添加视图。 (ConstraintLayout)

ViewGroupOverlay 是一个额外的层,位于 ViewGroup(“宿主视图”)之上,在该视图中所有其他内容绘制完成后绘制(包括视图组的子项)。与覆盖层的交互是通过添加和删除视图和可绘制对象来完成的。

<android.support.constraint.ConstraintLayout
            android:id="@+id/root_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:paddingBottom="@dimen/default_view_margin_bottom_8dp">

如果您在代码中引用根目录,那么您就可以添加您的 ProgressBar
类似这样的形式:
rootLayout.overlay.add(ProgressBar(context).apply {
    measure(View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY), View.MeasureSpec.makeMeasureSpec(100, View.MeasureSpec.EXACTLY))
    layout(0, 0, 100, 100)
})

你还可以查看这个 链接 获取更多信息。
这个 SO 问题也可以帮助。

1
你可以尝试以下其中一种选项: 1. 在你的布局外面添加一个相对布局,如这里
 <RelativeLayout
   android:id="@+id/relativelayout_progress"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   android:visibility="gone"
   android:background="#aa000022" >

   <ProgressBar 
       android:layout_centerInParent="true"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:indeterminateOnly="true" />

</RelativeLayout>
  1. 在您的活动 onCreate 中添加视图叠加层,如此处所述Android 4.3用户界面视图叠加层

1
我不想在我的布局中添加更多的层级。 - Ajay S

1
另一种解决方法是将Button更改为View,更改背景和大小。用TextView覆盖它以替换旧的Button文本,并使用另一个View进行后续可点击操作。

我不想为此在我的布局中增加更多的层级。 - Ajay S

0

我使用额外的图像将覆盖层添加到图像视图布局中。 另外,将额外的覆盖层图像视图放置在主要图像视图的正下方。

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

    <androidx.cardview.widget.CardView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:cardCornerRadius="@dimen/spacing_small"
        android:layout_marginTop="@dimen/spacing_normal">

        <androidx.constraintlayout.widget.ConstraintLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <androidx.appcompat.widget.AppCompatImageView
                android:id="@+id/imageViewBlog"
                android:layout_width="match_parent"
                android:layout_height="@dimen/blog_post_height"
                android:scaleType="centerCrop"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/ic_placeholder" />

            <androidx.appcompat.widget.AppCompatImageView
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:scaleType="fitXY"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent"
                app:srcCompat="@drawable/black_overlay" />

            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/tvBlogTopTitle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginBottom="@dimen/spacing_small"
                android:ellipsize="end"
                android:fontFamily="sans-serif-medium"
                android:maxLines="1"
                android:textColor="@color/pale_grey"
                app:layout_constraintBottom_toTopOf="@+id/tvBlogBigTitle"
                app:layout_constraintEnd_toEndOf="@+id/tvBlogBigTitle"
                app:layout_constraintStart_toStartOf="@id/tvBlogBigTitle"
                tools:text="Travel Inspiration" />

            <androidx.appcompat.widget.AppCompatTextView
                android:id="@+id/tvBlogBigTitle"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginStart="@dimen/spacing_normal"
                android:layout_marginLeft="@dimen/spacing_normal"
                android:layout_marginEnd="@dimen/spacing_normal"
                android:layout_marginRight="@dimen/spacing_normal"
                android:layout_marginBottom="@dimen/spacing_normal"
                android:ellipsize="end"
                android:maxLines="2"
                android:textColor="@color/white"
                android:textSize="@dimen/font_tiny"
                app:layout_constraintBottom_toBottomOf="@+id/imageViewBlog"
                app:layout_constraintEnd_toEndOf="@id/imageViewBlog"
                app:layout_constraintStart_toStartOf="@id/imageViewBlog"
                tools:text="This Photographer is Selling Prints of Her Wildlife Photos to \n in the Masai Mara…" />

        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.cardview.widget.CardView>

看起来像下面这张图片。

enter image description here

叠加图像:

enter image description here


0
您可以通过为视图设置Z翻译来实现您的目标。
将此方法放入助手类中(例如:UIUtils),并将其用于您的视图:
/**
 * Set the 'Z' translation for a view
 *
 * @param view         {@link View} to set 'Z' translation for
 * @param translationZ 'Z' translation as float
 */
public static void setTranslationZ(View view, float translationZ) {
    ViewCompat.setTranslationZ(view, translationZ);
}

用法:

UIUTils.setTranslationZ(YOUR_VIEW, 5);

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