how to MODIFY a constraint layout programmatically? - android

Ive got a view like below :
<org.rayanmehr.atlas.shared.customview.CustomTextView
android:id="#+id/tvDownVoteCount"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="6dp"
app:layout_constraintVertical_bias="0"
app:layout_constraintTop_toBottomOf="#+id/anchorHelper"
app:layout_constraintLeft_toRightOf="#+id/tvDownVoteIcon"
app:layout_constraintBottom_toBottomOf="#+id/imgComment"
/>
how can i modify the value of app:layout_constraintVertical_bias or any other constraint property programmatically without having top set the whole set of the properties again in my activity ?

Here is what I did (no need for ConstraintSet, we can work directly on the constraints themselves):
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) myView.getLayoutParams();
params.horizontalBias = 0.2f; // here is one modification for example. modify anything else you want :)
myView.setLayoutParams(params); // request the view to use the new modified params
it worked like a charm when I had a SeekBar and a TextView below it (aligned to it both left+right), and I wanted to update the TextView position to be under the SeekBar's cursor, so I had to update the horizontal bias params on the SeekBar's OnSeekBarChangeListener

If you are using Kotlin and you have androidx-core-ktx lib you can simply do:
someView.updateLayoutParams<ConstraintLayout.LayoutParams> { horizontalBias = 0.5f }

I just found the answer in here and you can use ConstraintSet to achieve this like below:
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(context, R.id.activity_constraint);
//for example lets change the vertical bias of tvDownVoteIcon
float biasedValue = 0.2f;
constraintSet.setVerticalBias(R.id.tvDownVoteIcon, biasedValue);
//or change the anchor
constraintSet.connect
(R.id.tvDownVoteIcon,ConstraintSet.RIGHT,R.id.txt,ConstraintSet.RIGHT,0);
//then apply
constraintSet.applyTo( (ConstraintLayout) findViewById(R.id.activity_constraint));

Disclaimer: I haven't used this specific functionality. This is just my interpretation on how I would try to do it.
I think that what you need is ConstraintSet. After obtaining it, you could modify it and apply it again. This is a relevant example from this article.
override fun onCreate(savedInstanceState: Bundle?) {
...
val constraintSet1 = ConstraintSet()
constraintSet1.clone(constraintLayout)
val constraintSet2 = ConstraintSet()
constraintSet2.clone(constraintLayout)
constraintSet2.centerVertically(R.id.image, 0)
var changed = false
findViewById(R.id.button).setOnClickListener {
TransitionManager.beginDelayedTransition(constraintLayout)
val constraint = if (changed) constraintSet1 else constraintSet2
constraint.applyTo(constraintLayout)
changed = !changed
}
}

Related

How to add array of views to layout kotlin

I want to add a list of views to a layout. The views are added, but are not showing up.
val container:LinearLayout=findViewById(R.id.container)
val card:FrameLayout=findViewById(R.id.card)
card.setBackgroundColor(Color.parseColor("#E67E22"))
val cardlimiter=4
var cards= arrayOfNulls<FrameLayout>(10)
for(i in 0 until cardlimiter)
{
cards[i]= FrameLayout(card.context).apply{
setBackgroundColor(Color.parseColor("red"))
}
container.addView(cards[i])
}
In Android we already have a component for this approach for us, It's called Recyclerview, check this doc: https://developer.android.com/develop/ui/views/layout/recyclerview
Nothing showing up because of FrameLayout you are trying to add doest not have any width, height or padding. You must specify a valid LayoutParam or nothing will shown with empty width or height.
Try to take a look at how to programmaticly create a valid layoutParam with width and height.
val params : ViewGroup.LayoutParams = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
FrameLayout(card.context).apply {
setBackgroundColor(Color.parseColor("red"))
layoutParams = params
setPadding(10, 10, 10, 10)
}

Android : How to programmatically set layout_constraintRight_toRightOf "parent"

