2020 年 7 月更新: ConstrainedWidth/Height は何をしますか?
constrainedWidth/Height
多くの人が、これを true (デフォルトは false) に設定すると正確には何をするのかと尋ね続けました。私は最終的に(Googleの従業員から)答えを得たので、この投稿に来る人々が持ち続けているすべての疑問を解決する代わりに、ここに私が集めたものがあります(一部は私の言葉であり、一部は元のGoogleの問題の引用からの直接の引用です.
ConstraintLayout
関連するすべてのビューのディメンションを決定する必要があり、そのビューがどのように制約されているかに応じて、さまざまな計算を実行する必要があります。
与えられたビュー:
- 固定次元の場合、CL はその次元のみを使用します。
- の場合
match_parent
、CL は親の正確な寸法を使用します
- の場合
wrap_content
、CL はウィジェットにそのサイズを尋ねますが、固定サイズとして使用します
- の場合
0dp
、CL は寸法に制約を適用します
1) 固定寸法
これは、幅/高さが固定されているビューです24dp
。この場合、CL はその値を使用するだけで、サイズ変更に関してそのウィジェットに必要な他の計算はありません。
2)match_parent
これは CL には当てはまらないといつも思っていましたが、以前のバージョンと同じように動作し、親の次元を取得し、それを「固定」として使用することが判明しました。上記の #1 とは異なり、CL は親ディメンションがここで使用できることがわかっていることを確認する必要があるため、これは計算コストが高くなる可能性があると思います。これは実際には有効ではないといつも思っていたので、これを使用したことはありませんでした。
3)wrap_content
予想どおり、ビューはその「必要なサイズ」を決定する必要があるため、ImageView の場合は、ソースに基づいて imageView に寸法を尋ねます。上記の数が取得された後、#1 のように固定サイズとして使用されます。
4)0dp
これは、各次元 (幅と高さ) に制約を適用し、アルゴリズムの結果によって次元の値を決定できるようにすることで、CL が輝く場所です。
では、なぜこれが必要なのですか (constrainedWidth/Height)?
最初に理解しておくべきこと0dp
は、スプレッドとラップの動作 (およびパーセント) があることです。をラップするために、エンジンはビューのディメンションwrap_content
(上記の #3) から開始しますが、必要に応じて制約が変更されるのを待ちます。テキストビューの幅にラップを使用し、その制約により画面の端 (開始/終了から親) に固定されているとします。それらはさまざまな方向に引っ張られている可能性があります。テキストビューは、テキストをラップするために小さくする必要があり、制約はウィジェットの端を引っ張って親の開始/終了に到達します。ここで戦いがあります。(テキストがスペースよりも大きい場合、戦闘は依然として存在しますが、反対方向になります)。
この属性が存在する理由は、一部のウィジェット ( _Like textView
) がいくつかのショートカットを使用し、 がある0dp
場合、それらが常に正しく更新されるとは限らないためです。0dp + weights を使用した LinearLayoutsが同じことを行ったことに注意することが重要です(したがって、これが LL でも問題であった理由)。を使用することにより、TextView のようなウィジェットは、必要に応じてラッピング動作constrainedWidth/Height
で 0dp を正しく使用できます。ビューにそれ自体を正しく再測定する機会を与えます。
この問題は主に、TexView を再利用するときに発生します (他のどのビューがこの恩恵を受けるかは正確にはわかりませんが、テキストを含むものには計算のショートカットやハックが含まれる傾向があり、再測定を正しくトリガーするためにこの余分な情報が必要になる場合があります)。 )。TextView のようなテキストを含むウィジェットの再利用は、これが最も必要とされる場所です。ViewHolder が ConstraintLayout にある RecyclerView を考えてみてください (非常に一般的です)。スクロールすると、ViewHolder が再利用され、別の「データ モデル」に再バインドされます。この属性がないと、TextView は、来る可能性のある新しいテキストのサイズを再計算するか、失敗する可能性があります。
これが理にかなっていることを願っています。
tl;dr
: これは、特に RecyclerView で、特に RecyclerView で再利用したときに寸法を再計算できない一部のウィジェットの潜在的な問題を修正するための回避策ですが、ほとんどの場合、それに限定されません。
そこにあります。:)
2018 年 7 月の更新:
を使用している場合ConstraintLayout 1.1.0
、使用する正しいプロパティapp:layout_constrainedWidth="true"
は、古いapp:layout_constraintWidth_default="wrap"
(および対応する高さ) の代わりになります。
2017 年 11 月の更新
私は Constraint Layouts 1.0.2 を使用していますが、ネストの少ないソリューションを見つけましたapp:layout_constraintWidth_default="wrap"
(1.0.0 で導入されたプロパティですが、この投稿で使用していたベータ版にはありませんでした)。
FrameLayout
を含む の代わりに、LinearLayout
すべてを削除して、次のようにすることができます。
<android.support.constraint.ConstraintLayout
android:id="@+id/new_way_container"
android:layout_height="wrap_content"
android:layout_width="0dp" // THIS GUY USES ALL THE WIDTH.
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:ellipsize="end"
android:id="@+id/some_text"
android:layout_height="wrap_content"
android:layout_width="0dp" //NO WRAP CONTENT, USE CONSTRAINTS
android:lines="1"
android:maxLines="1"
app:layout_constraintEnd_toStartOf="@+id/disclosure_arrow"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed" //CHAIN IT for biasing.
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintWidth_default="wrap" /> //THIS IS THE KEY THAT WILL CAUSE THIS TO WORK
<ImageView
android:id="@+id/disclosure_arrow"
android:layout_height="wrap_content"
android:layout_width="10dp"
app:layout_constraintBottom_toTopOf="@id/some_text"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/some_text"
app:layout_constraintTop_toBottomOf="@id/some_text"
app:srcCompat="@drawable/your_vector_image" />
</android.support.constraint.ConstraintLayout>
これは、ハックやガイドライン、またはハードコーディングされたサイズなしで、私が望むことを効果的に行います。
TextView は Constraints によって提供されるサイズを使用します (通常の状況では、これは間違っているか、「親」を超えて大きくなるということを意味します) が、新しい属性のおかげで、これらの制約は、コンテンツが小さい/大きい。
iOS プライオリティよりもはるかに優れていると言わざるを得ません。(少なくとも、私には理解しやすいです)。これについてGoogleに賛成です:)
OLD ANSWER(まだ必要な場合)。
Nicolas Roard の回答に基づいて、基本的に使用可能なスペースを計算するカスタム コンテナーを作成し、プログラムで TextView の maxWidth を設定するつもりでした。プロジェクトに別のクラス、単体テスト、バグの可能性のあるセットなどを追加する代わりに、いくつかのレイアウトをネストするという少し効率の悪い方法を試しました。黎明期からレイアウトをネストしてきたこと、これがスクロール リスト ビューに表示されたり、移動しすぎたり (またはまったく移動しない) することはなく、ConstraintLayouts を使用してほとんどの階層をフラット化していることを考慮して (これまでにない方法で) !) では、これがより適切にサポートされるまで、少しネストすることはそれほど悪いことではないと思います。
だから私がしたことは、基本的に、FrameLayout を使用することでした。これは、設計によって最適化された (または考えられた) 1 つの子を持つように設計されています (さらに多くの子を含めることができます)。この FrameLayout は、次のように ConstraintLayout ルールが適用されたものです。
<FrameLayout
android:id="@+id/hostTextWithCaretContainer"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintRight_toRightOf="parent">
<!-- MY CONTENT GOES HERE -->
</FrameLayout>
したがって、私の実際のアプリでは、この FrameLayout は別の ConstraintLayout 内にあり、その左にアイコンがあり、その他のものがありますが、この例のために、この FrameLayout の左/右を任意のスペースに「固定」する必要があると想像してください。占拠したい。この例parent
では、すべての制約で使用していることがわかりますが、この FrameLayout の左右に他のウィジェットが存在する可能性があります。ConstraintLayout の魔法のおかげで、これは利用可能なすべてのスペースを占有します。
トリックの 2 番目の部分は次のとおりです... ConstraintLayout は、FrameLayout が「すべてのスペース」を使用し、それ以上 (またはそれ以下) になることは決してないことを保証するため、内部で LinearLayout を使用できるようになりました... そのように...</p>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:id="@+id/textView"
android:layout_height="wrap_content"
android:layout_width="0dp"
tools:text="Some Text"
android:text="Some Text"
android:textAlignment="viewStart"
android:layout_gravity="center_vertical"
android:gravity="start"
android:ellipsize="end"
android:maxLines="1"
android:layout_weight="1"/>
<ImageView
android:id="@+id/caret"
android:layout_width="8dp"
android:layout_height="8dp"
app:srcCompat="@drawable/ic_selection"
android:contentDescription=""
android:layout_gravity="center_vertical"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
</LinearLayout>
賢明な読者は、LinearLayoutwrap_content
の幅が非常に重要であることに気付くでしょう。これは、子 TextView の幅が 0dp で aが 1 である可能性があるため、他のすべてのウィジェットが幅を計算した後に、使用可能なすべての空き領域を使用weight
することを意味します。
この特定のケースでは、他の子 (ImageView)caret
には重みが指定されておらず、幅が固定されているため、TextView は空き領域を他の人と共有/分割する必要はなく、すべてを取得できます (ただし、空き領域のみです。幅は 0 dp です)。
この非効率的なアプローチは、必要に応じて ConstraintLayout Magicが少なくても、私が望んでいたことを効果的に達成します。
プラス面としては、カスタム ビューを作成し、計算を実行しrequestLayout()
、すべての計算が完了した後に を発行する必要がありませんでした。この非効率的なアプローチはスケーリングされます/スケーリングされるべきであり、 ConstraintLayout が有効な代替手段を提供するまでは、そのままで十分かもしれません。
ソーシャル メディアで返信し、最終的にこれについて考える時間をとった Google のエンジニアに感謝します。たぶん将来、彼らが ConstraintLayout 1.1 に関するタスクやストーリー ポイントを書いているときに、これを思い出して、良い解決策を思いつくでしょう。