51

2020 年 7 月の更新:

以下の回答に情報を追加して、なぜ/何が機能constrainedWidth/Heightし、いつそれらを使用できるかをより詳細に説明しました。

2018 年 7 月の更新:

を使用している場合ConstraintLayout 1.1.0、使用する正しいプロパティapp:layout_constrainedWidth="true"は、古いapp:layout_constraintWidth_default="wrap"(および対応する高さ) の代わりになります。更新された回答を参照してください。

2017 年 11 月の更新:

ConstraintLayout 1.0.0安定版 (またはそれ以降) (現時点では 1.0.2)を使用している場合は、レイアウトをネストする必要のないより簡単なソリューションについて、更新された回答を参照してください。

元の質問:

ConstraintLayouts-Beta32016 年 11 月 3 日にリリースされた使用。 ( source )

私は次のことをしようとしています:

|                                        |
|<-[TextView]<->[ImageView] -----------> |
|                                        |

私は次のようなレイアウトで達成しました:

  <TextView
      android:id="@+id/textView"
      android:layout_height="wrap_content"
      android:layout_width="wrap_content"

      app:layout_constraintHorizontal_chainStyle="packed"

      app:layout_constraintLeft_toLeftOf="parent"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintRight_toLeftOf="@+id/caret"
      app:layout_constraintHorizontal_bias="0.0"

      android:text="Some Text"
      android:textAlignment="viewStart"
      android:gravity="start" />

  <ImageView
      android:id="@+id/caret"
      android:layout_width="wrap_content"
      android:layout_height="8dp"
      app:layout_constraintDimensionRatio="1:1"

      app:layout_constraintLeft_toRightOf="@+id/textView"
      app:layout_constraintRight_toRightOf="parent"

      app:layout_constraintTop_toTopOf="@+id/textView"
      app:layout_constraintBottom_toBottomOf="@+id/textView"


      app:layout_constraintHorizontal_bias="0.0"

      android:contentDescription=""
      app:srcCompat="@drawable/ic_selection"
      android:layout_marginStart="8dp"/>

これは問題ないように見えますが、テキストが使用可能なスペースよりも長い場合…</p>

|                                        |
|<-[TextView Larger Than The Space Avail]|
|                                        |

テキスト ビューには、以下を指定するスタイルがあります。

<item name="android:lines">1</item>
<item name="android:maxLines">1</item>
<item name="android:ellipsize">end</item>

うまくいくはずですが、画像を右端までスライドさせてからそこで停止し、テキストビューにスペースがないことを理解させるために必要な制約がわかりません。

私は何が欠けていますか?

注:テキストビューの幅を0dpに設定すると機能しますが、画像は常に右側にあります(水平バイアスは無視されるようです)

注 2: beta2 でも試してみましたが、実際のところ、Beta3にはビジュアル エディターにバグがあるようです。

更新: Xcode/AutoLayout でこれを複製しようとしました:

これは、短いテキストでどのように見えるかです

短いテキスト

同じレイアウトで、テキスト ビューに長いテキストを入力するだけです…</p>

長いテキスト

画像ビューのトレイル (右) 制約を見るとわかるように、右マージンから8 ポイント以上離れています。

また、ラベル (textView) の左側にピン留めされています。

Twitter から学んだことから、現時点では Android の ConstraintLayoutではこれができない可能性があります。

4

5 に答える 5

62

2020 年 7 月更新: ConstrainedWidth/Height は何をしますか?

constrainedWidth/Height多くの人が、これを true (デフォルトは false) に設定すると正確には何をするのかと尋ね続けました。私は最終的に(Googleの従業員から)答えを得たので、この投稿に来る人々が持ち続けているすべての疑問を解決する代わりに、ここに私が集めたものがあります(一部は私の言葉であり、一部は元のGoogleの問題の引用からの直接の引用です.

ConstraintLayout関連するすべてのビューのディメンションを決定する必要があり、そのビューがどのように制約されているかに応じて、さまざまな計算を実行する必要があります。

与えられたビュー:

  1. 固定次元の場合、CL はその次元のみを使用します。
  2. の場合match_parent、CL は親の正確な寸法を使用します
  3. の場合wrap_content、CL はウィジェットにそのサイズを尋ねますが、固定サイズとして使用します
  4. の場合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 に関するタスクやストーリー ポイントを書いているときに、これを思い出して、良い解決策を思いつくでしょう。

于 2016-11-08T15:50:53.147 に答える
4

私の場合、Martin からの 2018 年 7 月の Update は機能しません。

ConstraintLayout 1.1.0 を使用している場合、使用する正しいプロパティ app:layout_constrainedWidth="true"は、古い app:layout_constraintWidth_default="wrap"(および対応する高さ) の代わりになります。

android:width="wrap_content"でテキストビューに使用する必要がありますapp:layout_constrainedWidth="true"android:layout_width="0dp"(match_constraint) は、私の場合、短い文字列用にテキスト ビューを引き伸ばします。

同じ結果を得る別の可能性は、代わりにandroid:layout_width="0dp"with flagを使用することです。app:layout_constraintWidth_max="wrap"

制約レイアウトのフラグの詳細については、 https ://developer.android.com/reference/android/support/constraint/ConstraintLayout のドキュメントを参照してください。

于 2019-01-03T12:32:46.560 に答える
3

実際には、TextViews でそれを行う方法は次のとおりです。次を使用しますandroid:maxWidth

<TextView
    android:id="@+id/textView7"
    android:maxWidth="200dp"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:maxLines="1"
    android:ellipsize="end"
    android:text="TextViewTextViewTextViewTextViewTextViewTextViewTextViewTextViewTextView"
    tools:layout_editor_absoluteY="72dp"
    android:layout_marginStart="16dp"
    app:layout_constraintLeft_toLeftOf="parent"
    android:layout_marginLeft="16dp" />

<Button
    android:id="@+id/button20"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Button"
    tools:layout_editor_absoluteY="56dp"
    android:layout_marginStart="8dp"
    app:layout_constraintLeft_toRightOf="@+id/textView7"
    android:layout_marginLeft="8dp" />

ここに画像の説明を入力

それはまさにあなたが尋ねたものではありません (幅を制限するためにサイズを知って設定する必要があります) が、ConstraintLayout 自体でそれをサポートするまで、それが役立つかもしれません。

于 2016-11-04T00:01:41.357 に答える