问题
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:
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.
回答1:
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.
回答2:
If you want to 100% overlay the target view, constrain all the sides of the overlaying view to the corresponding sides of the target view and set the height and width of the overlaying view to 0dp
like the following:
<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"/>
Here is a working example. In the following image, a red scrim is placed over an image. The XML follows the image.
<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>
See the documentation for ConstraintLayout
for more information.
回答3:
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
回答4:
Here is your API 19 solution. It puts a CircularProgressDrawable
in an overlay on top of your ConstraintLayout
.
This is what it looks like:
What you have to do is:
Get rid of the XML
ProgressBar
.Give your XML
ConstraintLayout
an id, for example:android:id="@+id/cl"
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(); } }
回答5:
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 end of the file will be at the top of the z-axis and the view at the start 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 end of the ConstraintLayout should make it appear above the other views, this has worked for me.
回答6:
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.
回答7:
You could try one of these options: 1. Add a relative layout outside your layout as 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>
- Add a view overlay in your activity's onCreate as described here Android 4.3 User Interface View overlays
回答8:
Another easy way to solve this is to change Button
to View
, change background and size. Overlay it with TextView
for replacing old Button
text and another View
for clickable later
回答9:
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);
回答10:
Updated Solution for 2020
Kotlin language
I have 2 scenarios, the first is just a normal button and the second one is progress on the center of the button with preventing the click action
here is just shortcode if you want to know how I handle button and progress bar
Use jsut these two method if you want get an idea
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
}
and surprise is had for you a class that does all the stuff you just use it I handle create parent constraint layout and it's child's (button, progress bar) programmatically
/**
* 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
}
}
Usage :
buttonLoading.setOnClickListener {
progressButton.isLoading = true
}
buttonDone.setOnClickListener {
progressButton.isLoading = false
}
来源:https://stackoverflow.com/questions/47140693/how-to-overlay-a-view-in-a-constraintlayout