この記事について
この記事は、以下のような人に役立ちます。つまり私のことです。
- 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を使っても、想定通りに動くことは普通にあります。
たとえば、以下のようなレイアウトファイルを作ったとします。
<?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クラスのコードは、記事の最後に補足で載せておきます)
<?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>
<?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を表示することができました。
<?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クラスのコード
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)
}