【问题标题】:How to overlay a view in a constraintlayout?如何在约束布局中覆盖视图?
【发布时间】:2018-04-18 20:05:42
【问题描述】:

我的登录活动有这个布局。我想覆盖progressBar,就像可以使用FrameLayout 一样。如何使用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>

看起来像这样:

我正在寻找适用于 API 级别 19+ 的解决方案。我不想通过将Button 或ProgressBar 包裹在ViewGroup 中来在我的布局中添加更多层次结构。

【问题讨论】:

    标签: android android-constraintlayout


    【解决方案1】:

    有两种选择,在每种情况下,您都向&lt;ProgressBar/&gt; 标记添加一个属性。要么是

    android:translationZ="2dp"
    

    或

    android:elevation="2dp"
    

    API 级别必须 >= 21。

    【讨论】:

    • @AjayS 是的,我添加了一个也适用于 API 19 的解决方案。
    • @AjayS 了解更多信息,请参阅stackoverflow.com/a/44378384/2013835
    • "android:stateListAnimator="@null" 这需要 API 级别 21 或更高
    • 没错。我错过了。我编辑了我的答案,并将继续考虑 api-level-19 解决方案。
    【解决方案2】:

    如果要 100% 覆盖目标视图,请将覆盖视图的所有边约束到目标视图的相应边,并将覆盖视图的高度和宽度设置为 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>
    

    有关更多信息,请参阅documentation ConstraintLayout。

    【讨论】:

    • 我按照您在pastebin.com/842ma87r 的建议进行了更改,但它没有覆盖LottieAnimationView
    • 谢谢。您可以尝试使用我的布局,看看为什么它不适用于我的布局吗?
    • @AjayS 这是您在更新后的答案中来自 pastebin 的代码,除了我使用 ImageView 而不是 LottieAnimationView,我没有并添加了一个稀松布,这样我就可以看到覆盖了。
    • 我的意思是。你能试试我在问题中的代码吗?我相信你会面临同样的问题
    • @AjayS 我不确定您正在寻找的结果。我将有几天无法访问开发环境,因此我无法进一步调查此事。
    【解决方案3】:

    在ProgressBar 2dp 上设置海拔似乎可行。

    android:elevation="2dp"
    

    您也可以尝试按照answer 中的建议将translationZ 设置为similar question。对我来说,这适用于运行 API 17 的模拟器,并且进度条按预期显示在顶部。如果您收到任何警告,请检查您的构建版本

    【讨论】:

      【解决方案4】:

      在我的观察中,Android 按照它们在 xml 文件中出现的顺序沿 z 轴“堆叠”视图,因为文件末尾的视图将位于 z 轴的顶部,而视图位于文件的开头将位于底部。当然,一旦你开始设置高程和 zTranslation 等,z 轴堆栈顺序就会受到影响......

      所以将 progressBar 声明移动到 ConstraintLayout 的末尾应该使它出现在其他视图之上,这对我有用。

      【讨论】:

      • 取决于顺序似乎不够安全,因为 xml 可能会被错误或被 IDE 重新排序
      【解决方案5】:

      这是您的 API 19 解决方案。它将CircularProgressDrawable 置于ConstraintLayout 之上的叠加层中。

      这就是它的样子:

      你要做的是:

      1. 摆脱 XML ProgressBar。

      2. 给你的 XML ConstraintLayout 一个 id,例如:

        android:id="@+id/cl"
        
      3. 将此代码添加到您的 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();
            }
        }
        

      【讨论】:

        【解决方案6】:

        我想为您提供 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)
        })
        

        您也可以查看此link 了解更多信息。
        这个SO 问题也可以提供帮助。

        【讨论】:

          【解决方案7】:

          您可以尝试以下选项之一: 1. 在你的布局之外添加一个相对布局为here

           <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 User Interface View overlays

          【讨论】:

          • 我不想为此在布局中添加更多层次结构。
          【解决方案8】:

          解决此问题的另一个简单方法是将Button 更改为View,更改背景和大小。用TextView 覆盖它以替换旧的Button 文本和另一个View 以供稍后点击

          【讨论】:

          • 我不想为此在布局中添加更多层次结构。
          【解决方案9】:

          您可以通过为视图设置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);
          

          【讨论】:

            【解决方案10】:

            2020 年更新解决方案

            Kotlin 语言

            我有两种情况,第一种只是普通按钮,第二种是 阻止点击动作的按钮中心进度

            如果你想知道我如何处理按钮和进度条,这里只是简码

            如果您想了解,请使用这两种方法

            private fun disableButton() {
                button.run {
                    preservedButtonText = text.toString()
                    text = ""
                    isClickable = true
                    isEnabled = false
                }
                progressBar.visibility = View.VISIBLE
            
            }
            
            private fun enableButton() {
                button.run {
                    text = preservedButtonText
                    isClickable = false
                }
                progressBar.visibility = View.INVISIBLE
            }
            

            给你一个惊喜的类可以完成你刚刚使用的所有东西 我处理创建父约束布局,它是孩子的​​(按钮,进度条) 以编程方式

             /**
             * Created by Amirahmad Adibi.
             * XProject | Copyrights 2019-12-16.
             */
            
            class ZProgressButton(context: Context, var attributeSet: AttributeSet) :
                ConstraintLayout(context, attributeSet) {
                var isLoading: Boolean = false
                    set(value) {
                        handelShowing(value)
                        field = value
                    }
            
                var preservedButtonText: String = ""
                var button: Button
                var progressBar: ProgressBar
            
                init {
                    resetPadding()
            
                    button = getButton(context, attributeSet)
                    progressBar = getProgressBar(context, attributeSet)
                    preservedButtonText = button.text.toString()
            
                    addView(button)
                    addView(progressBar)
                    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
                    progressBar.elevation = 2f
                    ViewCompat.setTranslationZ(progressBar, 5f)
                    ViewCompat.setTranslationZ(button, 1f)
                    handleConstraint(this, button, progressBar)
                }
            
                private fun handleConstraint(
                    view: ConstraintLayout,
                    button: Button,
                    progressBar: ProgressBar
                ) {
                    with(ConstraintSet()) {
                        clone(view)
                        connect(button.id, ConstraintSet.RIGHT, view.id, ConstraintSet.RIGHT)
                        connect(button.id, ConstraintSet.LEFT, view.id, ConstraintSet.LEFT)
                        connect(button.id, ConstraintSet.TOP, view.id, ConstraintSet.TOP)
                        connect(button.id, ConstraintSet.BOTTOM, view.id, ConstraintSet.BOTTOM)
                        connect(progressBar.id, ConstraintSet.RIGHT, view.id, ConstraintSet.RIGHT)
                        connect(progressBar.id, ConstraintSet.LEFT, view.id, ConstraintSet.LEFT)
                        connect(progressBenter code herear.id, ConstraintSet.TOP, view.id, ConstraintSet.TOP)
                        connect(progressBar.id, ConstraintSet.BOTTOM, view.id, ConstraintSet.BOTTOM)
                        applyTo(view)
                    }
                }
            
                private fun getButton(context: Context, attributeSet: AttributeSet): Button {
                    return ZButton(context, attributeSet).run {
                        id = View.generateViewId()
                        text = "text"
                        layoutParams = ViewGroup.LayoutParams(
                            ViewGroup.LayoutParams.MATCH_PARENT,
                            ViewGroup.LayoutParams.MATCH_PARENT
                        )
                        isClickable = true
                        return@run this
                    }
                }
            
                private fun getProgressBar(context: Context, attributeSet: AttributeSet): ProgressBar {
                    return ProgressBar(context, attributeSet).run {
                        id = View.generateViewId()
                        visibility = View.VISIBLE
                        layoutParams = ViewGroup.LayoutParams(
                            ViewGroup.LayoutParams.WRAP_CONTENT,
                            ViewGroup.LayoutParams.WRAP_CONTENT
                        )
                        setPadding(4, 4, 4, 4)
                        return@run this
                    }
                }
            
            
                fun resetPadding() {
                    setPadding(0, 0, 0, 0)
                }
            
            
                fun handelShowing(value: Boolean) {
                    removeView(button)
                    removeView(progressBar)
                    if (value) {
                        disableButton()
                    } else {
                        enableButton()
                    }
                    addView(button)
                    addView(progressBar)
                }
            
                private fun disableButton() {
                    button.run {
                        preservedButtonText = text.toString()
                        text = ""
                        isClickable = true
                        isEnabled = false
                    }
                    progressBar.visibility = View.VISIBLE
            
                }
            
                private fun enableButton() {
                    button.run {
                        text = preservedButtonText
                        isClickable = false
                    }
                    progressBar.visibility = View.INVISIBLE
                }
            }
            

            用法:

            buttonLoading.setOnClickListener {
                progressButton.isLoading = true
            }
            buttonDone.setOnClickListener {
                progressButton.isLoading = false
            }
            

            【讨论】:

              【解决方案11】:

              android:elevation 在 API 级别 19 中不起作用。将使用一个简单的技巧来使用卡片视图并设置此卡片的高度

               <androidx.cardview.widget.CardView
                      android:layout_width="match_parent"
                      android:layout_height="wrap_content"
                      android:background="@color/white"
                      android:padding="@dimen/padding_10"
                      app:contentPadding="16dp"/>
              

              【讨论】:

                【解决方案12】:

                对于上述情况,我是这样解决的

                <FrameLayout
                        android:layout_marginTop="16dp"
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content">
                
                        <ProgressBar
                            android:id="@+id/progress_reset_password"
                            android:visibility="visible"
                            android:layout_width="wrap_content"
                            android:layout_height="wrap_content"
                            android:indeterminateTint="@color/colorAccent"
                            android:layout_gravity="center_horizontal"
                            android:translationZ="2dp"
                            android:elevation="2dp"/>
                
                        <Button
                            android:id="@+id/btnGetOTP"
                            style="@style/styleButton"
                            android:layout_width="match_parent"
                            android:layout_height="wrap_content"
                            android:layout_marginLeft="80dp"
                            android:layout_marginRight="80dp"
                            android:text="@string/check_email"
                            android:textColor="@color/colorWhite"
                            android:textSize="16sp"
                            android:textStyle="bold"
                            app:layout_constraintEnd_toEndOf="parent"
                            app:layout_constraintStart_toStartOf="parent"
                            app:layout_constraintTop_toTopOf="parent" />
                
                
                    </FrameLayout>
                

                Open the image above here

                【讨论】:

                • 请解释你的代码是做什么的以及它是怎么做的。
                【解决方案13】:

                我使用额外的图像在图像视图布局中添加了叠加层。此外,将额外的叠加图像视图放在主图像视图的正下方。

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

                如下图所示。

                叠加图:

                【讨论】:

                  【解决方案14】:

                  这里有很多关于如何在 xml 中设置 z 顺序的示例,非常棒。但是,如果您最终需要以编程方式调整视图叠加层,而这些视图恰好是按钮,请确保在您的 xml 布局文件中将 stateListAnimator 设置为 null。 stateListAnimator 是 android 的底层单击按钮时调整按钮的翻译Z的引擎盖过程,因此被单击的按钮最终在顶部可见。这并不总是您想要的...对于完整的 Z 顺序控制,请执行以下操作:

                  【讨论】:

                    猜你喜欢
                    • 2018-10-16
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 2019-08-28
                    • 2015-06-20
                    • 1970-01-01
                    • 1970-01-01
                    • 2017-10-09
                    相关资源
                    最近更新 更多