web-dev-qa-db-ja.com

ConstraintLayoutの<merge>で<include>を使用する

ConstraintLayout内でタグ<include>および<merge>を使用すると問題が発生します。

フラットビュー階層(制約)を作成したいのですが、再利用可能な要素がまだあります。したがって、レイアウトで<include>を使用し、含まれるレイアウトで<merge>を使用して、ネストされたレイアウトを回避します(特にネストされたConstraintLayoutsを回避します)

だから私はこれを書いた:親レイアウト

<Android.support.constraint.ConstraintLayout
    Android:layout_width="match_parent"
    Android:layout_height="match_parent">

    <include
        Android:id="@+id/review_1"
        layout="@layout/view_movie_note"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/review_2"/>

    <include
        layout="@layout/view_movie_note"
        Android:id="@+id/review_2"
        Android:layout_width="0dp"
        Android:layout_height="0dp"
        Android:layout_marginLeft="7dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/review_1"
        app:layout_constraintRight_toRightOf="parent"
        />

</Android.support.constraint.ConstraintLayout>

そしてこのview_movie_note:

<merge>

    <TextView
        Android:id="@+id/note_Origin"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginBottom="15dp"
        Android:layout_marginStart="5dp"
        app:layout_constraintStart_toStartOf="@+id/cardView2"
        app:layout_constraintTop_toTopOf="parent"
        Android:layout_marginLeft="5dp" />


    <Android.support.v7.widget.CardView
        Android:id="@+id/five_star_view_container"
        Android:layout_width="0dp"
        Android:layout_height="52dp"
        Android:layout_marginBottom="8dp"
        Android:layout_marginTop="10dp"
        Android:elevation="3dp"
        app:cardUseCompatPadding="true"
        app:contentPaddingTop="22dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintHeight_min="52dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/note_Origin">

        <FiveStarsView
            Android:id="@+id/five_star_view"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:layout_gravity="center_horizontal" />

    </Android.support.v7.widget.CardView>

    <Android.support.v7.widget.CardView
        Android:id="@+id/cardView2"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginTop="20dp"
        app:cardBackgroundColor="@color/colorPrimary"
        app:contentPaddingLeft="15dp"
        app:contentPaddingRight="15dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="@+id/note_Origin">

        <TextView
            Android:id="@+id/grade"
            Android:layout_width="wrap_content"
            Android:layout_height="wrap_content"
            Android:textSize="12sp" />

    </Android.support.v7.widget.CardView>


</merge>

私はこれを期待しています

I am expecting this

代わりに私はこれを得た

Instead I get this

明らかに、<include>タグに設定した制約は、含まれるレイアウトの制約によってオーバーライドされます。

これは予想される動作ですか?はいの場合、<include>とConstraintLayoutを使用してフラットレイアウトをどのように維持するのですか?

27
JDenais

短い答え

最善の方法は、冗長レイアウト構造を使用するのではなく、<merge>ブロックを(ネストされた)ConstraintLayoutに置き換えることです。




ConstraintLayoutは優れていますが、各部分の構成と責任の分離ではうまく機能しません

それは間違いです。 ConstraintLayoutは、レイアウトの再利用でうまく機能します。兄弟ビューと親レイアウトの関係に従ってすべての子ビューがレイアウトされるレイアウトは、このように動作します。これは、RelativeLayoutにも当てはまります。


次に、問題はどこにありますか?

<merge> とは何かを詳しく見てみましょう。

ドキュメントは言う

<merge/>タグは、あるレイアウトを別のレイアウトに含めるときに、ビュー階層内の冗長なビューグループを排除するのに役立ちます。

<include>要素を<merge>ブロックのコンテンツで置き換えるのと同じ効果があります。つまり、<merge/>ブロック内のビューは、中間ビューグループなしで親レイアウトに直接配置されます。したがって、<include>要素の制約は完全に無視されます。

この特定の例では、インクルードレイアウトのビューが、2番目のビューとして2回追加されます。


結論

レイアウトリソースファイルは、独立して使用することを目的としています。再利用可能という用語を修飾するには、その親(将来的に追加されるビューグループ)に依存してはなりません。レイアウトを一度だけ含める必要がある場合は、問題ないように見えます。ただし、</merge>は、別のレイアウトで別の位置に配置できないため、その場合も良いアイデアではありません。

明らかに、フラットレイアウト階層のパフォーマンスは向上しています。しかし、時にはそれを犠牲にしなければならないかもしれません。

20
Anees

Androidのドキュメント は言う

