I created a custom Tab with ConstraintLayout, I decided to use ConstraintSet and TransitionManager for animating the tab selection.
This is the animation I need.
But when I implemented the animation I had problems.
Here is my code:
Xml layout definition
<android.support.constraint.ConstraintLayout
android:id="#+id/card_constraint_canvas"
android:layout_width="match_parent"
android:layout_height="match_parent">
<!-- View on which I want to apply constraint for animation -->
<View
android:id="#+id/tab_selection"
android:layout_width="0dp"
android:layout_height="4dp"
android:background="#color/colorSoftBlue"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="#+id/divider"
app:layout_constraintBottom_toBottomOf="parent"/>
<!-- Centered grey divider bar, view which is my anchor point -->
<View
android:id="#+id/divider"
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#color/colorDividerGrey"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Java code
#BindView(R.id.tab_selection) View tabSelection;
#BindView(R.id.card_constraint_canvas) ConstraintLayout cardConstraintCanvas;
...
applyConstraintSet.clone(cardConstraintCanvas); // Working
TransitionManager.beginDelayedTransition(cardConstraintCanvas); // Working
applyConstraintSet.clear(R.id.tab_selection); // Working
applyConstraintSet.connect(R.id.tab_selection, ConstraintSet.LEFT, R.id.divider, ConstraintSet.RIGHT); // Working
applyConstraintSet.connect(R.id.tab_selection, ConstraintSet.RIGHT, R.id.card_constraint_canvas, ConstraintSet.RIGHT); // Working
applyConstraintSet.connect(R.id.tab_selection, ConstraintSet.BOTTOM, R.id.card_constraint_canvas, ConstraintSet.BOTTOM); // Working
// Not working, the animation behaves strange, the tab_selection view disappears, I put 0 as width because I defined in the xml like that (0dp) but not working
// applyConstraintSet.constrainWidth(R.id.tab_selection, 0);
// Working, but I want my tab_selection view width spread at divider's start edge and parent's end edge after the animation, not a constant dimension
applyConstraintSet.constrainWidth(R.id.tab_selection, 10);
applyConstraintSet.constrainHeight(R.id.tab_selection, 10); // Working
applyConstraintSet.applyTo(cardConstraintCanvas); // Working
I suppose the problem is at constraintWidth. I also try this method for removing only the anchor and maintaining the width and the height.
void clear (int viewId, int anchor)
But I think it doesn't work as I thought.
Any thoughts?
Thank you for the help :)
If you remove the line
applyConstraintSet.clear(R.id.tab_selection);
and change ConstraintSet.LEFT and ConstraintSet.RIGHT to ConstraintSet.START and ConstraintSet.END respectively to be consistent with the naming of attributes used in the XML then this animation will work as intended.
I'm not sure why the clear method is causing the unwanted behaviour.
Related
I have some issue with follow structure:
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/white_rectangle_radius10">
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/first"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
/// many views here
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/second"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/first"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
/// many views here
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/third"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="#id/second"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
/// many views here
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
Programatically it can be 3 inner constraints or 4 or 2.
Root Constraint has round border radius.
Programatically when one of inner items clicked I set to this item background color, BUT when its first or last it overlap root border radius.
Note:
No need to suggest cardview as root element (not my option)
"Smart play" with margins/paddings also doesn't suit me because it should be changed programatically.
Is there some option to do it via XML?
When I added background color to first element it's top start/end corners not rounded.
First it is not recommended to nest layouts if you are going to use constraintLayout.
Second is how to design your view smoothly?
one of constraintlayout benefits is using the drag and drop option, therefore, start by doing that and ensure to place every item in its right place (but do not apply any constraint yet).
once all items are on the layout, click any item and look at the tool bar above the mobile screen, there is a magic wound, click on it, the constraint will be generated automatically based on the way you orgnized the view on the screen.
once that is done, now you might need to adjust the margins a bit according to the requirements.
also please assign id to each view before clicking the magic wound.
I hope I was helpful.
I would like to achieve the behavior when adding or removing the items to/from the recycler view, it changes its height according to the content up to the specified #dimen/maxRecyclerViewHeight value with smooth animation. It's working fine without animation after notifyItemInserted/Removed but the views under the recycler view are 'jumping' so that looks a bit odd. Can I achieve that somehow using TransitionManager.beginDelayedTransition(...)? I appreciate any other ideas.
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constrainedHeight="true"
app:layout_constraintHeight_max="#dimen/maxRecyclerViewHeight"/>
<!-- Other views go under the recycler view -->
</android.support.constraint.ConstraintLayout>
After doing some research I found this can definitely be achieved with TransitionManager.
Let's assume the xml file containing the constraint layout with the recycler view inside is activity_example.xml. So immediately after invoking notifyItemInserted/notifyItemRemoved and notifyItemRangeChanged you can call something like method below:
private void beginDelayedTransition() {
final ConstraintSet constraintSet = new ConstraintSet();
constraintSet.clone(this, R.layout.activity_example);
// this transition is responsible for animation resizing the view
final Transition transition = new ChangeBounds();
// or any other duration
transition.setDuration(250);
// constraintLayout is a root layout in the activity_example.xml
TransitionManager.beginDelayedTransition(constraintLayout, transition);
constraintSet.applyTo(constraintLayout);
}
I have an element that should remain centered in the layout and a button on the right size. The button width is variable.
The following design exemplify two scenarios.
Scenario 1: Long text button
Scenario 2: Small text button
The current solution is have an invisible duplicated button on the left. This is not ideal because the button look and feel may also vary for different locales. I have tried guidelines but that would require to define a percentage and I would prefer if it was dynamic. Barriers don't seem to work either because I would need them to be mirrored.
Any tips how to achieve this?
Try doing width with 0dp and give them weight and change this in runtime
may be they are in linear layout which is horizontal.
LinearLayout.LayoutParams params = (LinearLayout.LayoutParams)
button.getLayoutParams();
params.weight = 1.0f; // afterwards you can do the same with changing the weight
button.setLayoutParams(params);
Perhaps you can take advantage of the ConstraintLayoutStates:
https://riggaroo.co.za/constraintlayout-constraintlayoutstates/
And have two layouts one for each scenario.
I ended up using a different solution. I used two guidelines, one at 20% and another at 80%, to define the areas where the button could expand to.
When no button is available, I used the property app:layout_constrainedWidth="false" that ignores the constrainst and allow the title to expand to the available space.
I used this solution because I may need multiple buttons with different call to actions according to the selected language. It would be difficult to manage multiple hidden anchor buttons.
If I understand your pictures correctly, you want to have two elements on your screen:
A View that's centered on the screen, it can be any width size.
A Button that's positioned to the right of the View that can also be any width size. You want this button to be centered in the empty space between the View and the edge of the right screen.
You can try this:
<?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/view"
android:layout_width="100dp"
android:layout_height="100dp"
android:gravity="center"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
android:text="1"
android:textColor="#FFFFFF"
android:background="#000000"
/>
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toEndOf="#+id/view"
app:layout_constraintEnd_toEndOf="parent"
android:text="2"
/>
</androidx.constraintlayout.widget.ConstraintLayout>
Android ViewGroup has the following methods to add (child) Views:
I know we can easily define horizontal/vertical orientation to LinearLayout in xml/programatically and add child views, eg.
Similarly with RelativeLayout, we can use ViewGroup's addView and RelativeLayout.LayoutParams's method:
I am also aware that we can use LinearLayout as a child inside ConstraintLayout and play with it.
Is there any solid and recommended way to add child views to ConstraintLayout dynamically?
UPDATE:
Let me give a not-so-simple example which I what I want to achieve.
Suppose you have a View1, View2 and View3. All V1, V2 and V3 are aligned vertically one below another and are complex views consisting of multiple TextViews and ImageViews. Based on user action and what server sends information, I need to add multiple V1 and V2 (can be 1 pair of V1-V2 and can be 3 pairs of V1-V2)between original V2 and V3. If I am using ConstraintLayout, would it be best if I add multiple constraints programatically when I can easily use LinearLayout with vertical orientation?
Now, in terms of efficiency, performance and less-and-beautiful-code, is ConstraintLayout best for this requirement as compared to Linear Layout?
Views can be added to ConstraintLayout using addView() in the same way as you would with LinearLayout. The difference is that with ConstraintLayout the added views must be constrained. To constrain a view programmatically, use ConstraintSet.
This class allows you to define programmatically a set of constraints to be used with ConstraintLayout. It lets you create and save constraints, and apply them to an existing ConstraintLayout.
Here is a brief example:
activity_main
Define two TextViews. Center them horizontally and positioned at the top.
<android.support.constraint.ConstraintLayout
android:id="#+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:id="#+id/topView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Top View"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="#+id/bottomView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="Bottom View"
android:textSize="20sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#id/topView" />
</android.support.constraint.ConstraintLayout>
This is what you will see when a new TextView ("Middle View") is added to this layout without setting constraints. Notice that the new view defaults to position (0,0).
Let's say that we want the generated middle view to be placed between the top view and the bottom view centered horizontally in the window like this:
Here is the code that will produce this result:
MainActivity.java
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Define the new TextView and add it to the ConstraintLayout. Without constraints,
// this view will be positioned at (0,0).
TextView middleView = new TextView(this);
middleView.setId(View.generateViewId());
middleView.setText("Middle View");
middleView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20.0f);
ConstraintLayout layout = findViewById(R.id.constraintLayout);
ConstraintLayout.LayoutParams lp =
new ConstraintLayout.LayoutParams(ConstraintLayout.LayoutParams.WRAP_CONTENT,
ConstraintLayout.LayoutParams.WRAP_CONTENT);
layout.addView(middleView, lp);
// Move the new view into place by applying constraints.
ConstraintSet set = new ConstraintSet();
// Get existing constraints. This will be the base for modification.
set.clone(layout);
int topMargin = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
16, getResources().getDisplayMetrics());
// Set up the connections for the new view. Constrain its top to the bottom of the top view.
set.connect(middleView.getId(), ConstraintSet.TOP, R.id.topView, ConstraintSet.BOTTOM, topMargin);
// Constrain the top of the bottom view to the bottom of the new view. This will replace
// the constraint from the bottom view to the bottom of the top view.
set.connect(R.id.bottomView, ConstraintSet.TOP, middleView.getId(), ConstraintSet.BOTTOM, topMargin);
// Since views must be constrained vertically and horizontally, establish the horizontal
// constaints such that the new view is centered.
set.centerHorizontally(middleView.getId(),ConstraintSet.PARENT_ID);
// Finally, apply our good work to the layout.
set.applyTo(layout);
}
I am trying to build an Android UI where I need 7 boxes with the days of the week. In order to do this, I decided to go with a ConstraintLayout in order to be able to auto resize my views on any screen.
I created a chain between all 7 views with the with the "spread_inside" attribute, with worked but since I had my views' widths set to wrap_content, due to the nature of TextViews the views did not have equal widths. So I tried making them have equal widths by setting all of 7 views' widths to 0dp. This works but leaves no space between the views. Is there a way to add some spacing between these 7 views? Or is there another way of achieving the "equal widths" to all 7 views while keeping the auto-resizing ability on any screen? Is this even possible with ConstraintLayout or should I keep using LinearLayout for this kind of things? (as seen in the last screenshot)
I want my views to shrink when the screen is small and to expand up to a level when the screen is big. Please see the screenshots below of how it looks now. I want to add an 8dp padding between each view (on a LinearLayout I achieve this by adding a transparent divider on the layout with 8dp width, as in the last screenshot)
How it should look, achieved using LinearLayout
if you want them to be all the same width you don't even need spread_inside, just set the width to 0dp and then add margins to the views. for example:
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="#+id/view1"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginRight="4dp"
android:layout_marginLeft="8dp"
android:background="#ffff0000"
app:layout_constraintEnd_toStartOf="#+id/view2"
app:layout_constraintStart_toStartOf="parent"/>
<View
android:id="#+id/view2"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginRight="8dp"
android:layout_marginLeft="4dp"
android:background="#ff00ff00"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/view1"/>
</android.support.constraint.ConstraintLayout>
keep in mind that space between views will be the sum of 2 margins, first and last view will have only 1 margin space, so you need to set them accordingly (like 4dp for margins between views and 8dp for first and last margin)