I have a view in ConstrainLayout as follows.
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxWidth="260dp"
android:textColor="#FFF"
android:textSize="16sp"
app:layout_constraintLeft_toLeftOf="#+id/parent"
app:layout_constraintTop_toBottomOf="#id/message_date"
android:id="#+id/text_main"
/>
I would like to change the view to either app:layout_constraintLeft_toLeftOf="#+id/parent" or layout_constraintLeft_toRightOf="#+id/parent" programmatically in recycleViewHolder based on some conditions.
Here is an example of setting a button to the bottom of parent view using java code:
ConstraintLayout constraintLayout;
ConstraintSet constraintSet;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
constraintLayout = (ConstraintLayout) findViewById(R.id.activity_main_constraint_layout);
Button button = new Button(this);
button.setText("Hello");
constraintLayout.addView(button);
constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.connect(button.getId(), ConstraintSet.LEFT, constraintLayout.getId(), ConstraintSet.RIGHT, 0);
constraintSet.constrainDefaultHeight(button.getId(), 200);
constraintSet.applyTo(constraintLayout);
}
to achieve something like this,
app:layout_constraintLeft_toLeftOf="#+id/parent"
your java code should look like this
set.connect(YOURVIEW.getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.LEFT,0);
and to achieve something like this,
layout_constraintLeft_toRightOf="#+id/parent"
your java code should look like this,
set.connect(YOURVIEW.getId(),ConstraintSet.LEFT,ConstraintSet.PARENT_ID,ConstraintSet.RIGHT,0);
Here, I'm assuming android:id="#+id/parent" is the id of your parent ConstraintLayout .
constraintSet = new ConstraintSet(); and so on
Did not work for me anyhow.
Solved by
LayoutParams layoutParams = (LayoutParams) viewToChange.getLayoutParams();
layoutParams.leftToLeft = anotherViewId;
layoutParams.rightToRight =anotherViewId;
layoutParams.topToTop = anotherViewId;
layoutParams.bottomToBottom = anotherViewId;
layoutParams.startToStart =anotherViewId;
layoutParams.endToEnd = anotherViewId;
viewToChange.setLayoutParams(layoutParams);
Use id
class MyLayout(context: Context) : ConstraintLayout(context) {
fun show() {
val view = ImageView(context)
addView(view)
val params = view.layoutParams as ConstraintLayout.LayoutParams
params.height = 100
params.width = 50
params.rightToRight = id
view.requestLayout()
}
}
I had same situation when I needed to move buttons down once a new EditText appears, In XML file they had constraints like this:
app:layout_constraintTop_toBottomOf="#+id/first_layout"
and what I needed to do is changing to sth like programmatically:
app:layout_constraintTop_toBottomOf="#+id/second_layout"
Using ConstraintLayout.LayoutParams the task is pretty straightforward e.g.
Kotlin:
val currentLayout = btn.layoutParams as ConstraintLayout.LayoutParams // btn is a View here
currentLayout.topToBottom = R.id.second_layout // resource ID of new parent field
btn.layoutParams = currentLayout
and that's it!
Haven't seen better solution than this:
textMain.updateLayoutParams<ConstraintLayout.LayoutParams> {
startToEnd = anyOtherView.id
topToTop = anyOtherView.id
endToStart = anyOtherView.id
bottomToBottom = anyOtherView.id
}
In Android, you can programmatically set the layout_constraintRight_toRightOf attribute to "parent" using the ConstraintLayout.LayoutParams class. Here's an example:
ConstraintLayout layout = findViewById(R.id.constraint_layout);
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) view.getLayoutParams();
params.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID;
view.setLayoutParams(params);
In the above example, "constraint_layout" is the id of the ConstraintLayout, "view" is the view that you want to set the constraint on and the "params.rightToRight = ConstraintLayout.LayoutParams.PARENT_ID" is used to set the layout_constraintRight_toRightOf attribute to "parent".
This will set the right side of the view to the right side of its parent, aligning it to the right edge of the screen.
You can also use setRightToRight(int rightToRight) method of ConstraintLayout.LayoutParams.
It's also important to note that you should call requestLayout() on the view after modifying its layout parameters, so that the changes will be reflected on the layout.

Change Guideline percentage in constraint layout programmatically

