问题
I'm new to ConstraintLayout, and I'm trying to replicate the same grid behavior offered by GridLayout with ConstraintLayout.
Specifically, I want to design a two columns grid. The first column width should be as narrow as possible, while the second column should take all the remaining horizontal space. Of course, the second column should be just to the right of the first column, or rather, to the widest view on the first column.
I don't know how can I replicate this last requirement with ConstraintLayout. I don't want to use a Gridline between the two columns, because the first column should not have a fixed nor a percentage width, but rather be as wide as the widest of its view.
At https://gist.github.com/venator85/499dd82f47b3efbbed7a1e9e1ca1412d I prepared a layout example, and the corresponding preview, showing a GridLayout that implements what I want. The first two ConstraintLayout attempts in that layout show C1 and D1 be aligned to B1, and C2 and D2 be aligned to B2. When B2 is narrower than A2, A1 and C1 will overlap.
Any help?
Thanks
回答1:
Google has introduced the idea of "barriers" in the latest release of ConstraintLayout
that helps to make an answer to this question 100% solvable in XML. See the ConstraintLayout 1.1.0 beta 1 release notes . Although, that note doesn't contain a lot of information on the new capabilities there was a talk at I/O 2017 that touched on the new stuff.
The new solution is to replicate the grid of GridLayout
with barriers. There is a vertical barrier placed to the right of the left-hand TextView
s and a barrier under the top three rows. The barriers shift depending upon how much text is present in each TextView
but always maintain the position specified in app:constraint_referenced_ids
. In essence, barriers act like floating guidelines. This solution is not relying upon any coding to support what is in the video.
Here is a video of the new layout that shows the desired positioning each TextView
being maintained as the contents of another TextView
changes. The video was made in the design tools of Android Studio 2.3.2.
And XML for the new layout using barriers:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constrained"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Barrier
android:id="@+id/barrierVertical"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="vertical"
app:barrierDirection="right"
app:constraint_referenced_ids="L1, L2, L3, L4" />
<TextView
android:id="@+id/L1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="L1 *"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1 R1*"
app:layout_constraintLeft_toRightOf="@+id/barrierVertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<android.support.constraint.Barrier
android:id="@+id/barrier1"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:barrierDirection="bottom"
app:constraint_referenced_ids="L1, R1" />
<TextView
android:id="@+id/L2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="L2 L2*"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier1"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2 R2*"
app:layout_constraintLeft_toRightOf="@+id/barrierVertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier1"
tools:ignore="HardcodedText" />
<android.support.constraint.Barrier
android:id="@+id/barrier2"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:barrierDirection="bottom"
app:constraint_referenced_ids="L2, R2" />
<TextView
android:id="@+id/L3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="L3 L3 L3*"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier2"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3 R3*"
app:layout_constraintLeft_toRightOf="@id/barrierVertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/barrier2"
tools:ignore="HardcodedText" />
<android.support.constraint.Barrier
android:id="@+id/barrier3"
android:layout_width="0dp"
android:layout_height="0dp"
android:orientation="horizontal"
app:barrierDirection="bottom"
app:constraint_referenced_ids="L3, R3" />
<TextView
android:id="@+id/L4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="L4 L4 L4 L4 L4 L4*"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barrier3"
tools:ignore="HardcodedText,RtlHardcoded" />
<TextView
android:id="@+id/R4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:text="R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4 R4*"
app:layout_constraintLeft_toRightOf="@+id/barrierVertical"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/barrier3"
tools:ignore="HardcodedText" />
</android.support.constraint.ConstraintLayout>
回答2:
Try this out.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<TextView
android:id="@+id/textView5"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:text="A1 A1 A1 A1 "
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView8"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="C1 C1 C1 C1 "
app:layout_constraintHorizontal_bias="0.506"
app:layout_constraintLeft_toRightOf="@+id/textView5"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView9"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="B1 B1 B1 B1 "
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/textView8"
app:layout_constraintTop_toBottomOf="@+id/textView8" />
<TextView
android:id="@+id/textView10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
android:text="D1 D1 D1 D1 "
app:layout_constraintHorizontal_bias="0.506"
app:layout_constraintLeft_toRightOf="@+id/textView5"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView5" />
</android.support.constraint.ConstraintLayout>
Something like this.
回答3:
Update : See the accepted answer.
I doubt that there is a way to replicate a GridLayout
with a ConstraintLayout
in the way that you want purely with XML. If you are willing to let a little code assist the layout, then you can set up the ConstraintLayout
to work as a GridLayout
using a movable vertical guideline.
Build the XML layout in two columns as you depict. The top of each TextView
in the left column will be constrained to the top of the corresponding TextView
in the right column, so the left entries will float up or down as the entries on the right increase or decrease in height.
All right column views will be constrained on the left to the vertical guideline mentioned above. The placement of this guideline in the XML should be something reasonable to work with but the actual placement will be made in code and will be adjusted depending upon the width of the widest view on the left.
This is a solution to the problem that you pose but it is not a general solution. The following depends on the height of each TextView
on the left being less than or equal to the height of the corresponding TextView
on the right.
Here is what the layout looks like in the Android Studio layout editor. I pushed the guideline over to the right to demonstrate how it floats. (Code follows images.)
Here is a screen shot. I hope that you find this useful.
Here is the layout using ConstraintLayout
. (Updated since initial post to get wrapping right in left column.)
constrained.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/constrained"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.Guideline
android:id="@+id/guideline"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
app:layout_constraintGuide_begin="257dp" />
<TextView
android:id="@+id/L1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="0dp"
android:text="A1 A1 A1 A1 A1*"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="wrap"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/L2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:text="B1 B1 B1 B1 B1*"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="@+id/R2"
app:layout_constraintWidth_default="wrap"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/L3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:text="A2 A2 A2 A2*"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="@+id/R3"
app:layout_constraintWidth_default="wrap"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/L4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="0dp"
android:layout_marginTop="0dp"
android:text="B2 B2 B2 B2 B2*"
app:layout_constraintHorizontal_bias="0.02"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@id/guideline"
app:layout_constraintTop_toTopOf="@+id/R4"
app:layout_constraintWidth_default="wrap"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1 C1*"
app:layout_constraintLeft_toRightOf="@id/guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1 D1*"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/R1"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2 C2*"
app:layout_constraintLeft_toRightOf="@id/guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/R2"
tools:ignore="HardcodedText" />
<TextView
android:id="@+id/R4"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2 D2*"
app:layout_constraintLeft_toRightOf="@+id/guideline"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@+id/R3"
tools:ignore="HardcodedText" />
</android.support.constraint.ConstraintLayout>
Here is the Activity
that adjusts the location of the guideline.
MainActivity.java
package com.example.layout2;
import android.os.Bundle;
import android.support.constraint.ConstraintLayout;
import android.support.constraint.Guideline;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;
public class MainActivity extends AppCompatActivity {
private Guideline mGuideline;
private ConstraintLayout mConstraintLayout;
private TextView L1;
private TextView L2;
private TextView L3;
private TextView L4;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.constrained);
mConstraintLayout = (ConstraintLayout) findViewById(R.id.constrained);
mGuideline = (Guideline) findViewById(R.id.guideline);
L1 = (TextView) findViewById(R.id.L1);
L2 = (TextView) findViewById(R.id.L2);
L3 = (TextView) findViewById(R.id.L3);
L4 = (TextView) findViewById(R.id.L4);
// We will adjust the location of the guideline after layout is completed.
mConstraintLayout.post(new Runnable() {
@Override
public void run() {
moveGuideline();
}
});
}
public void moveGuideline() {
ConstraintLayout.LayoutParams params;
params = (ConstraintLayout.LayoutParams) mGuideline.getLayoutParams();
// Find the widest TextView in the left column...
params.guideBegin = Math.max(Math.max(L1.getWidth(), L2.getWidth()),
Math.max(L3.getWidth(), L4.getWidth()));
// ... and set the guideline to the right of the widest one.
mGuideline.setLayoutParams(params);
}
}
回答4:
I don't think it is possible to perform purely with one ConstraintLayout
container at this time. I hope that in the future Google will add something like group_id
or some other way to calculate layout with "align to group".
In the mean time, I suggest you use ConstraintLayout
containers inside ConstraintLayout
.
This is how I implemented this:
To make the rows "grid" like, I used "ConstraintLayout Chains". Number of rows in each ConstraintLayout
chain must be identical, so the auto distribution will align rows correctly. (Though they can stay empty or hidden if not used).
The xml gist
来源:https://stackoverflow.com/questions/42846261/trying-to-replicate-gridlayout-column-alignment-with-constraintlayout