きっかけ
Google I/OのこれがConstraintLayoutでできているという噂を聞いて、これは!!と思ってConstraintLayuotを始めてみました。![output_sicle.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/9aebeb3d-3010-1b15-006d-35b8157e1a7c.gif)
![output.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/9116baee-9fbe-73e0-09df-abfbf1a12d7a.gif)
![](https://qiita-image-store.s3.amazonaws.com/0/27388/900d9393-3ce6-5d71-41c3-f9fc72014140.png)
基礎的なところ
大きさの指定について、てきとうになってしまっていたのでメモしておきます。Bias
![bias.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/3a073d5b-a30b-5032-69c8-67466ba7a35d.gif)
ConstraintSet
を使うことで、プロパティをいじることができます
val constraintSet = ConstraintSet()
constraintSet.clone(constraintLayout)
val bias = 0.5F
constraintSet.setVerticalBias(R.id.button, bias)
constraintSet.applyTo(constraintLayout)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/4bd9ddbf-fb14-2af5-28da-36c7d63b42c6.png)
Chain
他のViewと同じ大きさなど大きさを整えるような形でのレイアウトが出来ます。 例えば均等配置とか、LinearLayoutのweight=1みたいなのが楽にできます。 また間にあるViewが消えたときもちゃんとLinearLayoutのような動きをするようです。 均等配置 (ちなみにChain Styleというものがあり、CHAIN_SPREAD -> CHAIN_SPREAD_INSIDE -> CHAIN_PACKEDの順番で動きます。見ればわかると思います。)![chain.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/4c5e1aab-04ad-64df-046e-ac83dbab966f.gif)
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintHorizontal_weight="1"
GuideLine
![](https://qiita-image-store.s3.amazonaws.com/0/27388/1c969b6c-7ccd-c42d-ccbf-ec2814bb59bd.png)
app:layout_constraintGuide_begin="20dp"
以下のようにapp:layout_constraintGuide_percent
を使うように修正することがで利用することができます。割合は0〜1で指定します。
app:layout_constraintGuide_percent="0.5"
ConstraintLayoutはViewがgoneのときの挙動もサポートしている
![gone_2.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/7b5768d3-5f77-e06f-466a-c65d7cbee190.gif)
app:layout_goneMarginLeft="200dp"
1.1.0 Beta-1
新しく追加された機能について紹介していきます。
Barrier
なぜ使うか?
以下のようなレイアウトの時に"Settings"という文字列がめっちゃ長くなる可能性がある時、どうやって実装しますか??
通常時 | 長くなった時 |
---|---|
![]() |
![]() |
使い方
こういうときCameraとSettingsのTextViewをレイアウトで囲むのではなくBarrierを使います。 右クリックからHelpers -> Add Vertical barrierで追加できます。(どうでもいいんですが、BarrierのBが大文字と小文字で違いますね)![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/5e9a3177-1693-768a-dfe2-2f1bc3d4c56c.png)
![add.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/c83b9cc9-ea51-eaa2-b969-7335f59089e5.gif)
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/af574c8b-92e2-3af1-6c59-40b661464195.png)
![constraint.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/822f1ec8-4c92-56d3-bac2-c00a9007152c.gif)
![testbarrier.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/f237dee7-8b80-ec97-771c-308de618bbf1.gif)
Group
なぜ使うか?
例えば固定のヘッダーがあったとして、それがいくつものViewで構成されているとき、表示非表示を一気に切り替えるようなときに使います。 (コードをなんとなく見たのですが、今のところvisibilityとelevationのみしか変更できないようです。)使い方
まずConstraintLayoutで右クリックしてAdd Groupします![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/a1927e2a-72fe-567b-3ce6-4f12803b8932.png)
![group.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/fd451929-b046-6d07-fdd3-81b189f0df33.gif)
findViewById<Group>(R.id.group).visibility = View.GONE
PlaceHolder
PlaceHolderは用意しておくことで、簡単にウィジェットの内容を置き換えることができます。
たったこれだけ。最初はなぜこんな動きができるのか意味不明でした。
val onClickListener: (View) -> Unit = { view ->
TransitionManager.beginDelayedTransition(root as ViewGroup)
placeholder.setContentId(view.id)
}
imageA.setOnClickListener(onClickListener)
imageB.setOnClickListener(onClickListener)
imageC.setOnClickListener(onClickListener)
imageD.setOnClickListener(onClickListener)
使い方
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/0be74922-67e0-9e88-a606-9a12aabba6b3.png)
<android.support.constraint.Placeholder
android:id="@+id/placeholder"
...
app:content="@+id/image_d"
... />
![image.png](https://qiita-image-store.s3.amazonaws.com/0/27388/8c094fde-0946-da10-f506-60610ddfa02f.png)
val onClickListener: (View) -> Unit = { view ->
TransitionManager.beginDelayedTransition(placeHolderBinding.root as ViewGroup)
placeholder.setContentId(view.id)
}
imageA.setOnClickListener(onClickListener)
imageB.setOnClickListener(onClickListener)
imageC.setOnClickListener(onClickListener)
imageD.setOnClickListener(onClickListener)
Percent Dimension
以下で%(割合)で大きさを指定できます。(現状、AndroidStudioのUI上で設置できない?)
PercentRelativeLayoutの置き換えが快適になりそうです。
android:layout_width="0dp"
app:layout_constraintWidth_default="percent"
app:layout_constraintWidth_percent="0.5"
Google I/Oのアニメーションについて
![output_sicle.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/9aebeb3d-3010-1b15-006d-35b8157e1a7c.gif)
private final ConstraintSet expandedConstraints = new ConstraintSet();
private ConstraintLayout mainLayout;
// 前もってやっておく レイアウトのConstraintSetの内容をcloneする。
expandedConstraints.clone(mainLayout.getContext(), R.layout.feed_message_card_expanded);
// アニメーション + レイアウト変更
TransitionManager.beginDelayedTransition(parent);
expandedConstraints.applyTo(mainLayout);
constraints.clone(context, R.lay
out.expanded);
で既存のレイアウトから制約だけ取ってきて、それをconstrains.applyTo(constra
intLayout)
で反映させるということを行っています。
本当はTransitionの動きをCustom Transitionを作ってカスタマイズして、画像と文字が重ならないようにしたりなどを行っていますが、Transitionの話になってしまうので、ここでは省略します。
最初の方に出していたサンプルのアプリのコードは以下になります。ConstraintLayoutを使うことで簡単にアニメーションを実装することができます。
https://github.com/takahirom/constraint-layout-samples/blob/master/app/src/main/java/com/github/takahirom/constraint_layout_samples/AnimationActivity.kt
ConstraintLayoutの拡張性
BarrierやGroupはConstraintHelper
クラスを継承して実現されています。
Viewのライフサイクルのタイミングなどで、
指定されたViewの操作が出来きます
カスタムしたのを簡単に作ってみました。ちなみに勝手に作ることも公式にサポートされています。ちゃんとした使い方はちゃんとしたドキュメントが出てからになると思います。
![blink.gif](https://qiita-image-store.s3.amazonaws.com/0/27388/d87db1cf-6847-2a3e-3acf-77bf8d073253.gif)
<com.github.takahirom.constraint_layout_samples.Blink
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="textView,imageView" />
ConstraintHelperを継承したBlinkクラス
class Blink : ConstraintHelper {
constructor(context: Context) : super(context)
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr)
override fun updatePreLayout(container: ConstraintLayout) {
super.updatePreLayout(container)
for (i in 0 until this.mCount) {
val id = this.mIds[i]
val view = container.getViewById(id)
if (view != null) {
ObjectAnimator.ofFloat(view, "alpha", 1F, 0F).apply {
repeatCount = ValueAnimator.INFINITE
repeatMode = ValueAnimator.REVERSE
}.start()
}
}
}
}
まとめ
- いろんなレイアウトの機能が入っていて、 結構いろんなパターンに適応できそう
- ある程度拡張できるように作ってある
- ConstraintLayout 1.1が待ち遠しい
- アニメーションを簡単にできる
参考資料
https://codelabs.developers.google.com/codelabs/constraint-layout/index.html
https://androidstudio.googleblog.com/2017/05/constraintlayout-110-beta-1-release.html
https://medium.com/google-developers/building-interfaces-with-constraintlayout-3958fa38a9f7
https://blog.stylingandroid.com/constraintlayout-chains-spread-chains/
https://www.youtube.com/watch?v=nYb4FUdlLZE
https://academy.realm.io/posts/360-andev-2017-nicolas-roard-advanced-constraintlayout/
https://github.com/googlesamples/android-ConstraintLayoutExamples
https://constraintlayout.com/