I have a guideline in constraint layout like this
<android.support.constraint.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/guideline8"
app:layout_constraintGuide_percent="0.5"
android:orientation="vertical"/>
Later I want to change the app:layout_constraintGuide_percent value to something else conditionally. How can I achieve this.
There is two ways to do it:
Using ConstraintLayout.LayoutParams
This method get a specific parameter from the ConstraintLayout and modify it.
Guideline guideLine = (Guideline) findViewById(R.id.your_guideline);
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) guideLine.getLayoutParams();
params.guidePercent = 0.45f; // 45% // range: 0 <-> 1
guideLine.setLayoutParams(params);
Using ConstraintSet
This method consists in cloning the ConstraintLayout attributes, modifying them and then applying to the View.
BUT it's very slow.
ConstraintLayout constraintLayout = (ConstraintLayout) findViewById(R.id.your_constraint_with_guideline);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.setGuidelinePercent(R.id.your_guideline, 0.07f); // 7% // range: 0 <-> 1
TransitionManager.beginDelayedTransition(constraintLayout);
constraintSet.applyTo(constraintLayout);
Creating a parallax effect
To test those methods I created a parallax animation using a GuideLine and a ScrollView on top.
On the AndroidManifest.xml, inside your Activity, add this: android:hardwareAccelerated="true".
<activity
android:name=".MainActivity"
android:configChanges="keyboardHidden|orientation|screenSize"
android:hardwareAccelerated="true">
...
MainActivity.java
Guideline guideTopInfo;
ConstraintLayout constraintLayout;
ConstraintLayout.LayoutParams params;
ConstraintSet constraintSet;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
guideTopInfo = (Guideline) findViewById(R.id.guideline2);
constraintLayout = (ConstraintLayout) findViewById(R.id.constraint_root);
params = (ConstraintLayout.LayoutParams) guideTopInfo.getLayoutParams();
//constraintSet = new ConstraintSet();
//constraintSet.clone(constraintLayout);
final ScrollView scrollView = (ScrollView) findViewById(R.id.scroll_front);
scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
#Override
public void onScrollChanged() {
float percentage = scrollView.getScrollY() * 0.0001f; // 0.001f faster // 0.00001f slower parallax animation
Log.d("mLog", String.valueOf(percentage));
if(percentage >= 0) {
// my XML percentage value was 12%
params.guidePercent = 0.12f - percentage; // up
//params.guidePercent = 0.12f + percentage; // downm
guideTopInfo.setLayoutParams(params);
//constraintSet.setGuidelinePercent(R.id.guideline2, 0.12f - percentage);
//TransitionManager.beginDelayedTransition(constraintLayout);
//constraintSet.applyTo(constraintLayout);
}
}
});
}
My other answer about parallax if anyone is interested. ;)
This code will change you guidePercent to 0
ConstraintLayout.LayoutParams lp = (ConstraintLayout.LayoutParams) guideline.getLayoutParams();
lp.guidePercent = 0;
guideline.setLayoutParams(lp);
guideline.setGuidelinePercent(value: Float)
This is enough to change the guideline percent during runtime.
you cam make it center doing it worked with me i hope it helps
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintGuide_percent="0.5"
app:layout_constraintStart_toStartOf="parent" />
You need to use ConstraintSet object to set layout_constraintGuide_percent in code.
Below is the code to set layout_constraintGuide_percent in code. First you need to create constraint set object, call clone method passing constraintlayout and then call setGuidelinePercent passing guideline id and ratio.
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(constraintLayout);
constraintSet.setGuidelinePercent(R.id.guideline, 0.4f);
In Kotlin & inside Fragment or other activities,set GuideLine app: related parameters has a little difficulty , but I Solved it in this way :
val view = inflate(this, R.layout.ly_custom_menu_item_wallet_side_menu, null)
val guideL = view.findViewById<Guideline>(R.id.guideline_wallet)
var constParam: ConstraintLayout.LayoutParams = guideL.layoutParams as ConstraintLayout.LayoutParams
constParam.guidePercent = 0.42f
guideL.layoutParams = constParam
and this is Guideline element inside our ly_custom_menu_item_wallet_side_menu.xml layout :
<androidx.constraintlayout.widget.Guideline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/guideline_wallet"
android:orientation="vertical"
app:layout_constraintGuide_end="84dp" />

Android databinding set padding if value is true

