如何使用 ConstraintLayout 将多个视图居中?
How to center multiple Views together using ConstraintLayout?
背景
Google 宣布了一个名为“ConstraintLayout”的新布局,它应该是最终的布局,可以在保持平坦(没有嵌套布局)的同时替换所有布局并具有更好的性能.
问题
事实是,除了 Google IO 上的视频外,我几乎看不到任何可以帮助我解决这个问题的教程。
我想做的是,假设我在另一个布局中有一个垂直居中的 LinearLayout - 将它们都转换成一个 ConstraintLayout。
毕竟,这就是这个新布局的目的...
我希望处理的布局如下所示:
请注意,位于中心的视图仅垂直居中,并且 2 个 textView 位于 ImageView 的右侧,ImageView 也是垂直居中的。
这一切都适用于 RelativeLayout,它具有 2 个 TextView 的 LinearLayout,但我想知道如何将它们转换为单个 ConstraintLayout。
这是我展示的示例 XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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="wrap_content"
android:minHeight="?attr/listPreferredItemHeightSmall">
<ImageView
android:id="@+id/appIconImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginEnd="4dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="4dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:src="@android:drawable/sym_def_app_icon"
tools:ignore="ContentDescription"/>
<LinearLayout
android:id="@+id/appDetailsContainer"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/appIconImageView"
android:layout_toLeftOf="@+id/overflowView"
android:layout_toRightOf="@+id/appIconImageView"
android:layout_toStartOf="@+id/overflowView"
android:orientation="vertical">
<TextView
android:id="@+id/appLabelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:text="label"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textDirection="locale"
tools:ignore="HardcodedText,UnusedAttribute"/>
<TextView
android:id="@+id/appDescriptionTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:minLines="3"
android:text="description"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textDirection="locale"
tools:ignore="HardcodedText,UnusedAttribute"/>
</LinearLayout>
<ImageView
android:id="@+id/overflowView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="10dp"
app:srcCompat="@drawable/ic_more_vert_black_24dp"
tools:src="@drawable/ic_more_vert_black_24dp"
tools:ignore="ContentDescription"/>
<ImageView
android:id="@+id/isSystemAppImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/overflowView"
android:layout_alignLeft="@+id/overflowView"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/overflowView"
android:layout_alignStart="@+id/overflowView"
android:adjustViewBounds="true"
android:scaleType="centerInside"
app:srcCompat="@drawable/ic_warning_black_24dp"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_warning_black_24dp"/>
</RelativeLayout>
我试过的
我试着阅读了一些文章并观看了 Google 的一些视频:
- https://codelabs.developers.google.com/codelabs/constraint-layout/index.html#0
- https://www.youtube.com/watch?v=sO9aX87hq9c
- https://youtu.be/csaXml4xtN8?t=1693
这没有帮助,所以我尝试使用它,希望我能自己找到如何使用它。
但我不知道该怎么做。我尝试使用该功能来转换布局,但这会使视图变得一团糟,并增加了我不想拥有的额外边距。
问题
如何将 2 个布局转换为一个 ConstraintLayout ?
将app:layout_constraintVertical_bias="0.5"
设置为需要垂直居中的视图,bias属性仅在您指定边界约束时才有效(例如垂直偏置的顶部和底部,水平偏置的左侧和右侧)
一个例子:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/constraintLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="@id/constraintLayout"
app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
app:layout_constraintVertical_bias="0.5" />
</android.support.constraint.ConstraintLayout>
它在我这里的布局中工作:https://github.com/hidroh/tldroid/blob/master/app/src/main/res/layout/activity_main.xml,非常相似的布局,尽管我把东西放在屏幕高度的 1/3 处。
编辑:这个答案是在链可用之前写的。请立即使用链,参见上面的答案:
目前我认为您唯一的选择是将两个文本视图包装在另一个布局中 - 线性布局可能最适合这种情况。
但是约束布局背后的团队说他们想引入 "virtual containers",它正好服务于这个用例:将两个或多个视图组合在一起并在容器上设置约束(例如垂直居中)。这个想法是它不是约束布局内部的完整嵌套布局,而只是约束布局用来定位它的子项的东西 - 因此它应该提供比嵌套更好的性能。
他们在 their I/O talk 中提到了它(链接到确切时间)。所以我想敬请期待。
看看我的例子(Center components in ConstraintLayout)
现在您可以居中 "image"(约束左上-下),将 "label" 顶部约束到 "image" 顶部,将 "description" 顶部约束到 "label"底部。
在下面的示例中,我将按钮高度与右侧的两个文本视图相匹配(这是您的情况)。其他文本视图受到上述 link 的限制。
更新:回复您的以下评论:
我已经安装了你的应用程序,这是我目前唯一能想到的。由于它是一个 ViewHolder 布局,layout_height 可以设置为 wrap_content,或者如果您愿意,可以修复它。如果你想要我可以发给你xml,我不想淹没答案。
Top TextView 的左约束约束到ImageView 的右约束。此外,顶部 TextView -> 顶部约束被限制在容器顶部,所以是正确的。底部 TextView 被限制在容器底部。中间的 TextViews 被限制为匹配顶部 TextView 的宽度并且与 ImageView 没有联系。
看看我的回答。
ContraintLayout
包含一项功能 - Chains
- 可以实现您的要求:
Chains provide group-like behavior in a single axis (horizontally or
vertically).
A set of widgets are considered a chain if they a linked together via
a bi-directional connection
Once a chain is created, there are two possibilities:
- Spread the elements in the available space
- A chain can also be "packed", in that case the elements are grouped together
至于你的情况,你必须打包你的 label
和 description
TextViews 并将它们在你 parent 垂直居中:
(确保您使用支持链的 ConstraintLayout
版本)
<?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">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="TextView"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintLeft_toRightOf="@+id/imageView2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"/>
<TextView
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button\nMkay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/imageView2"
app:layout_constraintTop_toBottomOf="@+id/textView"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:srcCompat="@mipmap/ic_launcher"/>
</android.support.constraint.ConstraintLayout>
25-06-2019 更新(@Saeid Z):
现在在约束布局 1.1.3 中我们必须使用 app:layout_constraintHorizontal_chainStyle="packed"
而不是 app:layout_constraintVertical_chainPacked="true"
要垂直或水平居中,请在布局上设置 opposing constraint。
垂直居中
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
水平居中
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
背景
Google 宣布了一个名为“ConstraintLayout”的新布局,它应该是最终的布局,可以在保持平坦(没有嵌套布局)的同时替换所有布局并具有更好的性能.
问题
事实是,除了 Google IO 上的视频外,我几乎看不到任何可以帮助我解决这个问题的教程。
我想做的是,假设我在另一个布局中有一个垂直居中的 LinearLayout - 将它们都转换成一个 ConstraintLayout。
毕竟,这就是这个新布局的目的...
我希望处理的布局如下所示:
请注意,位于中心的视图仅垂直居中,并且 2 个 textView 位于 ImageView 的右侧,ImageView 也是垂直居中的。
这一切都适用于 RelativeLayout,它具有 2 个 TextView 的 LinearLayout,但我想知道如何将它们转换为单个 ConstraintLayout。
这是我展示的示例 XML:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
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="wrap_content"
android:minHeight="?attr/listPreferredItemHeightSmall">
<ImageView
android:id="@+id/appIconImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginEnd="4dp"
android:layout_marginLeft="2dp"
android:layout_marginRight="4dp"
android:layout_marginStart="2dp"
android:adjustViewBounds="true"
android:src="@android:drawable/sym_def_app_icon"
tools:ignore="ContentDescription"/>
<LinearLayout
android:id="@+id/appDetailsContainer"
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_toEndOf="@+id/appIconImageView"
android:layout_toLeftOf="@+id/overflowView"
android:layout_toRightOf="@+id/appIconImageView"
android:layout_toStartOf="@+id/overflowView"
android:orientation="vertical">
<TextView
android:id="@+id/appLabelTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:text="label"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textDirection="locale"
tools:ignore="HardcodedText,UnusedAttribute"/>
<TextView
android:id="@+id/appDescriptionTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:minLines="3"
android:text="description"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textDirection="locale"
tools:ignore="HardcodedText,UnusedAttribute"/>
</LinearLayout>
<ImageView
android:id="@+id/overflowView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:adjustViewBounds="true"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="10dp"
app:srcCompat="@drawable/ic_more_vert_black_24dp"
tools:src="@drawable/ic_more_vert_black_24dp"
tools:ignore="ContentDescription"/>
<ImageView
android:id="@+id/isSystemAppImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/overflowView"
android:layout_alignLeft="@+id/overflowView"
android:layout_alignParentBottom="true"
android:layout_alignRight="@+id/overflowView"
android:layout_alignStart="@+id/overflowView"
android:adjustViewBounds="true"
android:scaleType="centerInside"
app:srcCompat="@drawable/ic_warning_black_24dp"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_warning_black_24dp"/>
</RelativeLayout>
我试过的
我试着阅读了一些文章并观看了 Google 的一些视频:
- https://codelabs.developers.google.com/codelabs/constraint-layout/index.html#0
- https://www.youtube.com/watch?v=sO9aX87hq9c
- https://youtu.be/csaXml4xtN8?t=1693
这没有帮助,所以我尝试使用它,希望我能自己找到如何使用它。 但我不知道该怎么做。我尝试使用该功能来转换布局,但这会使视图变得一团糟,并增加了我不想拥有的额外边距。
问题
如何将 2 个布局转换为一个 ConstraintLayout ?
将app:layout_constraintVertical_bias="0.5"
设置为需要垂直居中的视图,bias属性仅在您指定边界约束时才有效(例如垂直偏置的顶部和底部,水平偏置的左侧和右侧)
一个例子:
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/constraintLayout">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="@id/constraintLayout"
app:layout_constraintBottom_toBottomOf="@id/constraintLayout"
app:layout_constraintVertical_bias="0.5" />
</android.support.constraint.ConstraintLayout>
它在我这里的布局中工作:https://github.com/hidroh/tldroid/blob/master/app/src/main/res/layout/activity_main.xml,非常相似的布局,尽管我把东西放在屏幕高度的 1/3 处。
编辑:这个答案是在链可用之前写的。请立即使用链,参见上面的答案:
目前我认为您唯一的选择是将两个文本视图包装在另一个布局中 - 线性布局可能最适合这种情况。
但是约束布局背后的团队说他们想引入 "virtual containers",它正好服务于这个用例:将两个或多个视图组合在一起并在容器上设置约束(例如垂直居中)。这个想法是它不是约束布局内部的完整嵌套布局,而只是约束布局用来定位它的子项的东西 - 因此它应该提供比嵌套更好的性能。
他们在 their I/O talk 中提到了它(链接到确切时间)。所以我想敬请期待。
看看我的例子(Center components in ConstraintLayout)
现在您可以居中 "image"(约束左上-下),将 "label" 顶部约束到 "image" 顶部,将 "description" 顶部约束到 "label"底部。 在下面的示例中,我将按钮高度与右侧的两个文本视图相匹配(这是您的情况)。其他文本视图受到上述 link 的限制。
更新:回复您的以下评论:
我已经安装了你的应用程序,这是我目前唯一能想到的。由于它是一个 ViewHolder 布局,layout_height 可以设置为 wrap_content,或者如果您愿意,可以修复它。如果你想要我可以发给你xml,我不想淹没答案。
Top TextView 的左约束约束到ImageView 的右约束。此外,顶部 TextView -> 顶部约束被限制在容器顶部,所以是正确的。底部 TextView 被限制在容器底部。中间的 TextViews 被限制为匹配顶部 TextView 的宽度并且与 ImageView 没有联系。
看看我的回答。
ContraintLayout
包含一项功能 - Chains
- 可以实现您的要求:
Chains provide group-like behavior in a single axis (horizontally or vertically).
A set of widgets are considered a chain if they a linked together via a bi-directional connection
Once a chain is created, there are two possibilities:
- Spread the elements in the available space
- A chain can also be "packed", in that case the elements are grouped together
至于你的情况,你必须打包你的 label
和 description
TextViews 并将它们在你 parent 垂直居中:
(确保您使用支持链的 ConstraintLayout
版本)
<?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">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:text="TextView"
app:layout_constraintBottom_toTopOf="@+id/button"
app:layout_constraintLeft_toRightOf="@+id/imageView2"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_chainStyle="packed"/>
<TextView
android:id="@+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="Button\nMkay"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toRightOf="@+id/imageView2"
app:layout_constraintTop_toBottomOf="@+id/textView"/>
<ImageView
android:id="@+id/imageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
android:layout_marginTop="16dp"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/imageView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@mipmap/ic_launcher"/>
<ImageView
android:id="@+id/imageView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:srcCompat="@mipmap/ic_launcher"/>
</android.support.constraint.ConstraintLayout>
25-06-2019 更新(@Saeid Z):
现在在约束布局 1.1.3 中我们必须使用 app:layout_constraintHorizontal_chainStyle="packed"
而不是 app:layout_constraintVertical_chainPacked="true"
要垂直或水平居中,请在布局上设置 opposing constraint。
垂直居中
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
水平居中
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintLeft_toLeftOf="parent"