How to overlay a view in a constraintlayout? - android

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.

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.

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.

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

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.

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

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.

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

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

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

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
}

android:elevation is not working in API level 19. A simple trick will be used to use a card view and set the elevation of this card
<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"/>

With the above case, I fixed it this way
<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

I added overlays to the image view layout using an extra image. Also, put the extra overlay image view just immediately below the main image view.
<?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>
Looks like the below picture.
Overlay image:

There are lots of examples here on how to set z order in xml, which are great. However, if you if you end up needing to programmatically adjust view overlays, and those views happen to be buttons, make sure to set stateListAnimator to null in your xml layout file. stateListAnimator is android's under-the-hood process to adjust translationZ of buttons when they are clicked, so the button that is clicked ends up visible on top. This is not always what you want... for full Z order control, do this:

Related

Top-level Layout in Android XML is ALWAYS FrameLayout?

I was trying to set height of ConstraintLayout programmatically and I intended to use ConstraintLayout.LayoutParams. ConstraintLayout is a top-level layout in this XML. but I had weird experience when I set height from old to new with ConstraintLayout.LayoutParams and
ClassCastException occurred.
java.lang.ClassCastException: android.widget.FrameLayout$LayoutParams cannot be cast to androidx.constraintlayout.widget.ConstraintLayout$LayoutParams
the detail code is below.
Dialog
private fun initLayoutSize() = with(binding) {
dialog?.let {
val height = getDisplayHeight(it.window!!.windowManager)
val params = homeBottomSheetContainer.layoutParams as ConstraintLayout.LayoutParams // excpeiton here!!
params.height = (height * 0.3).toInt()
homeBottomSheetContainer.layoutParams = params
}
}
XML
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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:id="#+id/homeBottomSheetContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingHorizontal="#dimen/basic16"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<View
android:id="#+id/homeBottomSheetTopView"
android:layout_width="#dimen/basic30"
android:layout_height="#dimen/basic03"
android:layout_marginTop="#dimen/basic12"
android:background="#drawable/bottomsheet_top_radius_view"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/homeBottomLottoSearchNotice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/basic12"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:gravity="end"
android:padding="#dimen/basic04"
android:text="#string/ask_search"
android:textColor="#color/black"
android:textSize="#dimen/txt12"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#id/homeBottomSheetTopView"
tools:visibility="invisible" />
<EditText
android:id="#+id/homeLottoSearch"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:hint="#string/input_lotto_round"
android:imeOptions="actionDone"
android:inputType="number"
android:maxLines="1"
android:textColor="#color/black"
android:textCursorDrawable="#drawable/shape_edittext_cursor_black"
android:textSize="#dimen/txt12"
android:visibility="invisible"
app:backgroundTint="#color/black"
app:layout_constraintBottom_toBottomOf="#id/homeBottomLottoSearchNotice"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/homeBottomLottoSearchNotice" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/homeBottomSheetLottoRV"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="#dimen/basic12"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/homeBottomLottoSearchNotice"
tools:listitem="#layout/item_lotto_all_round" />
</androidx.constraintlayout.widget.ConstraintLayout>
what happend?
I didn't use any FrameLayout in my XML and I couldn't find it anywhere. but why the android system tells me about FrameLayout? is FrameLayout a default top-level layout in Android View XML???
So, i changed the code with below one. and It worked!
private fun initLayoutSize() = with(binding) {
dialog?.let {
val height = getDisplayHeight(it.window!!.windowManager)
val params = homeBottomSheetContainer.layoutParams as FrameLayout.LayoutParams // change here!!
params.height = (height * 0.3).toInt()
homeBottomSheetContainer.layoutParams = params
}
}
But I'm so confused now. anyone help? and thank you in advance. :)
if you are setting only height then you may use more general ViewGroup.LayoutParams. all layout params are extending it, so there is no chance for class cast, no matter of parent
val params = homeBottomSheetContainer.layoutParams as ViewGroup.LayoutParams
besides that you may use is keyword for checking instance type

Unable to match parent the custom dialog's width

