Constraint layout barrier not working as expected - android

I have 2 views and I need a barrier below but the barrier does not work as expected.
Here is my layout.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/textView15"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="This is a text view"
app:layout_constraintEnd_toStartOf="#+id/t1"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/t1"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/textView15"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is a demo text to check wrap content"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.constraintlayout.widget.Barrier
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="textView15,t1"/>
</androidx.constraintlayout.widget.ConstraintLayout>
The black dotted line is the barrier.
This might be a bug or I am doing it wrong, The result is same in preview and actual device

If you specify
app:layout_optimizationLevel="none"
in the XML for the ConstraintLayout, you will find that the barrier will be placed correctly. I am not sure what setting the optimization level achieves, but it has been an issue recently with barriers. (ConstraintLayout version 2.1.3).
Here is how the layout looks before suppressing optimization. The barrier rises up into the right TextView as noted.
We suppress optimization by stating in the XML with no other changes:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_optimizationLevel="none"
xmlns:app="http://schemas.android.com/apk/res-auto">
Now the layout looks like this:
The barrier has dropped below the right TextView where it belongs.
This is with ConstraintLayout version 2.1.3.
implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
(It seems that setting optimization level to anything but standard solves this problem.)

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="#+id/textView15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
android:text="This is a text view"
app:layout_constraintEnd_toStartOf="#+id/t1"
android:textSize="20sp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/t1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/textView15"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.textfield.TextInputEditText
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is a demo text to check wrap content"/>
</com.google.android.material.textfield.TextInputLayout>
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="textView15,t1"/>
<Space
android:id="#+id/space"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:visibility="visible"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#id/barrier" />
</androidx.constraintlayout.widget.ConstraintLayout>

As was written before, ConstraintLayout versions 2.1.3 and 2.1.4 work incorrectly, you can downgrade it to 2.0.0. If not, you can use two ways. In both cases remove <androidx.constraintlayout.widget.Barrier>.
Insert LinearLayout to occupy needed views. For instance, if you have 2 TextInputLayouts in horizontal position, you can write so:
<LinearLayout
android:id="#+id/layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/upper_view">
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/input_layout_1"
android:layout_weight="1"
...
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/input_layout_2"
android:layout_weight="1"
...
</LinearLayout>
If you have a complex layout with different views, write so.
<Space
android:id="#+id/space"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="#id/input_layout_2"
app:layout_constraintTop_toBottomOf="#+id/upper_view" />
Then you should change Space height in code. Probably you can apply this code to all TextInputLayouts.
// View size change listener
private fun View.onSizeChange(callback: () -> Unit) {
addOnLayoutChangeListener(object : OnLayoutChangeListener {
override fun onLayoutChange(
view: View?,
left: Int,
top: Int,
right: Int,
bottom: Int,
oldLeft: Int,
oldTop: Int,
oldRight: Int,
oldBottom: Int,
) {
view?.removeOnLayoutChangeListener(this)
if (right - left != oldRight - oldLeft || bottom - top != oldBottom - oldTop) {
callback()
}
}
})
}
binding.inputLayout2.onSizeChange {
// First remove bottom constraint
val layoutParams = binding.space.layoutParams as ConstraintLayout.LayoutParams
layoutParams.bottomToBottom = ConstraintLayout.LayoutParams.UNSET
// Now set height of a <Space> (maximum of two TextInputLayouts)
val h1 = binding.inputLayout1.height
val h2 = binding.inputLayout2.height
binding.inputLayout2.postDelayed({
binding.space.updateLayoutParams { height = max(h1, h2) }
}, 1)
}
Then bind bottom views to LinearLayout or Space instead of Barrier.

Related

How to stop Text View from increasing in width from one side in Android

