How to overlay a view in a constraintlayout?

2019-06-15 02:41发布

I have this layout of my login activity. I want to overlay progressBar as like it can be done using FrameLayout. How to do this using 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>

It looks like this:

enter image description here

I am looking for solution which should work for API level 19+. I don't want to add more hierarchy in my layout by wrapping Button or ProgressBar inside ViewGroup or so.

9条回答
手持菜刀,她持情操
2楼-- · 2019-06-15 02:52

Here is your API 19 solution. It puts a CircularProgressDrawable in an overlay on top of your ConstraintLayout.

This is what it looks like:

enter image description here

What you have to do is:

  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();
        }
    }
    
查看更多
女痞
3楼-- · 2019-06-15 02:55

You can achieve your goal by setting the Z translation for the view.

Put this method in a helper class (for example: UIUtils) and use it for your view:

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

USAGE:

UIUTils.setTranslationZ(YOUR_VIEW, 5);
查看更多
欢心
4楼-- · 2019-06-15 02:58

Set an elevation on the ProgressBar 2dp seems to work.

android:elevation="2dp"

You could also try setting translationZ as suggested in the answer to a similar question. For me this works on an emulator running API 17 and the progress bar appeared on top as expected. If you get any warning than check your build version

查看更多
何必那么认真
5楼-- · 2019-06-15 03:03

There are two options, in each case you add one attribute to your <ProgressBar/> tag. It is either

android:translationZ="2dp"

or

android:elevation="2dp"

API level must be >= 21.

查看更多
爷、活的狠高调
6楼-- · 2019-06-15 03:12

In my observations Android "stacks" the views along the z-axis in the order they appear in the xml file in that the view at the start of the file will be at the top of the z-axis and the view at the end of the file will be at the bottom. Of course once you start setting elevation and zTranslation etc the z-axis stack order will be affected...

so moving the progressBar declaration to the top of the ConstraintLayout should make it appear above the other views, this has worked for me.

查看更多
Emotional °昔
7楼-- · 2019-06-15 03:13

I want to provide you with an alternative to the XML solution.
You can also add a view programmatically to your root view. (ConstraintLayout)

ViewGroupOverlay is an extra layer that sits on top of a ViewGroup (the "host view") which is drawn after all other content in that view (including the view group's children). Interaction with the overlay layer is done by adding and removing views and drawables.

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

If you reference the root in your code you can then add your ProgressBar.
Something like this:

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

You can also check out this link for extra info.
And this SO question can also help.

查看更多
登录 后发表回答