I am have created a custom dialog and defined the width as match_parent. But when I ran the application, it appears not even wrapping the content as most of the content is getting clipped. I tried some of the solutions but getting the same result, I have no idea what am I doing wrong:
dialog_messages.xml
<com.google.android.material.card.MaterialCardView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:cardCornerRadius="#dimen/corner"
app:cardElevation="#dimen/elevation"
app:cardUseCompatPadding="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawableLeft="#drawable/ic_msg_sm"
android:drawablePadding="#dimen/normal"
android:drawableTint="#color/colorDanger"
android:padding="#dimen/normal_2x"
android:text="Send Message"
android:textColor="#color/colorBlack"
android:textStyle="bold" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#color/colorGrey" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/normal_2x"
android:text="Tap a message to send"
android:textColor="#color/colorGrey"
android:textSize="#dimen/font_sm1" />
<ListView
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="#dimen/normal_2x" />
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:background="#color/colorGrey" />
<Button
android:id="#+id/btnCancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginBottom="#dimen/normal"
android:background="#null"
android:paddingHorizontal="#dimen/normal_4x"
android:text="Cancel"
android:textColor="#color/colorDanger" />
</LinearLayout>
</com.google.android.material.card.MaterialCardView>
and the setup class
MessageDialog.kt
class MessageDialog(
private val activity: Activity,
private val onMessageClickListener: MessageClickListener
) : Dialog(activity) {
private val messageList = arrayListOf<String>(
"Can't locate you. Send instructions",
"Heavy Traffic. I'll be late 10 minutes",
"Just turning around the block",
"Taxi arrived",
"I am coming to your location",
"I am arriving in 5 minutes",
"Ok"
)
private lateinit var listView: ListView
private lateinit var btnCancel: Button;
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestWindowFeature(Window.FEATURE_NO_TITLE)
setContentView(R.layout.dialog_messages)
val adapter = ArrayAdapter(activity, android.R.layout.simple_list_item_1, messageList)
listView = findViewById(R.id.listView)
listView.adapter = adapter
listView.setOnItemClickListener { parent, view, position, id ->
onMessageClickListener.onMessageClick(messageList.get(position))
dismiss()
}
btnCancel = findViewById(R.id.btnCancel)
btnCancel.setOnClickListener {
dismiss()
}
}
}
But this is the result:
I have tried some other answers already provided here, but nothing works.
Create a Dialog object and do this in the show() part.
val dialog = MessageDialog(activity, messageClickListener)
val lp = WindowManager.LayoutParams()
lp.copyFrom(dialog.window!!.attributes)
lp.width = WindowManager.LayoutParams.MATCH_PARENT
lp.height = WindowManager.LayoutParams.WRAP_CONTENT
dialog.show()
val window = dialog.window
window!!.attributes = lp
For my case, i was using afollestad material dialog library, and it had default margin. I overrided that margin on my dimens.xml file
<dimen name="md_dialog_horizontal_margin">8dp</dimen>
Here 8dp means, dialog both from left and right margin will be 16 dp so if you want margin to be 16dp type it as 8dp, divided by two :)

FloatingActionButton - how to make button have a rounded image and no background?

I have the following ViewHolder layout -
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/transparent">
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/row_product_page_ghost_floating_action_bar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:layout_marginTop="10dp"
android:alpha="0.5"
android:background="#color/grey_text_cart"
android:backgroundTint="#color/grey_text_cart"
android:elevation="1dp"
android:scaleType="center"
android:src="#drawable/user_icon"
app:backgroundTint="#color/white"
app:fabCustomSize="50dp"
app:fabSize="auto"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:maxImageSize="30dp" />
</androidx.constraintlayout.widget.ConstraintLayout>
override fun onBindViewHolder(holder: ProductPageGhostHolder, position: Int) {
holder.floatingGhostButton.setOnClickListener {
listener.onClick(position)
}
val jid = models[position].JID
val roster = VerteDatabaseManager.ROSTER.getRoster(jid)
if (roster != null && roster.vcard != null) {
Picasso.get().load(roster.vcard.image).into(holder.floatingGhostButton)
}
}
so I want each FloatingActionButton to have it's own image which is rounded to 50px and without any other background. Here is the current result I have in my UI -
As you can see in the second floating bar (which is the one that comes from the RV) the image is not circle like it's background and it's circle background has another background itself also.
How can I fix it?

Add view to constraintLayout with constraints similar to another child