I am new to android studio & have encountered an issue. I have a constraint layout which has an image view & a text view. I have attached the screenshot of the constraint layout.
The text view contains the current climate of a location. For eg , when the climate is "rainy" , it shows rainy in the center. The issue I am encountering it that If the climate is "scattered clouds" or " heavy showers" (Basically if the text's length is relatively bigger) , it's no longer centered. It feels like text view in increasing in width from the left side but I want it to increase from the right side with start letter of text to be in the center position always. How do I Implement it
My code for the constraint layout ->
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/ivTemp"
android:layout_width="#dimen/_20sdp"
android:layout_height="#dimen/_20sdp"
android:src="#drawable/te"
android:contentDescription="#string/temperature"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintEnd_toStartOf="#id/tvClimate"
app:layout_constraintHorizontal_bias=".96"
android:layout_marginTop="#dimen/_11sdp"
app:layout_constraintStart_toStartOf="parent"/>
<TextView
android:id="#+id/tvClimate"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_marginTop="#dimen/_10sdp"
android:fontFamily="#font/aldrich"
android:gravity="center"
android:layout_marginEnd="#dimen/_110sdp"
android:paddingBottom="#dimen/_10sdp"
android:textSize="18sp"
tools:text="Scattered Clouds 20°C" />
</androidx.constraintlayout.widget.ConstraintLayout>
I want it to increase from the right side with start letter of text
to be in the center position always
If I understood correctly, you want your view to look somewhat like this:
That can be achieved using vertical guideline in middle of screen:
<?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="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.Guideline
android:id="#+id/vertical_guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
<ImageView
android:id="#+id/iv_img"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:src="#android:drawable/ic_lock_lock"
app:layout_constraintEnd_toStartOf="#id/vertical_guideline"
app:layout_constraintTop_toTopOf="parent"
app:tint="#android:color/holo_blue_dark" />
<TextView
android:id="#+id/tv_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="8dp"
android:text="my very very very very very very very very very very very very very long text"
android:textAlignment="textStart"
android:textColor="#000"
android:textSize="12sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/vertical_guideline"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Add an empty <View> in the centre and constrain the <TextView> to that <View>

Enable button after user scrolled to the bottom of ScrollView Android

I wanna achieve the following be behavior for one screen of my app.
I have a fragment's layout with ConstraintLayout as it's parent. Inside ConstraintLayout I have a ScrollView with nested ConstraintLayout (nested ConstraintLayout contains ImageView and TextView) and simple Button below the ScrollView.
I wanna enable button as soon as user reaches to the bottom of ScrollView and disable when user scrolls up.
Layout is below.
<?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="match_parent"
android:layout_height="match_parent"
xmlns:tools="http://schemas.android.com/tools"
>
<ScrollView
android:id="#+id/scrollableView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:fillViewport="true"
app:layout_constraintBottom_toTopOf="#id/elevationShadow"
app:layout_constraintTop_toBottomOf="#id/appbar">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_marginStart="#dimen/spacing_large"
android:layout_marginEnd="#dimen/spacing_large"
android:layout_height="wrap_content">
<ImageView
android:id="#+id/user_image"
android:layout_width="144dp"
android:layout_height="144dp"
android:layout_gravity="center"
android:layout_marginTop="#dimen/spacing_large"
android:src="#drawable/user_image"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
/>
<TextView
android:id="#+id/heading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/text_size_xxlarge"
android:textStyle="bold"
android:textAlignment="center"
android:textColor="#color/black_color"
android:layout_marginTop="#dimen/spacing_large"
tools:text="Tools text"
android:textAppearance="?tvptTextAppearanceBody"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/user_image"
/>
<TextView
android:id="#+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/spacing_large"
android:textAlignment="center"
android:textSize="#dimen/user_info_content_text_size"
android:textAppearance="?tvptTextAppearanceBody"
android:textColor="#color/black"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/heading"
tools:text="Tools test content"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>
<View
android:id="#+id/elevationShadow"
android:layout_width="match_parent"
android:layout_height="3dp"
android:background="#drawable/shadow_elevation"
app:layout_constraintEnd_toEndOf="parent"
android:layout_marginBottom="#dimen/user_info_activity_confirm_button_margin"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="#id/button_confirm"/>
<com.travelportdigital.android.compasswidget.button.PercentageBasedStateButton
android:id="#+id/button_confirm"
style="#style/PrimaryButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#android:color/transparent"
android:layout_marginBottom="#dimen/user_info_activity_confirm_button_margin"
android:text="#string/user_info_continueButton_title"
android:textAllCaps="true"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The problem is that content for TextView which is inside ScrollView can both long and short. That's why I had to add ScrollView if the content is long.
By the small piece of code I was able to achieve the behavior I needed with one small remark.
fun addScrollChangeListener() {
scrollView.viewTreeObserver
.addOnScrollChangedListener {
enableContinueButton(scrollView.getChildAt(0).bottom <= scrollView.height + scrollView.scrollY)
}
}
And the code above works fine for the scenario when content is long (when user arrives to this screen Continue button is disabled and when user scrolls to the bottom of the scroll view it become enabled if user scrolls up it becomes disabled again.
I wanna update this logic to enable button when user arrives to this screen and content of TextView inside ScrollView is short (no need scrolling for this scenario).
I made some researches in Google and could not find the solution which would work for me.
In onViewCreated() method I added logic to disable or enable button when user arrives to this screen.
enableContinueButton(!isScrollingRequired())
I tried this implementation
private fun isScrollingRequired(): Boolean {
val view = scrollView.getChildAt(scrollView.childCount - 1) as View
val diff = view.bottom - (scrollView.height + scrollView.scrollY)
return diff != 0
}
and this
return if (child != null) {
val childHeight = child.height
scrollView.height <= childHeight + scrollView.paddingTop + scrollView.paddingBottom;
} else {
false
}
but it did not work, because ScrollView height and it's child height is always 0
Looking forward your advices.
Regards,
Alex
I dont know is this what you want to do but it should be one of the solution.
I think you can just simply add the button in the "ScrollView" so when user scrolls at the bottom, user will see the button and when user scrolls up, user cannot press the button as well.
Below layout .XML works for me, Using ScrollView with ConstraintLayout:
(you may need extra dependencies)
<ScrollView
android:id="#+id/msg_scroll"
android:layout_width="0dp"
android:layout_height="200dp"
android:fillViewport="true"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="#+id/infoSumm">
<!--Display the <ScrollView> under <TextView>"#+id/infoSumm" -->
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/inside_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="#+id/infoDetail"
android:text=""
android:layout_marginTop="12dp"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxHeight="240dp"
app:layout_constraintTop_toTopOf="#id/inside_scroll"
app:layout_constraintStart_toStartOf="parent"
tools:text="Info Detail"/>
<com.google.android.material.button.MaterialButton
android:id="#+id/btn_infoClose"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/default_field_spacing"
android:backgroundTint="#color/colorPrimary"
android:textAppearance="#style/TextAppearance.MaterialComponents.Button"
android:textAlignment="center"
android:textStyle="bold"
android:textAllCaps="false"
android:textSize="20sp"
android:textColor="#color/colorWhite"
android:text="#string/btn_Close"
android:paddingTop="10dp"
android:paddingBottom="10dp"
app:cornerRadius="25dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="#+id/infoDetail"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</ScrollView>

NestedScrollView not expanding in height inside AlertDialog

I am currently using a scroll view inside an Alert Dialog and need the scroll view to grow in height until the dialog reaches its maximum default height. It's a bit tough for me to explain so i have attached an illustration to help. Hope it does.
The issue i'm getting is the scrollview does not grow in height and even if i remove the app:layout_constraintBottom_toTopOf="#id/linearLayout4", it grows however the bottom part will be partially hidden by the button layout. This is my current code :
<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/filter_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/dialog_box"
android:minHeight="300dp"
android:elevation="12dp">
<TextView
android:id="#+id/filter_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="#string/filter"
style="#style/element_header"
android:textColor="#color/textColorDark"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/filter_reset_btn"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:text="#string/reset"
style="#style/text_info_nBold"
android:textSize="14sp"
android:textColor="#color/textColorDark"
app:layout_constraintBottom_toBottomOf="#+id/filter_header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/filter_header" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintBottom_toTopOf="#id/linearLayout4"
app:layout_constraintTop_toBottomOf="#id/filter_header"
app:layout_constraintVertical_bias="0.0">
<androidx.core.widget.NestedScrollView
android:id="#+id/filter_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true"
android:layout_marginTop="16dp"
android:scrollbarFadeDuration="1000">
<!-- VIEWS INSIDE HERE -->
</androidx.core.widget.NestedScrollView>
</LinearLayout>
<LinearLayout
android:id="#+id/linearLayout4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/dialog_bottombar_layout"
android:orientation="horizontal"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="#+id/dialog_secondary_button"
style="#style/low_emphasis_btn"
android:layout_width="0dp"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:backgroundTint="#color/textColorDark"
android:text="#string/dialog_cancel"
android:textColor="#color/textColorDark"
android:visibility="visible" />
<Button
android:id="#+id/dialog_primary_button"
style="#style/high_emphasis_btn"
android:layout_width="0dp"
android:layout_weight="1"
android:text="#string/apply" />
</LinearLayout>
Any help would be appreciated :)
Alert dialogs tend to wrap their content or can be forced to be full screen. A size in between the optimizes the screen real estate takes a some work, but it is not impossible.
One approach is to let the system lay out the alert dialog but, before it is displayed, use a ViewTreeObserver.OnGlobalLayoutListener to examine the resulting size of the dialog. In the layout listener, the size of the dialog can be adjusted up to fit the contents of the scrolling view or adjusted up to full screen if the scrolling view contents are too large for the screen.
Here is a demo app that shows how this can be done. Comments in the code explain more.
MainActivity.kt
class MainActivity : AppCompatActivity(), View.OnClickListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
override fun onClick(v: View) {
val text = when (v.id) {
R.id.customDialogShort -> getString(R.string.short_string)
R.id.customDialogMedium -> getString(R.string.lorem_medium)
else -> getString(R.string.lorem_long)
}
// Specifying the viewGroup as a parent to the inflater makes no difference.
val dialogView = LayoutInflater.from(v.context).inflate(R.layout.con_custom_view, null, false) as ConstraintLayout
(dialogView.findViewById(R.id.textView) as TextView).text = text
val alertDialog = AlertDialog.Builder(this).setView(dialogView).create()
val decorView = alertDialog.window!!.decorView
decorView.setBackgroundResource(R.drawable.alert_dialog_background)
// We need a layout pass to determine how big everything is and needs to be. Place a hook
// at the end of the layout process to examine the layout before display.
decorView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
decorView.viewTreeObserver.removeOnGlobalLayoutListener(this)
// Find out how much of the scrolling view is usable by its child.
val scrollingView: NestedScrollView = decorView.findViewById(R.id.filter_scroll)
val scrollingViewPadding = scrollingView.paddingTop + scrollingView.paddingBottom
val scrollingUsableHeight = scrollingView.height - scrollingViewPadding
// If the child view fits in the scrolling view, then we are done.
val childView = scrollingView.getChildAt(0)
if (childView.height <= scrollingUsableHeight) {
return
}
// Child doesn't currently fit in the scrolling view. Resize the top-level
// view so the child either fits or is forced to scroll because the maximum
// height is reached. First, find out how much space is allowed by the decor view.
val displayRectangle = Rect()
decorView.getWindowVisibleDisplayFrame(displayRectangle)
val decorViewPadding = decorView.paddingTop + decorView.paddingBottom
val decorUsableHeight = displayRectangle.height() - decorViewPadding - scrollingViewPadding
// Compute the height of the dialog that will 100% fit the scrolling content and
// reduce it if it won't fit in the maximum allowed space.
val heightToFit = dialogView.height + childView.height - scrollingUsableHeight
dialogView.minHeight = min(decorUsableHeight, heightToFit)
}
})
var buttonOk: Button = dialogView.findViewById(R.id.dialog_primary_button)
buttonOk.setOnClickListener { alertDialog.dismiss() }
buttonOk = dialogView.findViewById(R.id.dialog_secondary_button)
buttonOk.setOnClickListener { alertDialog.dismiss() }
alertDialog.show()
}
}
activity_main.xml
<LinearLayout
android:id="#+id/parent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<Button
android:id="#+id/customDialogShort"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Short text" />
<Button
android:id="#+id/customDialogMedium"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Medium text" />
<Button
android:id="#+id/customDialogLong"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="onClick"
android:text="Long text" />
</LinearLayout>
con_custom_view
Custom Layout for the AlertDialog.
<TextView
android:id="#+id/filter_header"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="#string/filter"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/filter_reset_btn"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:background="?attr/selectableItemBackgroundBorderless"
android:text="#string/reset"
android:textSize="14sp"
app:layout_constraintBottom_toBottomOf="#+id/filter_header"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="#+id/filter_header" />
<androidx.core.widget.NestedScrollView
android:id="#+id/filter_scroll"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_marginTop="16dp"
android:padding="16dp"
android:scrollbarFadeDuration="1000"
app:layout_constraintBottom_toTopOf="#id/linearLayout4"
app:layout_constraintTop_toBottomOf="#id/filter_header">
<TextView
android:id="#+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="#string/lorem_long" />
</androidx.core.widget.NestedScrollView>
<LinearLayout
android:id="#+id/linearLayout4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<Button
android:id="#+id/dialog_secondary_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_weight="1"
android:text="#string/dialog_cancel"
android:visibility="visible" />
<Button
android:id="#+id/dialog_primary_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="#string/apply" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

