I have a constraint
app:layout_constraintStart_toStartOf="parent". Runtime I need to change this constraint to app:layout_constraintStart_toEndOf="#+id\myid" .
From my research i found only constraintSet.connect(viewid, ConstraintSet.END, ConstraintSet.PARENT_ID, ConstraintSet.END, 50); . But how to achieve my requirements with this. Any idea on this?
This is in kotlin but it's barely different from Java.
Give an ID to the rootLayout.
Now, use this code:
val constraintSet = ConstraintSet()
constraintSet.clone(rootLayout) //Your rootLayout
constraintSet.connect(
yourView.id, //ID of the view whose position you want to change
ConstraintSet.START,
yourMyIdView.id, //ID of the correspondent view
ConstraintSet.END
)
constraintSet.applyTo(rootLayout) //Your rootLayout
ProTip : You can also animate the change by setting animateChange to true in rootLayout (XML) or you can use a Transition animation which I always prefer.
val transition: Transition = ChangeBounds()
transition.interpolator = AccelerateInterpolator() //Or Any other Interpolator
transition.duration = 700 //Duration
TransitionManager.beginDelayedTransition(rootLayout, transition) //Your rootLayout
constraintSet.applyTo(rootLayout) //Remember to put above 4 lines before this
You can further add Alpha animation it by using yourView.animate().alpha(1f).duration = 700 //1f (0 to 1) is the value and 700 is the duration.
Remember, it is barely different from Java, you just have to add ; at the end possibly.
Related
I have been using constraintlayout animations using two layout files
private void animate() {
ConstraintSet set1 = new ConstraintSet();
set1.clone(root);
ConstraintSet set2 = new ConstraintSet();
set2.clone(this, R.layout.layout_file_2);
TransitionManager.beginDelayedTransition(root, transition);
set2.applyTo(root);
}
However, in the layout i am only changing two constraints. I decided to try doing so programmatically and did this, which worked.
private void showHidden() {
ConstraintLayout parentLayout = findViewById(R.id.root_constraint_layout);
ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(parentLayout);
constraintSet.connect(R.id.search_layout, ConstraintSet.TOP, R.id.toolbar_layout, ConstraintSet.BOTTOM);
constraintSet.connect(R.id.search_layout, ConstraintSet.BOTTOM, R.id.root_constraint_layout, ConstraintSet.BOTTOM);
constraintSet.applyTo(parentLayout);
}
This is however, not animated. I tried to animate as I did before in the first one (using only 1 layout file this time) and my result was this
private void animate2() {
ConstraintLayout parentLayout = findViewById(R.id.root_constraint_layout);
ConstraintSet set1 = new ConstraintSet();
set1.clone(parentLayout);
ConstraintSet set2 = new ConstraintSet();
set2.clone(parentLayout);
set2.connect(R.id.search_layout, ConstraintSet.TOP, R.id.toolbar_layout, ConstraintSet.BOTTOM);
set2.connect(R.id.search_layout, ConstraintSet.BOTTOM, R.id.root_constraint_layout, ConstraintSet.BOTTOM);
Transition transition = new Slide(Gravity.END).setDuration(500);
TransitionManager.beginDelayedTransition(parentLayout, transition);
set2.applyTo(parentLayout);
}
This works like the second one, but shows no animation, the new constraints immediately jump into place. Is there any way I can make the animation work without using two layoyuts? or is there something i'm doing wrong? thanks.
You didn't clear the constraints on side of view on which you applying constraint set .
You can do this approach using
constraintSet.clear(view.Layout.id, ConstraintSet.LEFT) //You can change LEFT with whatever side you are choosing
Before connecting layout sides using connect function .
Here is the full code for getting it in better way :-
val constraintSet = ConstraintSet()
constraintSet.clone(view.rootLayout)
view.layout.animate().alpha(1f).duration = 700
constraintSet.clear(view.layout.id, ConstraintSet.LEFT)
constraintSet.connect(
view.layout.id,
ConstraintSet.RIGHT,
ConstraintSet.PARENT_ID,
ConstraintSet.RIGHT
)
view.layout.animate().translationX(resources.getDimension(R.dimen.zeroDP))
val transition: Transition = ChangeBounds()
transition.interpolator = EasingInterpolator(Ease.QUINT_OUT)
transition.duration = 700
TransitionManager.beginDelayedTransition(view.rootLayout, transition)
constraintSet.applyTo(view.rootLayout)
I want to create a view (i.e. TextView) in my constraint layout and animate it's position on button click.
This is how my onClick looks like:
fun onClick(view: View) {
val layout = findViewById<ConstraintLayout>(R.id.layout)
val txt2 = TextView(this)
txt2.text = "Hello2"
txt2.textSize = 70F
txt2.id = View.generateViewId()
tid = txt2.id
layout.addView(txt2)
val cs = ConstraintSet()
cs.clone(layout)
cs.connect(txt2.id, ConstraintSet.BOTTOM, textbox.id, ConstraintSet.TOP, 0)
cs.connect(txt2.id, ConstraintSet.LEFT, layout.id, ConstraintSet.LEFT)
cs.connect(txt2.id, ConstraintSet.RIGHT, layout.id, ConstraintSet.RIGHT)
cs.applyTo(layout)
startAnimation(txt2.id)
}
fun startAnimation(txt: Int) {
val layout = findViewById<ConstraintLayout>(R.id.layout)
TransitionManager.beginDelayedTransition(layout)
val cs2 = ConstraintSet()
cs2.clone(layout)
cs2.connect(txt, ConstraintSet.TOP, layout.id, ConstraintSet.TOP, 50)
cs2.applyTo(layout)
}
Animation works if I create TextView somewhere before actual click, but how to do this in one shot?
I think problem in the way how android calculate layout. When you call TextView(this) and layout.addView(txt2) view created and added to children list, but have no height, width and position on screen. In Looper of UI thread will be enqueued task to layout textView, that call onMeausure and layout for txt2 and its children. If it enqueued in UI thread, it will be copmlite after your functions finish, and you start animation before tx2 was "layouted".
I see 2 ways to resolve this. First: create txt2 in layout on start of activity or specify it in xml, just not create constrains for it and set visibility to GONE. ConstraintSet.applyTo correctly works with appearing and disappearing when it change visibility. Second: put starting of animation as delayed. Not the perfect solution, but it should works.
Handler().postDelayed({
startAnimation(txt2.id)
}, 100)
I am trying to change the constraints programmatically as follows:
final ConstraintSet set = new ConstraintSet();
set.clone(this);
if(text == null) {
view.setVisibility(GONE);
set.clear(div.getId(), ConstraintSet.TOP);
set.connect(div.getId(), ConstraintSet.TOP, otherView.getId(), ConstraintSet.BOTTOM);
}
set.applyTo(this);
But the rule is not applied. The div goes to the top of the layout like there is no top constraint.
What am I doing wrong?
Update
If I change the following:
set.clone(this);
...
set.apply(this);
to:
View root = LayoutInflater.from(getContext()).inflate(R.layout.my_layout, this);
ConstraintLayout layout = root.findViewById(R.id.constraint_layout);
....
set.clone(layout);
....
set.apply(layout);
The layout is correct! But I don't understand why.
this is a ConstraintLayout since I am extending a ConstraintLayout.
Good day,
I need to show a list of fidelity cards, one on the others with a small border margin, and while clicking on it, it reveal it completely while sliding.
In a fragment, I use a ConstraintLayout somewhere in the screen that I populate by code.
I add all child's (total known after WS called) into the ConstraintLayout, then I am using ConstraintSet to set the initial constraints.
But at the initial fragment creation, I have found out that I must apply a TimeOut between the ConstraintLayout population and creating the Constraints (disgusting, right?)
adding each card...
val card = FrequencyCardView()
fid_card_container.addView(card as View)
listCard.add(card)
Then create the constraints:
val set = ConstraintSet()
val containerId = fid_card_container.id
set.clone(fid_card_container)
listCard.forEachIndexed { index, card ->
val marginTop = when (index) {
0 -> 0
else -> (listCard[index -1] as ViewGroup).height / 4
}
val rootId = if (index == 0) containerId else (index - 1)
set.connect(card.getId(), TOP, rootId, TOP, marginTop)
set.connect(card.getId(), ConstraintSet.START, containerId, ConstraintSet.START, 0)
set.connect(card.getId(), ConstraintSet.END, containerId, ConstraintSet.END, 0)
card.setOpen(false)
}
// Apply the changes
set.applyTo(fid_card_container)
Any idea, why I need to apply any timeout between adding the Childs into the ConstraintLayout and then add the Constraints?
Any better solution, that this ugly TimeOut?
Thanks
I have found a better way.
It seems that the constraints creation must be ensured to be done on the UI Thread. This is why it does work with the timeout.
So, I have changed the timeout call by the Anko call:
doAsync {
uiThread {
resetCardLayout()
}
}
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
}
}