I have a constraint layout (alpha9) with views spread all over it, and I have one particular ImageView that I need to replicate and add more of like it.
The layout is like so :
The main purpose is to animate 5 of those "coin" imageViews when the button is pressed. The imageViews i need to generated sha'll have exact properties of the the imageView behind the Button down below (with same constraints)
What i tried to do is the following :
private ImageView generateCoin() {
ImageView coin = new ImageView(this);
constraintLayout.addView(coin,-1,originCoinImage.getLayoutParams());//originCoinImage is the imageView behind the button
coin.setImageResource(R.drawable.ic_coin);
coin.setScaleType(ImageView.ScaleType.FIT_XY);
coin.setVisibility(View.VISIBLE);
return coin;
}
And it failed. EDIT: It failed to show what i wanted , sometimes it shows a coin in the top left of the screen , sometimes it doesn't show anything but either way , the number of coins increments (meaning the animation is trying to actually do something and then calling the onAnimationFinished method)
I used for animation the library EasyAndroidAnimations
the code is as follows :
MainActivity.java
#OnClick(R.id.addCoin)
public void addCoin() {
// for (int x = 0 ; x < 5 ; x++){
final View newCoin = generateCoin();
sendCoin(newCoin, 300, new AnimationListener() {
#Override
public void onAnimationEnd(com.easyandroidanimations.library.Animation animation) {
incrementCoins();
constraintLayout.removeView(newCoin);
shakeCoin(targetCoinImage, 200, null);
}
});
// }
}
private void incrementCoins() {
coins++;
coinNumber.setText(String.valueOf(coins));
}
private void sendCoin(View coinView, long duration, AnimationListener listener) {
new TransferAnimation(coinView)
.setDestinationView(targetCoinImage)
.setDuration(duration)
.setInterpolator(new AccelerateDecelerateInterpolator())
.setListener(listener)
.animate();
}
private void shakeCoin(View coinView, long duration, AnimationListener listener) {
new ShakeAnimation(coinView)
.setDuration(duration)
.setShakeDistance(6f)
.setNumOfShakes(5)
.setListener(listener)
.animate();
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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:id="#+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="seaskyways.canvasanddrawtest.MainActivity">
<TextView
android:id="#+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Coins : "
android:textSize="30sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/coinNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="16dp"
android:layout_marginStart="8dp"
android:text="0"
android:textColor="#color/colorPrimary"
android:textSize="50sp"
android:textStyle="normal|bold"
app:layout_constraintBottom_toBottomOf="#+id/textView"
app:layout_constraintLeft_toRightOf="#+id/textView"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="#+id/textView" />
<ImageView
android:id="#+id/coinOrigin"
android:layout_width="50dp"
android:layout_height="50dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="#+id/addCoin"
app:layout_constraintLeft_toLeftOf="#+id/addCoin"
app:layout_constraintTop_toTopOf="#+id/addCoin"
app:srcCompat="#drawable/ic_coin"
app:layout_constraintRight_toRightOf="#+id/addCoin" />
<ImageView
android:id="#+id/coinTarget"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginLeft="16dp"
android:layout_marginStart="16dp"
android:scaleType="fitXY"
app:layout_constraintBottom_toBottomOf="#+id/coinNumber"
app:layout_constraintLeft_toRightOf="#+id/coinNumber"
app:layout_constraintTop_toTopOf="#+id/coinNumber"
app:srcCompat="#drawable/ic_coin" />
<Button
android:id="#+id/addCoin"
android:layout_width="0dp"
android:layout_height="75dp"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:layout_marginStart="16dp"
android:text="Button"
android:textAlignment="center"
android:textSize="36sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" />
</android.support.constraint.ConstraintLayout>
ConstraintLayout.LayoutParams cache its parameters, and is associated to the widget you use it on, so you cannot simply pass one widget's layoutParams to another.
You have to instead generate a new layoutParams for your new object, and copy the corresponding fields.
For example, if you have something like:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
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:id="#+id/content_main"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:text="Button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/button"
android:layout_marginTop="16dp"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginStart="16dp"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginLeft="16dp" />
</android.support.constraint.ConstraintLayout>
Then you could do:
ConstraintLayout layout = (ConstraintLayout) findViewById(R.id.content_main);
ConstraintLayout.LayoutParams params =
(ConstraintLayout.LayoutParams) button.getLayoutParams();
Button anotherButton = new Button(this);
ConstraintLayout.LayoutParams newParams = new ConstraintLayout.LayoutParams(
ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
newParams.leftToLeft = params.leftToLeft;
newParams.topToTop = params.topToTop;
newParams.leftMargin = params.leftMargin;
newParams.topMargin = params.topMargin;
layout.addView(anotherButton, -1, newParams);
This would put the second button exactly in the same place as the one defined in XML.

Expand and collapse CardView

What is the proper way to expand a CardView?
Use an expandable list view with cardview
or even
You can use wrap content as height of cardview and use
textview inside it below title, so on click make the textview visible
and vice-versa.
but isn't it bad design ?
nope it isn't if you give some transition or animation when it's expanded
or collapsed
If you want to see some default transition then just write android:animateLayoutChanges="true" in parent layout.
If you are using CardViews inside a ListView or RecyclerView see my answer for recommended way of doing it:
RecyclerView expand/collapse items
If you are just using CardView then do this in your on onClickListener of cardview:
TransitionManager.beginDelayedTransition(cardview);
detailsView.setVisibility(View.VISIBLE);
By default keep the visibility of your detailsView to be GONE in your xml.
I used a cardview and an expand section item_description in the cardview. For the expand part I created a TextView below the header section (LinearLayout/item_description_layout) and when the user clicks on the header layout an expand/collapse method is called. Here is the code in the cardview:
<LinearLayout
android:id="#+id/item_description_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:minHeight="48dp"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:orientation="horizontal">
<TextView
android:id="#+id/item_description_title"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.9"
android:gravity="center_vertical"
android:text="#string/description"/>
<ImageView
android:id="#+id/item_description_img"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.1"
android:layout_gravity="center_vertical|end"
app:srcCompat="#drawable/ic_expand_more_black_24dp"/>
</LinearLayout>
<TextView
android:id="#+id/item_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingStart="16dp"
android:paddingEnd="16dp"
android:paddingBottom="16dp"
android:gravity="center_vertical"
android:visibility="gone"
tools:text="description goes here"/>
Here is the method that is called. I also added a ObjectAnimator to handle the expand/collapse animation of the block. This is a simple animation that uses the length of the description text.
void collapseExpandTextView() {
if (mItemDescription.getVisibility() == View.GONE) {
// it's collapsed - expand it
mItemDescription.setVisibility(View.VISIBLE);
mDescriptionImg.setImageResource(R.drawable.ic_expand_less_black_24dp);
} else {
// it's expanded - collapse it
mItemDescription.setVisibility(View.GONE);
mDescriptionImg.setImageResource(R.drawable.ic_expand_more_black_24dp);
}
ObjectAnimator animation = ObjectAnimator.ofInt(mItemDescription, "maxLines", mItemDescription.getMaxLines());
animation.setDuration(200).start();
}
just a line of code before setting visibility GONE/ VISIBLE can do:
TransitionManager.beginDelayedTransition([the rootView containing the cardView], new AutoTransition());
no need to use the animateLayoutChanges=true in XML (this way also simple, but collapse behaviour is bad)
I originally tried doing this by just adding an extra View to the bottom of my CardView and setting its visibility to gone (gone vs invisible):
tools:visibility="gone"
Then, I set the CardView height to Wrap Content and added an icon with an onClickListener that changed the extra View's visibility to visible.
The issue with this was that even when the visibility was set to gone, my CardView was still behaving like the extra View was there.
To get around this, I explicitly set the visibility of the extra View to gone in my onBindViewHolder method:
holder.defPieces.visibility = View.GONE
After this, the expandable functionality worked how I wanted it to. I wonder if there's some odd order of operations that goes on when inflating a View to display in a RecyclerView.
mView.Click +=(sender, e) =>{
LinearLayout temp = mView.FindViewById<LinearLayout>(Resource.Id.LinerCart);
if (vs == false) {
temp.Visibility = ViewStates.Gone;
vs = true;
} else {
temp.Visibility = ViewStates.Visible;
vs = false;
}
};
I got the solution ( single cardview expandable listview )
check this link
http://www.devexchanges.info/2016/08/expandingcollapsing-recyclerview-row_18.html
if you add down arrow icon
you just use my code
create xml
<RelativeLayout
android:id="#+id/layout_expand"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:orientation="vertical">
<TextView
android:id="#+id/item_description_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#color/white"
android:clickable="true"
android:onClick="toggle_contents"
android:padding="10dp"
android:text="Guest Conditions"
android:textColor="#color/hint_txt_color"
android:fontFamily="sans-serif-medium"
android:textStyle="normal"
android:paddingBottom="15dp"
android:textSize="16dp"/>
<ImageView
android:layout_alignParentRight="true"
android:paddingTop="4dp"
android:paddingRight="10dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_keyboard_arrow_down"/>
<!--content to hide/show -->
<TextView
android:id="#+id/item_description"
android:layout_below="#+id/item_description_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/white"
android:padding="10dp"
android:text="#string/about_txt2"
android:textColor="#color/hint_txt_color"
android:fontFamily="sans-serif-medium"
android:textStyle="normal"
android:paddingBottom="15dp"
android:visibility="gone"
android:textSize="12dp" />
</RelativeLayout>
</android.support.v7.widget.CardView>
///////////////////////////////////////////////
Mainactivity.java
RelativeLayout layout_expand = (RelativeLayoutfindViewById(R.id.layout_expand);
item_description = (TextView) findViewById(R.id.item_description);
TextView item_description_title;
item_description_title = (TextView) findViewById(R.id.item_description_title);
item_description.setVisibility(View.GONE);
///////////////////////////////////////////////////////////////////
animationUp = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_up);
animationDown = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.slide_down);
layout_expand.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(item_description.isShown()){
item_description.setVisibility(View.GONE);
item_description.startAnimation(animationUp);
}
else{
item_description.setVisibility(View.VISIBLE);
item_description.startAnimation(animationDown);
}
}
});
item_description_title.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(item_description.isShown()){
item_description.setVisibility(View.GONE);
item_description.startAnimation(animationUp);
}
else{
item_description.setVisibility(View.VISIBLE);
item_description.startAnimation(animationDown);
}
}
});

Categories

Resources