LoginSignup
21
11

More than 5 years have passed since last update.

ConstraintLayoutでMATCH_PARENT利用時の想定外の挙動

Last updated at Posted at 2018-01-02

この記事について

この記事は、以下のような人に役立ちます。つまり私のことです。
- ConstraintLayoutを利用している
- ConstraintLayoutの中でmatch_parentを使ってしまっている

ConstraintLayoutの中でMATCH_PARENTを使った時に起きた想定外の挙動を紹介することで、ちゃんとMATCH_CONSTRAINTを使いましょうと伝える自戒を込めた記事になります。

ConstraintLayoutでMATCH_PARENTは推奨されない

そもそもConstraintLayoutでMATCH_PARENTは推奨されません。ConstraintLayoutのドキュメントによると、ConstraintLayoutでは、layout_widthとlayout_heightに以下のどれかを指定します。
- 固定のサイズ(120dpなど)
- WRAP_CONTENT(wrap_content)
- MATCH_CONSTRAINT(実際のレイアウトファイルでは0dpを指定)

そして、MATCH_PARENTを指定することは推奨されない(代わりにMATCH_CONSTRAINTで上下左右の制約をparentにセットする)と書かれています。

Important: MATCH_PARENT is not recommended for widgets contained in a ConstraintLayout. Similar behavior can be defined by using MATCH_CONSTRAINT with the corresponding left/right or top/bottom constraints being set to "parent".

使えないわけではない

ConstraintLayout内でMATCH_PARENTを使っても、想定通りに動くことは普通にあります。
たとえば、以下のようなレイアウトファイルを作ったとします。

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="example.com.constraintlayoutbadexample.MainActivity">

    <TextView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_bright"
        android:gravity="center"
        android:text="Hello World!" />

</android.support.constraint.ConstraintLayout>

この場合は、以下のようにTextViewが画面いっぱいに表示されます。

MATCH_PARENTを使うと起きた想定外の挙動

一応動いてしまうので、私はMATCH_PARENTは推奨されないというのをあまり気にせずについ使ってしまっていました。しかし、以下のようにRecyclerViewをMATCH_PARENT指定で配置していた時に想定外の事態に遭遇しました。(MainActivityクラスのコードは、記事の最後に補足で載せておきます)

activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="example.com.constraintlayoutbadexample.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@android:color/holo_blue_bright" />

</android.support.constraint.ConstraintLayout>
item.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:layout_width="match_parent"
    android:layout_height="56dp">

    <ImageView
        android:id="@+id/imageView"
        android:layout_width="40dp"
        android:layout_height="40dp"
        android:layout_margin="16dp"
        android:background="@color/colorPrimary"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/textView"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginEnd="16dp"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="16dp"
        android:layout_marginStart="16dp"
        android:textSize="16sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toRightOf="@id/imageView"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        tools:text="Item" />

</android.support.constraint.ConstraintLayout>

このレイアウトでリストを表示すると、以下のようになりました。

ImageViewの横にあるはずのTextViewが表示されていません。

MATCH_CONSTRAINT指定で解決

しばらくハマった後、MATCH_CONSTRAINTを指定して、制約をparentにセットすることで無事TextViewを表示することができました。

activity_main.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:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="example.com.constraintlayoutbadexample.MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        android:background="@android:color/holo_blue_bright"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

結論

ConstraintLayoutではMATCH_PARENTは使わずMATCH_CONSTRAINTを使いましょう

補足:MainActivityクラスのコード

MainActivity.kt
package example.com.constraintlayoutbadexample

import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.support.v7.widget.LinearLayoutManager
import android.support.v7.widget.RecyclerView
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import kotlinx.android.synthetic.main.activity_main.*

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recyclerView.layoutManager = LinearLayoutManager(this)
        recyclerView.adapter = ItemAdapter(arrayListOf("hoge", "fuga", "piyo"))
    }
}

private class ItemAdapter(private val items: List<String>) : RecyclerView.Adapter<ItemViewHolder>() {
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ItemViewHolder {
        val view = LayoutInflater.from(parent?.context)
                .inflate(R.layout.item, parent, false)
        return ItemViewHolder(view)
    }

    override fun onBindViewHolder(holder: ItemViewHolder?, position: Int) {
        holder?.textView?.text = items[position]
    }

    override fun getItemCount(): Int = items.size
}

private class ItemViewHolder(itemView: View?) : RecyclerView.ViewHolder(itemView) {
    val textView = itemView?.findViewById<TextView>(R.id.textView)
}
21
11
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
11