<merge />タグは、1つのレイアウトを別のレイアウトに含めるときに、ビュー階層内の冗長なビューグループを排除するのに役立ちます

そして例もあります

メインレイアウトが2つの連続したビューを複数のレイアウトで再利用できる垂直LinearLayoutである場合、2つのビューを配置する再利用可能なレイアウトには独自のルートビューが必要です。ただし、別のLinearLayoutを再利用可能なレイアウトのルートとして使用すると、垂直LinearLayout内に垂直LinearLayoutが作成されます。ネストされたLinearLayoutは、UIのパフォーマンスを低下させること以外の本当の目的を果たしません。

この回答 も参照してください。これにより、マージタグの詳細がわかります。

レイアウトの問題

子レイアウトの場合

<mergeタグ内の子要素に制約を設定します。それは大丈夫ではありません。なぜなら、両方の子レイアウトが親レイアウト内にマージされると、その制約は実行時に破棄されるからです。 (includeタグなしでこれを実行できる場合、制約は機能しますか?)

親レイアウトの場合

<includeタグについても同じです。制約/カスタム属性を<includeタグに与えていますが、これは<mergeタグがルートビューに結合されているため、<includeタグを使用して<mergeにカスタム属性を適用できません。 Bahman answerが機能するのはそのためです。

<includeタグの属性が機能する子レイアウト内にルート要素がある場合、および<mergeタグなし

結論

これが明らかなように、<mergeおよび<includeを使用しているわけではありません。 <includeおよび<mergeタグの機能を理解している。したがって、それらを適切に使用してください。

あなたが解決策を尋ねる場合

ConstraintLayout は、複雑なレイアウトを解決するために導入されました。複雑さを増やさないため。 LinearLayoutを使用して簡単にこれを行うことができる場合、なぜConstraintsを選択するのか

親レイアウト

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:Android="http://schemas.Android.com/apk/res/Android"
    Android:layout_width="match_parent"
    Android:layout_height="match_parent"
    >

    <include
        Android:id="@+id/review_1"
        layout="@layout/view_movie_note"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        Android:layout_weight="1"
        />

    <include
        Android:id="@+id/review_2"
        layout="@layout/view_movie_note"
        Android:layout_width="0dp"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="7dp"
        Android:layout_weight="1"
        />

</LinearLayout>

view_movie_note.xml

<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="wrap_content"
    Android:layout_height="wrap_content">

    <TextView
     .../>

    <Android.support.v7.widget.CardView
    ...
    </Android.support.v7.widget.CardView>

    <Android.support.v7.widget.CardView
    ...
    </Android.support.v7.widget.CardView>
</Android.support.constraint.ConstraintLayout>

output

あなたによく理解していただければ幸いです。

4
Khemraj

解決策として

親レイアウト enter image description here

<?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">


<Android.support.v7.widget.LinearLayoutCompat
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:weightSum="2">

    <include
        Android:id="@+id/review_1"
        layout="@layout/view_movie_note"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toLeftOf="@+id/review_2"
        app:layout_constraintTop_toTopOf="parent" />

    <include
        Android:id="@+id/review_2"
        layout="@layout/view_movie_note"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:layout_marginLeft="7dp"
        Android:layout_weight="1"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@+id/review_1"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

 </Android.support.v7.widget.LinearLayoutCompat>


</Android.support.constraint.ConstraintLayout>

view_movie_note 2]

<?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="wrap_content">

<TextView
    Android:id="@+id/note_Origin"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_marginBottom="15dp"
    Android:layout_marginLeft="5dp"
    Android:layout_marginStart="5dp"
    app:layout_constraintStart_toStartOf="@+id/cardView2"
    app:layout_constraintTop_toTopOf="parent" />


<Android.support.v7.widget.CardView
    Android:id="@+id/five_star_view_container"
    Android:layout_width="wrap_content"
    Android:layout_height="52dp"
    Android:layout_marginBottom="8dp"
    Android:layout_marginTop="10dp"
    Android:elevation="3dp"
    app:cardUseCompatPadding="true"
    app:contentPaddingTop="22dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintHeight_min="52dp"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toBottomOf="@+id/note_Origin">

    <!--<FiveStarsView-->
    <!--Android:id="@+id/five_star_view"-->
    <!--Android:layout_width="wrap_content"-->
    <!--Android:layout_height="wrap_content"-->
    <!--Android:layout_gravity="center_horizontal" />-->

    <RatingBar
        Android:id="@+id/ratingBar"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content" />
</Android.support.v7.widget.CardView>