I want to be able to to be able to set padding values if a boolean is true. The problem is that Android studio cannot parse the layout because it thinks 2dp is a decimal with a value of 2 and then doesn't know what to do with the p. how do I format this so that it understands i mean 2 density pixels.
Data layout:
<data class=".ItemBinding">
<variable name="isGroupType" type="Boolean"/>
</data>
View layout(whats important):
<android.support.v7.widget.AppCompatImageView
android:layout_width="64dp"
android:layout_height="64dp"
android:paddingBottom='#{isGroupType ? 2dp : 0dp}'
android:paddingTop='#{isGroupType ? 8dp : 0dp}'
android:paddingRight='#{isGroupType ? 2dp : 0dp}'
android:paddingLeft='#{isGroupType ? 2dp : 0dp}'/>
Store padding value in dimen.xml and use it. Please keep habit to write binding string with " " (double quotes)
android:paddingBottom="#{isGroupType ? #dimen/padding_normal : #dimen/padding_null}"
and so on for other paddings also.
For anyone looking to set margins via DataBinding, you'll have to use BindingAdapter as well:
#BindingAdapter("layoutMarginBottom")
fun setLayoutMarginBottom(view: View, dimen: Float) {
val layoutParams = view.layoutParams as MarginLayoutParams
layoutParams.bottomMargin = dimen.toInt()
view.layoutParams = layoutParams
}
And your xml property will look like this:
app:layoutMarginBottom="#{someCondition ? #dimen/zero_dp : #dimen/twenty_dp}"
Just as a heads-up this does not work with layout_margin's :(
Not sure why, but think it's due to the parent layout needs to be remeasured..
#Ravi's answer is correct.
But for more flexibility you can also try this:
#BindingAdapter({"padding", "shouldAdd"})
public static void setPadding(AppCompatImageView imageView, boolean shouldAdd, int padding){
if (shouldAdd){
imageView.setPadding(padding, padding, padding, padding);
}
}
Then:
<android.support.v7.widget.AppCompatImageView
android:layout_width="64dp"
android:layout_height="64dp"
shouldAdd="#{isGroupType}"
padding="#{10}"/>
#Ravi's answer is good, but it's working only for padding.
If You want to simply add margin, create empty view e.g TextView with padding.
Use a blank view
<View
android:layout_width = "8dp"
android:layout_height = "8dp"
>
Now constraint this view to the layout you want to control the visibility of .
Now disappear this view depending on the condition. would work the same as a margin
You can use logic and ternary statements in xml-binding, but you really shouldn't. It will come back to haunt you when you're looking the usual places you have logic and don't see what's going on.
BindingAdapter for all your margin needs:
fun bindingSetMargins(view: View, start: Float?, top: Float?, end: Float?, bottom: Float?) {
view.layoutParams = (view.layoutParams as ViewGroup.MarginLayoutParams).apply {
start?.toInt()?.let { leftMargin = it }
top?.toInt()?.let { topMargin = it }
end?.toInt()?.let { rightMargin = it }
bottom?.toInt()?.let { bottomMargin = it }
}
}

How to programmatically set the layout_align_parent_right attribute of a Button in Relative Layout?

I have a relative layout which I am creating programmatically:
RelativeLayout layout = new RelativeLayout( this );
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.WRAP_CONTENT);
Now I have two buttons which I want to add in this relative layout. But the problem is both buttons are being shown on the left of the RelatiiveLayout overlapping on each other.
buttonContainer.addView(btn1);
buttonContainer.addView(btn2);
Now I want to know how can I programmatically set the the android:layout_alignParentRight="true"
or android:layout_toLeftOf="#id/btn" attribute of buttons as we do in the xml?
You can access any LayoutParams from code using View.getLayoutParams. You just have to be very aware of what LayoutParams your accessing. This is normally achieved by checking the containing ViewGroup if it has a LayoutParams inner child then that's the one you should use. In your case it's RelativeLayout.LayoutParams. You'll be using RelativeLayout.LayoutParams#addRule(int verb) and RelativeLayout.LayoutParams#addRule(int verb, int anchor)
You can get to it via code:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams)button.getLayoutParams();
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
params.addRule(RelativeLayout.LEFT_OF, R.id.id_to_be_left_of);
button.setLayoutParams(params); //causes layout update
For adding a RelativeLayout attribute whose value is true or false use 0 for false and RelativeLayout.TRUE for true:
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) button.getLayoutParams()
params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE)
It doesn't matter whether or not the attribute was already added, you still use addRule(verb, subject) to enable/disable it. However, post-API 17 you can use removeRule(verb) which is just a shortcut for addRule(verb, 0).
you need to create and id for the
buttons you need to refference:
btn1.setId(1);
you can use the params variable to
add parameters to your layout, i
think the method is addRule(), check
out the android java docs for this
LayoutParams object.
Kotlin version:
Use these extensions with infix functions that simplify later calls
infix fun View.below(view: View) {
(this.layoutParams as? RelativeLayout.LayoutParams)?.addRule(RelativeLayout.BELOW, view.id)
}
infix fun View.leftOf(view: View) {
(this.layoutParams as? RelativeLayout.LayoutParams)?.addRule(RelativeLayout.LEFT_OF, view.id)
}
infix fun View.alightParentRightIs(aligned: Boolean) {
val layoutParams = this.layoutParams as? RelativeLayout.LayoutParams
if (aligned) {
(this.layoutParams as? RelativeLayout.LayoutParams)?.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
} else {
(this.layoutParams as? RelativeLayout.LayoutParams)?.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, 0)
}
this.layoutParams = layoutParams
}
Then use them as infix functions calls:
view1 below view2
view1 leftOf view2
view1 alightParentRightIs true
Or you can use them as normal functions:
view1.below(view2)
view1.leftOf(view2)
view1.alightParentRightIs(true)
In Kotlin:
val params = mBinding.tvTotalAmount.layoutParams as RelativeLayout.LayoutParams
params.addRule(RelativeLayout.ALIGN_PARENT_END)
mBinding.tvTotalAmount.layoutParams = params

Categories

Resources