constraint layout - Two views with largest width

I want to create a layout (using constraint layout) like the following:
In different languages Button1 may be larger than button2. How can I do this?
I've only been able to achieve this using a LinearLayout inside the constraint that contains the two buttons, but I'm trying to use only a layout.
Thanks
Update
I want to mention that Remq's answer works and gets around the self-reference problem I noted below where a view's width is determined by the location of a barrier that uses the same view as a referenced id. The key seems to be the specification of app:layout_constraintWidth_min="wrap" for both views. It is unclear to me why this works, or that it will continue to work, but I wanted to make note of it
Update #2
I took another look at why Remq's answer works. My experience is that without specifying app:layout_constraintWidth_min="wrap" for the views, both views collapse to zero width. Once the views measure out as zero width, app:layout_constraintWidth_min="wrap" grows them again so the contents are wrapped. This is just what I surmise and have no proof that this is what is going on, but it makes sense.
I have seen questions akin to this one on Stack Overflow a number of times. These questions never have a satisfactory answer IMO (including ones that I have answered.) The difficulty is that there is a dependency problem since one view depends on the width of another but that other view depends on the width of the first. We fall into a referential quandary. (Forcing widths programmatically is always an option but seems undesirable.)
Another and, probably, better approach is to use a custom ConstraintHelper that will inspect the sizes of the referenced views and adjust the width of all views to the width of the largest.
The custom ConstraintHelper is placed in the XML for the layout and references the effected views as in the following sample layout:
activity_main
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="Button1"
app:layout_constraintBottom_toTopOf="#+id/button2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed" />
<Button
android:id="#+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Button2 may be larger"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.498"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/button1" />
<com.example.constraintlayoutlayer.GreatestWidthHelper
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="button1,button2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
The custom ConstraintHelper looks like this:
GreatestWidthHelper
class GreatestWidthHelper #JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : ConstraintHelper(context, attrs, defStyleAttr) {
override fun updatePostMeasure(container: ConstraintLayout) {
var maxWidth = 0
// Find the greatest width of the referenced widgets.
for (i in 0 until this.mCount) {
val id = this.mIds[i]
val child = container.getViewById(id)
val widget = container.getViewWidget(child)
if (widget.width > maxWidth) {
maxWidth = widget.width
}
}
// Set the width of all referenced view to the width of the view with the greatest width.
for (i in 0 until this.mCount) {
val id = this.mIds[i]
val child = container.getViewById(id)
val widget = container.getViewWidget(child)
if (widget.width != maxWidth) {
widget.width = maxWidth
// Fix the gravity.
if (child is TextView && child.gravity != Gravity.NO_GRAVITY) {
// Just toggle the gravity to make it right.
child.gravity = child.gravity.let { gravity ->
child.gravity = Gravity.NO_GRAVITY
gravity
}
}
}
}
}
}
The layout displays as shown below.
I stumbled upon this question and was able to solve it with androidx.constraintlayout.widget.Barrier which were introduced in 1.1.0-beta1 of constraint layout. So I thought it'd be nice to share with people who run into the same issue:
<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="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/button1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:gravity="center"
android:text="Button1"
app:layout_constraintBottom_toTopOf="#+id/button2"
app:layout_constraintEnd_toEndOf="#id/barrierEnd"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintWidth_min="wrap" />
<Button
android:id="#+id/button2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="center"
android:text="Button2 may be larger"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="#id/barrierEnd"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/button1"
app:layout_constraintWidth_min="wrap" />
<androidx.constraintlayout.widget.Barrier
android:id="#+id/barrierEnd"
android:layout_width="0dp"
android:layout_height="0dp"
app:barrierDirection="end"
app:constraint_referenced_ids="button1,button2"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Haven't been able to center them horizontally, so if someone figures that out I'll update this example.
As per what I understand from your question, below code will help you.
<?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="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world"
app:layout_constraintBottom_toTopOf="#+id/text2"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello world"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/text1" />
</androidx.constraintlayout.widget.ConstraintLayout>

