最近編輯於2018年9月7日
ConstraintLayout
顧名思義,約束佈局———在constraintLayout下的子控件都會受到外來的“力”,從而確定該子控件的位置。
一、constraintLayout來自支持庫,所以要想使用先要在gradle中引入
repositories {
google()
}
dependencies {
implementation 'com.android.support.constraint:constraint-layout:1.1.2'
}
二、“力”的發出者,作用點,作用方向
舉個例子:
app:layout_constraintLeft_toLeftOf="parent"
parent:發出者是parent;
constraintLeft:作用在該子控件的左側;
toLeftOf constraintLeft:從parent的左側拉住該子控件的左側。
“力”的發出者:
parent:包含此控件的constraintLayout;
@+id/button8:其他子控件;
@+id/guideline3:準線———一條用於定位的不可見的線;
舉個例子:
<android.support.constraint.Guideline
android:layout_width="0dp"
android:layout_height="0dp"
android:id="@+id/guideline3"
android:orientation="vertical"
app:layout_constraintGuide_percent="0.5" />
android:orientation="vertical":這是一條從上至下的準線;
app:layout_constraintGuide_percent="0.5":該準線位於constraintLayout的50%處;
這條準線從中間把constraintLayout分成左右兩半;
@+id/barrier7:屏障———由幾個控件組成的一道不可見的“牆”;
舉個例子:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="short"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textView2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:text="longlonglong"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView1" />
<android.support.constraint.Barrier
android:id="@+id/barrier7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="end"
app:constraint_referenced_ids="textView2,textView1" />
<TextView
android:id="@+id/textView3"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:text="@string/lake_discription"這裏很長一段
app:layout_constraintStart_toEndOf="@+id/barrier7"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
app:constraint_referenced_ids="textView2,textView1":由 textView2,textView1組成的“牆”;
app:barrierDirection="end":在textView2,textView1的尾部(一般指右側);
app:layout_constraintStart_toEndOf="@+id/barrier7":這個牆的尾部(寫toStartOf一樣的效果,因爲“牆”只是一條線,不分首尾)拉住textView3的頭部;
最後效果圖:
“力”的方向與作用點:
layout_constraintLeft_toLeftOf
layout_constraintLeft_toRightOf
layout_constraintRight_toLeftOf
layout_constraintRight_toRightOf
layout_constraintTop_toTopOf
layout_constraintTop_toBottomOf
layout_constraintBottom_toTopOf
layout_constraintBottom_toBottomOf
layout_constraintBaseline_toBaselineOf
layout_constraintStart_toEndOf
layout_constraintStart_toStartOf
layout_constraintEnd_toStartOf
layout_constraintEnd_toEndOf
layout_constraintBaseline_toBaselineOf:文字基準線對齊文字基準線,這個這麼理解;
另外:constraintlayout中的app:layout_constraintLeft_toLeftOf="parent"和app:layout_constraintEnd_toEndOf="parent"配合起來用會使佈局靠近parent的右側。
android:layout_width="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="1.0"
app:layout_constraintStart_toStartOf="parent"
等價於
android:layout_width="0dp"//或者wrap_content
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintEnd_toEndOf="parent"
效果圖如下:
三、“力”的大小與bias:
默認情況下,左右的力是相同大小的,上下的力是相同大小的;
左右居中只需要如下:
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent
那麼如何更偏左一點,加大左邊的力?那不是直接拉到最左邊了?假設控件處於一個左右橫置的管子中,那麼只要減少左邊的壓強,那麼控件就會往左跑。跑到哪裏?還是不確定。Google提供了更簡單的方法是控件停在需要的位置:
app:layout_constraintHorizontal_bias="0.3"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent
app:layout_constraintHorizontal_bias="0.3":該控件會停在左邊佔空白30%,右側佔空白70%的位置。
四、邊距:
和原來一樣的邊距表示方式:
android:layout_marginStart
android:layout_marginEnd
android:layout_marginLeft
android:layout_marginTop
android:layout_marginRight
android:layout_marginBottom
另外還提供當某一個方向上的“力”的發出者消失的情況的邊距:
layout_goneMarginStart
layout_goneMarginEnd
layout_goneMarginLeft
layout_goneMarginTop
layout_goneMarginRight
layout_goneMarginBottom
舉個例子:
<Button
android:id="@+id/button4"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="button4"
app:layout_constraintRight_toRightOf="parent"/>
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="10dp"
android:text="button5"
app:layout_constraintRight_toLeftOf="@id/button4"
app:layout_goneMarginRight="110dp"/>
android:layout_marginRight="10dp":button4 visiable或invisiable時button5距離button4 10dp;
app:layout_goneMarginRight="110dp":當button4 gone時button5距離button4變成的點 110dp。
五、圓定位:
<Button android:id="@+id/buttonA" ... />
<Button android:id="@+id/buttonB" ...
app:layout_constraintCircle="@+id/buttonA"
app:layout_constraintCircleRadius="100dp"
app:layout_constraintCircleAngle="45" />
效果如下:
六、控件的大小:
和原來一樣的方式設置最大最小寬高
android:minWidth
android:minHeight
android:maxWidth
android:maxHeight
設置寬高的三種方式:
具體尺寸;
WRAP_CONTENT:包含內容大小;
0dp:相當於原來的MATCH_CONSTRAINT。
使用比例設置寬高:
如下兩例,高將根據寬的長度來設置:
<Button android:layout_width="wrap_content"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="1:1" />
<Button android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintDimensionRatio="H,16:9"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
七、鏈(chain)
幾個在橫向或者豎向上相鄰的兩兩控件互相作用的幾個控件構成鏈。
鏈頭是就是最左邊或者最上面的那個控件,關於鏈的整體屬性設置都需要在鏈頭中設置,在其他鏈結中設置無效。
chainStyle決定鏈中元素互相之間的位置關係
spread:默認方式,鏈結會舒展開;
spread_inside:鏈結會舒展,鏈的兩頭會一直舒展到parent邊;
packed:鏈結會聚在一起。
圖示,以及相應代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto">
<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/button2"
app:layout_constraintHorizontal_chainStyle="spread"/>
<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/button1"
app:layout_constraintRight_toLeftOf="@+id/button3"/>
<Button
android:id="@+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="@+id/button2"
app:layout_constraintRight_toRightOf="parent"/>
<Button
android:id="@+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button1"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/button5"
app:layout_constraintHorizontal_chainStyle="spread_inside"/>
<Button
android:id="@+id/button5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button1"
app:layout_constraintLeft_toRightOf="@+id/button4"
app:layout_constraintRight_toLeftOf="@+id/button6"/>
<Button
android:id="@+id/button6"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button1"
app:layout_constraintLeft_toRightOf="@+id/button5"
app:layout_constraintRight_toRightOf="parent"/>
<Button
android:id="@+id/button7"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button4"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/button8"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintHorizontal_weight="1"/>
<Button
android:id="@+id/button8"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button4"
app:layout_constraintLeft_toRightOf="@+id/button7"
app:layout_constraintRight_toLeftOf="@+id/button9"
app:layout_constraintHorizontal_weight="2"/>
<Button
android:id="@+id/button9"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button4"
app:layout_constraintLeft_toRightOf="@+id/button8"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintHorizontal_weight="3"/>
<Button
android:id="@+id/button10"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button7"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/button11"
app:layout_constraintHorizontal_chainStyle="packed"/>
<Button
android:id="@+id/button11"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button7"
app:layout_constraintLeft_toRightOf="@+id/button10"
app:layout_constraintRight_toLeftOf="@+id/button12"/>
<Button
android:id="@+id/button12"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button7"
app:layout_constraintLeft_toRightOf="@+id/button11"
app:layout_constraintRight_toRightOf="parent"/>
<Button
android:id="@+id/button13"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button10"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toLeftOf="@+id/button14"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintHorizontal_bias="0.3"/>
<Button
android:id="@+id/button14"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button10"
app:layout_constraintLeft_toRightOf="@+id/button13"
app:layout_constraintRight_toLeftOf="@+id/button15"/>
<Button
android:id="@+id/button15"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toBottomOf="@+id/button10"
app:layout_constraintLeft_toRightOf="@+id/button14"
app:layout_constraintRight_toRightOf="parent"/>
</android.support.constraint.ConstraintLayout>
MotionLayout
motionlayout是一種根據動作進行動畫的佈局。
一、motionlayout是constraintlayout支持庫2.0版本推出的,想要使用需要先引入:
implementation 'com.android.support.constraint:constraint-layout:2.0.0-alpha2'
二、所需元素:
以MotionLayout爲根佈局指定需要進行動畫的佈局;
使用app:layoutDescription="@xml/scene_01"指定動畫文件;
在xml文件夾下新建MotionScene爲根節點的動畫文件;
舉個例子:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_02"
android:layout_width="match_parent"
android:layout_height="match_parent">
<View
android:id="@+id/button"
android:background="@color/colorAccent"
android:layout_width="64dp"
android:layout_height="64dp" />
</android.support.constraint.motion.MotionLayout>
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@id/button" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent" />
</ConstraintSet>
</MotionScene>
三、MotionScene文件
Transition節點:motionScene文件必須包含的節點,使用motion:constraintSetStart和motion:constraintSetEnd來指定動畫的第一幀和最後一幀。
motion:constraintSetStart="@layout/motion_01_cl_start"
motion:constraintSetEnd="@layout/motion_01_cl_end"
可以指定第一幀和最後一幀在layout文件下;
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start"
也可以指定第一幀和最後一幀在對應id的ConstraintSet節點下。
ConstraintSet節點:節點下包含多個Constraint節點,每個Constraint節點代表對應id的控件在第一幀或最後一幀的位置。
Constraint節點除了指定位置大小信息外,可以使用
android:alpha="1.0"
android:scaleX="1.1"
android:scaleY="1.1"
android:rotation="-45.0"
android:translationY="8dp"
指定控件透明度、放大倍數、旋轉角度、移動距離,還可以
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="#9999FF" />
通過CustomAttribute節點指定控件的屬性。
來個例子:
<?xml version="1.0" encoding="utf-8"?>
<MotionScene xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:motion="http://schemas.android.com/apk/res-auto">
<Transition
motion:constraintSetEnd="@+id/end"
motion:constraintSetStart="@+id/start">
<OnSwipe
motion:dragDirection="dragRight"
motion:touchAnchorId="@id/button" />
</Transition>
<ConstraintSet android:id="@+id/start">
<Constraint
android:id="@id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginStart="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintStart_toStartOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="@color/colorAccent" />
</Constraint>
</ConstraintSet>
<ConstraintSet android:id="@+id/end">
<Constraint
android:id="@id/button"
android:layout_width="64dp"
android:layout_height="64dp"
android:layout_marginEnd="8dp"
android:alpha="1.0"
android:scaleX="1.1"
android:scaleY="1.1"
android:rotation="-45.0"
android:translationY="8dp"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent"
motion:layout_constraintTop_toTopOf="parent">
<CustomAttribute
motion:attributeName="BackgroundColor"
motion:customColorValue="#9999FF" />
</Constraint>
</ConstraintSet>
</MotionScene>
OnSwipe節點:使用motion:dragDirection="dragLeft"指定動作方向(動作需要在motionLayout佈局中進行),使用motion:touchAnchorId="@id/button"指定需要動畫的控件(多個需要動畫的控件時任意選擇一個id)。
<OnSwipe
motion:touchAnchorId="@+id/sublabel"
motion:dragDirection="dragUp" />
KeyFrameSet節點:指定中間關鍵幀,其下有KeyPosition、KeyAttribute、KeyCycle三種節點分別用來指定控件的位置、屬性、三角函數式位置。但KeyCycle不能與KeyPosition不能同時使用(同時使用只有KeyPosition生效)。
<KeyFrameSet>
<KeyAttribute
android:scaleX="2"
android:scaleY="2"
android:rotation="-45"
motion:framePosition="50"
motion:target="@id/button" />
<KeyPosition
motion:keyPositionType="pathRelative"
motion:percentY="-0.3"
motion:framePosition="50"
motion:target="@id/button"/>
</KeyFrameSet>
<KeyFrameSet>
<KeyCycle
android:translationY="50dp"
motion:framePosition="100"
motion:target="@id/button"
motion:waveOffset="0"
motion:wavePeriod="0"
motion:waveShape="sin" />
<KeyCycle
android:translationY="50dp"
motion:framePosition="50"
motion:target="@id/button"
motion:waveOffset="0"
motion:wavePeriod="1"
motion:waveShape="sin" />
<KeyCycle
android:translationY="50dp"
motion:framePosition="0"
motion:target="@id/button"
motion:waveOffset="0"
motion:wavePeriod="0"
motion:waveShape="sin" />
</KeyFrameSet>
四、ImageFilterView
android.support.constraint.utils.ImageFilterView控件是一個圖片漸變動畫控件;
他有兩種形式;
1、使用android:src="@drawable/roard"、app:altSrc="@drawable/hoford"設置兩張圖片;
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.motion.MotionLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/motionLayout"
app:layoutDescription="@xml/scene_04"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.utils.ImageFilterView
android:id="@+id/image"
android:src="@drawable/roard"
app:altSrc="@drawable/hoford"
android:layout_width="64dp"
android:layout_height="64dp"/>
</android.support.constraint.motion.MotionLayout>
同時使用Crossfade屬性指定第一幀或最後一幀的圖片是src還是altSrc(0爲src圖片,1爲altSrc圖片)。
<CustomAttribute
motion:attributeName="Crossfade"
motion:customFloatValue="0" />
2、使用一張圖片只指定src;
同時使用Saturation屬性指定第一幀或者最後一幀的圖片是黑白的還是彩色的(0爲黑白的,1爲彩色的)。
<CustomAttribute
motion:attributeName="Saturation"
motion:customFloatValue="1" />
五、使用其他動作來指揮動畫
1、只有motionLayout才能只能指揮動畫,我們根佈局必須使用motionLayout或者其子類,所以我們新建一個類繼承motionLayout;
2、需要什麼樣的動作,就要繼承該動作的接口,例如抽屜的抽拉動作接口DrawerLayout.DrawerListener,並在onAttachedToWindow時就監聽這個動作;
3、使用該動作的進度設置motionLayout的進度,調用setProgress()方法。
舉個例子:
import android.content.Context;
import android.support.constraint.motion.MotionLayout;
import android.support.design.widget.AppBarLayout;
import android.util.AttributeSet;
public class CollapsibleToolbar extends MotionLayout implements AppBarLayout.OnOffsetChangedListener {
public CollapsibleToolbar(Context context) {
super(context);
}
public CollapsibleToolbar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CollapsibleToolbar(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void onOffsetChanged(AppBarLayout appBarLayout, int i) {
setProgress(-(float) i / (float) (appBarLayout.getTotalScrollRange()));
}
@Override
protected void onAttachedToWindow() {
super.onAttachedToWindow();
((AppBarLayout) getParent()).addOnOffsetChangedListener(this);
}
}
六、ConstraintHelper
放置在MotionLayout中可以監聽並操作子控件;
使用app:constraint_referenced_ids="imageView9"指示需要監聽操作的控件:
<com.msz.motionlayout.helpers.ExampleFlyinBounceHelper
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:constraint_referenced_ids="imageView9"/>
自定義ConstraintHelper重寫updatePreLayout、updatePostLayout、updatePostMeasure、updatePostConstraints:
import android.animation.ObjectAnimator;
import android.content.Context;
import android.support.constraint.ConstraintHelper;
import android.support.constraint.ConstraintLayout;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.BounceInterpolator;
public class ExampleFlyinBounceHelper extends ConstraintHelper {
protected ConstraintLayout mContainer;
public ExampleFlyinBounceHelper(Context context) {
super(context);
}
public ExampleFlyinBounceHelper(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExampleFlyinBounceHelper(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@Override
public void updatePreLayout(ConstraintLayout container) {
if (mContainer!=container) {
View[] views = getViews(container);
for (int i = 0; i < mCount; i++) {
View view = views[i];
ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationX", - 2000, 0).setDuration(1000);
animator.setInterpolator(new BounceInterpolator());
animator.start();
}
}
mContainer = container;
}
}
這裏進行了一個界面初始化時彈性移動的動畫。
七、LottieAnimationView
可以使用MotionLayout動作的進度控制LottieAnimationView動畫的進度。
1、先引入三方包:
implementation 'com.airbnb.android:lottie:2.5.1'
2、使用LottieAnimationView控件並指定app:lottie_rawRes="@raw/walkthrough"
<com.airbnb.lottie.LottieAnimationView
android:id="@+id/animation_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:lottie_rawRes="@raw/walkthrough"/>
關於Lottie動畫的製作與更多使用,可以參考https://airbnb.design/lottie/ https://www.jianshu.com/p/d0f4c823fa06
https://github.com/airbnb/lottie-android
關於VectorDrawables的製作與使用,可以參考https://jingyan.baidu.com/article/7f766daf8775df4101e1d0e1.html
https://developer.android.com/guide/topics/graphics/vector-drawable-resources
更多參考:https://github.com/googlesamples/android-ConstraintLayoutExamples
https://github.com/1qu212/androidMotionLayoutExamplesmaster
https://developer.android.com/reference/android/support/constraint/ConstraintLayout
https://developer.android.com/reference/android/support/constraint/motion/MotionLayout