<Android.support.v7.widget.CardView
    Android:id="@+id/cardView2"
    Android:layout_width="wrap_content"
    Android:layout_height="wrap_content"
    Android:layout_marginTop="20dp"
    app:cardBackgroundColor="@color/colorPrimary"
    app:contentPaddingLeft="15dp"
    app:contentPaddingRight="15dp"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="@+id/note_Origin">

    <TextView
        Android:id="@+id/grade"
        Android:layout_width="wrap_content"
        Android:layout_height="wrap_content"
        Android:textSize="12sp" />

  </Android.support.v7.widget.CardView>
</Android.support.constraint.ConstraintLayout>
2
Artem

includeタグをConstraintLayoutタグでラップしてから、includeタグの属性をこれらの新しいConstraintLayoutタグに移動します。

    <Android.support.constraint.ConstraintLayout
        Android:layout_width="match_parent"
        Android:layout_height="match_parent">

    <Android.support.constraint.ConstraintLayout
            Android:id="@+id/review_1"
            Android:layout_width="0dp"
            Android:layout_height="0dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toLeftOf="parent"
            app:layout_constraintRight_toLeftOf="@+id/review_2">

                    <include  layout="@layout/view_movie_note"  />

   </Android.support.constraint.ConstraintLayout>

    <Android.support.constraint.ConstraintLayout
            Android:id="@+id/review_2"
            Android:layout_width="0dp"
            Android:layout_height="0dp"
            Android:layout_marginLeft="7dp"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintLeft_toRightOf="@+id/review_1"
            app:layout_constraintRight_toRightOf="parent">

                       <include layout="@layout/view_movie_note" />

    </Android.support.constraint.ConstraintLayout>

  </Android.support.constraint.ConstraintLayout>
2
Bahman

mergeはタグであり、ViewGroupではないため、インクルードに渡されるすべてのパラメーターは無視されます...このViewGroupは、複製されたレイアウトでのみフラット化できます。管理する必要がある場合は、 Group ... inflateを介したRelative LayoutへのマージレイアウトからのXML属性

2
Anis BEN NSIR

ファイルview_movie_note.xmlは、ルートに単一のxmlノードを持つ必要があります。そうでない場合、制約に合わせて配置できるレイアウトノードは2つありません(<merge>タグは役に立たない可能性があります)。同一のresIdを持つ2つの同一のレイアウトを持つことは問題に追加されます。これは、制約が常にresIdに対して定義されているためです。

代替アプローチは、a)2つのアイテムを水平方向に分配するために LinearLayout を使用することです-またはb)子ノードがさらにある場合(これを想定します) )、 GridView / RecyclerView 内で、2つの列を持つCardViewGridLayoutsをより適切に使用します。

このような子レイアウトのルートノードとしての単一のCardViewは、適切に整列するために最も少なくなります。

予想される結果によると、ノードを整列するためにConstraintLayoutは必要ありません。GridLayoutManagerを使用すると、表示サイズに応じて列数を調整することもできます。要素の高さが異なる場合、StaggeredGridLayoutManagerもオプションになります。

原則として、フレームワークに対して作業するよりも、フレームワークに対して作業する方が常に簡単です。

0
Martin Zeitler

あなたの質問に関するいくつかの問題:

  1. Android documentation link

また、インクルードレイアウトのルートビューのすべてのレイアウトパラメーター(Android:layout_ *属性)を<include />タグで指定することでオーバーライドできます。そのため、インクルードタグに設定した制約はすべて削除されます。

  1. どれか Android:id includeタグは、インクルードされたレイアウトでマージタグが使用されている場合は上書きされません。

  2. 制約の連鎖と追加は、異なるIDを持つビューで機能します。したがって、同じビューを同じ重みで複数回含めると、includeタグでは機能しません。

とはいえ、コピーして全体を貼り付けることもできます

したがって、この方法でincludeを使用することはできません。

次の3つのオプションがあります。

  1. 他のViewGroupを使用します(たとえば、LinearLayoutおよび制約レイアウト)
  2. ビューの異なるIDを持つincludeレイアウトのコンテンツをコピーして貼り付けます
  3. 含まれるレイアウト全体が水平方向にコピーされるように、ConstraintLayoutコードを変更してスプレッドチェーンをサポートします。

IMO、これらのレイアウトの数が少ない場合は1番目のオプションが最適で、レイアウトが1つしかない場合は2番目のオプションが最適です(質問)、多数のレイアウトがある場合は3番目のオプションが最適です。

0
Rahul Kumar