How to use wrap_content with a maximum width?

I am trying to layout a view that should wrap its content, but it shouldn't be more than ~100dp less than its parent width. How can I do that using a RelativeLayout or some other layout? What I have right now will always make the view 100dp less than its parent so that there is space for another view.
This picture is an example of what I have:
As you can see, the text doesn't fill the whole box, so it could be smaller. But, it should never be larger than 100dp less than its parent, so that there is room for the time the message was sent.
This is my layout.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:orientation="vertical"
android:paddingBottom="10dp"
android:paddingRight="#dimen/horizontalMargin"
android:paddingTop="10dp">
<TextView
android:id="#+id/message_holder"
android:layout_toLeftOf="#id/blank"
android:layout_alignParentLeft="true"
android:layout_marginLeft="#dimen/horizontalMargin"
android:background="#drawable/message_corners"
style="#style/white_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alsdkjf; alsdkf" />
<RelativeLayout
android:id="#+id/blank"
android:layout_width="wrap_content"
android:layout_height="1dp"
android:layout_alignParentRight="true"
android:minWidth="100dp">
</RelativeLayout>
<TextView
android:minWidth="100dp"
android:id="#+id/time"
style="#style/gray_text"
android:layout_toRightOf="#id/message_holder"
android:paddingLeft="10dp"
android:text="Yesterday,\n11:30 PM" />
<RelativeLayout
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignBottom="#id/message_holder"
android:layout_alignParentLeft="true"
android:background="#drawable/triangle" />
</RelativeLayout>
I have tried using the "minWidth" property on a blank view to the right of the message box to provide spacing, but it doesn't resize to be larger (which would make the message box smaller). When I don't have the blank view, and simply place the time TextView to the right of the message box, then that TextView isn't visible when the message box expands.
Update:
This is my "message_corners.xml":
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<solid
android:color="#color/green" >
</solid>
<padding
android:left="10dp"
android:top="10dp"
android:right="10dp"
android:bottom="10dp" >
</padding>
<corners
android:radius="10dp">
</corners>
</shape>
Update 2:
This is what I am looking for in a layout with short text:
And this is what I am looking for in a layout with long text:
Here you go, a layout that does exactly what you want.
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipChildren="false"
android:paddingBottom="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp">
<RelativeLayout
android:id="#+id/blank"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#aaaaaa">
<LinearLayout
android:id="#+id/message_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="100dp">
<TextView
android:id="#+id/message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello?"
android:background="#00ff00" />
</LinearLayout>
<TextView
android:id="#+id/time"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_toRightOf="#id/message_container"
android:layout_marginLeft="-100dp"
android:text="12:30 PM"
android:background="#ff0000" />
</RelativeLayout>
</RelativeLayout>
Short message
Long message
I know this is a really old question, but it's a frustrating problem I've encountered several times now and the existing answers weren't quite what I was looking for. Some colleagues and I came up with the following:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:background="#FFFFFF">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#888888"
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#00FF00"
tools:text="Short message."/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="0"
android:background="#CCCCCC"
tools:text="Yesterday,\n11:30pm"/>
</LinearLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:background="#888888"
android:padding="10dp"
android:orientation="horizontal">
<TextView
android:id="#+id/message_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#00FF00"
tools:text="Super ultra mega awesome long message which is going to help us take over the world."/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:layout_weight="0"
android:background="#CCCCCC"
tools:text="Yesterday,\n11:31pm"/>
</LinearLayout>
</LinearLayout>
Which looks like this when rendered:
The magic seems to be the zero value for the weight of the text box on the right (in addition to the non-zero weight value of the text box on the left, which some of the other answers already have).
Honestly, I can't explain exactly why it works, but after having looked for a solution to this for so long I'm not questioning it! :)
As an aside, I like this approach because it doesn't require any explicit or minimum widths, any intermediate wrapper views, or the use of clipping settings, margins, padding, etc. to implement view overlay.
What the author of this question really asks is, how to let the TextView expand to fit the message inside of it without overflowing the time TextView, and without leaving blank spaces.
Since you don't actually know the width of the whole screen, you can't tell your TextView to be 100dp less than it.
What you should do is wrap your TextView in a container which will have the toLeftOf rule, with the TextView only wrapping it's contents. This way, the container will expand all the way up to the right (without overflowing the time TextView) but the TextView will only wrap it's text contents (so, it won't extend any blank spaces)
Code
Instead of
<TextView
android:id="#+id/message_holder"
android:layout_toLeftOf="#id/blank"
android:layout_alignParentLeft="true"
android:layout_marginLeft="#dimen/horizontalMargin"
android:background="#drawable/message_corners"
style="#style/white_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alsdkjf; alsdkf" />
Use
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#id/blank"
android:layout_alignParentLeft="true"
android:layout_marginLeft="#dimen/horizontalMargin">
<TextView
android:id="#+id/message_holder"
android:background="#drawable/message_corners"
style="#style/white_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="alsdkjf; alsdkf" />
</LinearLayout>
By the way, your layout isn't very good. You should optimize it.
You can try the following arrangement of views and their widths:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="6dp"
>
<FrameLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#android:color/holo_blue_bright"
tools:text="Some long test is this which is support to wrap at the end of parent view"
/>
</FrameLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
tools:text="Yesterday,\n 11:30 PM"
/>
</LinearLayout>
Sat Sri Akal
This can also be achieved using ConstraintLayout
with 2 children in horizontal chain
1st child
layout width 0
constraint weight 1
constraint max width wrap
2nd child
layout width wrap content
A solution with ConstraintLayout using
app:layout_constrainedWidth
layout_constraintHorizontal_bias
layout_constraintHorizontal_chainStyle
.
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/edt_left"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Sample content"
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="#id/button_right"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="#+id/button_right"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Right Button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#id/edt_left"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
If you want to make time text on right and text message on its left, you can do something like that ( using this in relative layout) also you can use maxWidth not minWidth
<TextView
android:id="#+id/view_textView_timeText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"/>
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="#id/view_textView_timeText"
android:maxWidth="100dp"/>
What you could do is put an empty view between the 2 views and keep its width as MATCH_PARENT and assign the textview to leftof this empty view and the empty view to left of the date view. Just make sure to keep the view empty.
As i understand you want to make the layout or the textview to be 100 dp less than the screen size
Which you can do by getting the screen width in pixels which is done by this
Display display = getWindowManager().getDefaultDisplay();
Point size = new Point();
display.getSize(size);
int width = size.x;
int height = size.y;
Then you could set the textbiew width to be less 100dp from the screen size hope this help
P.s I think you might want to convert dp to px but i am not sure
You can do like this(not the direct answer for the question ):
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="bottom"
android:orientation="horizontal">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
android:paddingLeft="45px"
android:text="asdfadsfsafasdfsakljkljkhjhkkhjkjhjkjhjkhjkhljkhlkhjlkjhljkhljkhlkjhljkhljkhlfasd"
android:textColor="#4a4a4a"
android:textSize="40px" />
</LinearLayout>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="1"
android:paddingLeft="45px"
android:paddingRight="48px"
android:text="2017.08.09 13:00"
android:textColor="#9b9b9b"
android:textSize="34px" />
</LinearLayout>
I have a common solution to solve this kind of layout question:
Create a specific ViewGroup!
For the question above, the key point is how to set the correct maxWidth to the content view.
Create a SpecialViewGroup. The contentView is the left view, and the timeView is the right view.
class SpecialViewGroup #JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private lateinit var contentView: TextView
private lateinit var timeView: TextView
override fun onAttachedToWindow() {
super.onAttachedToWindow()
contentView = findViewById(R.id.content)
timeView = findViewById(R.id.time)
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
// measure the timeView firstly, because the contentView's maxWidth rely on it.
timeView.measure(
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED),
)
// then caculate the remained space for the contentView
val parentWidth = MeasureSpec.getSize(widthMeasureSpec)
val paddingHorizontal = paddingStart + paddingEnd
val view1MaxWidth = parentWidth - timeView.measuredWidth - paddingHorizontal
// set the maxWidth to the contentView
contentView.maxWidth = view1MaxWidth
// The rest thing will be handed over by LinearLayout
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
}
}
Use the SpecialViewGroup in your layout, like the usual LinearLayout.
<com.example.SpecialViewGroup
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFBB86FC"
android:orientation="horizontal"
android:padding="10dp">
<TextView
android:id="#+id/content"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#FF3700B3"
android:padding="10dp"
android:text="adaasdasdasasdadasdasdaaaaaaaaaaaaaaaa"
android:textColor="#color/white" />
<TextView
android:id="#+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="#FF018786"
android:padding="10dp"
android:text="1970-01-01"
android:textColor="#color/white" />
</com.example.archview.SpecialViewGroup>
And the result:
The benefits of this approach are obvious:
No extra nesting Layout.
Common to solve the similar layout questions.
Had the similar issue. Made it works with constraint.
<ConstraintLayout>
<TextView
android:id="#+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="2"
app:layout_constraintEnd_toStartOf="#+id/option_info"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_max="wrap" />
<ImageView
android:id="#+id/option_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_info"
app:layout_constraintBottom_toBottomOf="#+id/title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/title"
app:layout_constraintTop_toTopOf="#+id/title" />
</ConstraintLayout>

Categories

Resources