Yanolja + MotionLayout #Part1 ~ MotionLayout 에서는 MotionLayout에 대한 간단한 이야기와 기본적인 적용에 관해 설명했습니다. Part2에서는 야놀자 서비스에 도입된 CollapsibleToolbar
내용을 더 설명하겠습니다. 실제로 Part2에 해당하는 내용으로 많은 트러블 슈팅을 했습니다.
좀 더 MotionLayout을 다뤄보겠습니다.
먼저 리스트 화면에서는 Expand/Collapsing
되었을 때의 차이는 아래와 같습니다.
Expand | Collapsing |
---|---|
항목 | 위치 변화 | 크기 변화 | 노출 변화 |
---|---|---|---|
전체 높이 | O | ||
타이틀 텍스트 (CollapsingToolbarLayout 과 동일) |
O | O | |
Home 버튼 | O | ||
Search 버튼 | O | ||
요일 텍스트 | O | O | |
인원 텍스트 | O | O | |
쿠폰 버튼 | O | ||
필터 버튼 | O | ||
맵 버튼 | O |
CollapsibleToolbar
의 ViewGroup 아래에 다양한 항목 및 그에 따른 처리를 하고 있습니다.
Collapsing 을 지원하는 View인 경우 높이가 변한다는 것을 추측할 수 있습니다.
일반적으로 ConstraintLayout 작업시 대부분은 Top-To-Bottom
방식으로 위에서 아래로 Anchor를 처리하면서 레이아웃을 구성하는 방식을 사용합니다. 그러나, CollapsibleToolbar
사용 시에는 동일하게 Start/End에 대한 처리를 해서 동작시켜보시면 실제로 원하던 동작을 하지 않습니다. MotionLayout의 Show Path
를 활성시 움직이는 궤적도 원하던 형태가 아닐 것입니다.
정확한 이유는 파악하지 못했지만, CollapsibleToolbar
를 사용하는 소스를 보면 모두 Bottom-To-Top
형태로 레이아웃을 구성되어 있는 것을 볼 수 있습니다. Top-To-Bottom
로 안되는 이유는 AppBarLayout이 줄어들 때 상단 부분이 사라지는 기능의 연장선이라고 추측하고 있습니다. (개인적인 생각입니다)
Bottom-To-Top | Top-To-Bottom |
---|---|
위 결과는 다음 소스를 수정한 결과입니다
AppBarLayout + CollapsibleToolbar 사용시에는 Bottom-To-Top
로 레이아웃 구성하면 됩니다.
헤더에서 사용하는 타이틀은 Expand/Collapsing
시의 텍스트 크기가 다릅니다. MotionLayout의 Start/End 부분에 다른 텍스트 사이즈를 정의하는 것이 빠른 접근이지만, 실제로 동작 시에는 텍스트의 렌더링이 매끄럽게 그려지지 않습니다. 이 부분은 TextView의 Size 변경 시 일어나는 내부 처리로 인해 일어납니다.
실제 텍스트 사이즈 변경 시에는 TextView는 아래 작업을 합니다.
실제로 View 처리 시 4
/ 5
에 해당하는 항목이 가장 큰 부하가 걸리는 항목입니다. TextView 사이즈가 최소~최대 사이즈의 0.0 ~ 1.0 의 비율로 적용되므로 스크롤 시마다 위의 1~5번 항목이 매번 처리됩니다. 그래서 Scale
/ Pivot
을 이용해 부하를 줄이는 방법으로 빠르게 렌더링할 수 있었습니다.
MotionLayout 이 포함된 뷰의 내용을 변경할 경우에 레이아웃이 갱신되지 않습니다. 이 이유는 MotionLayout의 내부 로직을 보면 좀 더 알 수 있습니다. MotionLayout은 빠른 렌더링을 위해서 View와 각 View에 설정된 MotionController을 임시 저장해서 사용하고 있습니다. 그리고 MotionController 에 정의된 필드조차 많으며 컨트롤에 많은 처리를 해준다는 것을 알 수 있습니다.
public class MotionLayout extends ConstraintLayout implements NestedScrollingParent2 {
...
HashMap<View, MotionController> mFrameArrayList = new HashMap();
...
private void setupMotionViews() {
if (!this.mSetup) {
if (VERSION.SDK_INT < 18 || !this.isInLayout()) {
...
this.mFrameArrayList.clear();
int layoutWidth;
for(layoutWidth = 0; layoutWidth < n; ++layoutWidth) {
View v = this.getChildAt(layoutWidth);
MotionController f = new MotionController(v);
this.mFrameArrayList.put(v, f);
}
...
this.requestLayout();
this.mSetup = true;
}
}
}
...
}
public class MotionController {
...
View mView;
int mId;
private int mCurveFitType = -1;
private MotionPaths mStartMotionPath = new MotionPaths();
private MotionPaths mEndMotionPath = new MotionPaths();
private MotionConstrainedPoint mStartPoint = new MotionConstrainedPoint();
private MotionConstrainedPoint mEndPoint = new MotionConstrainedPoint();
private CurveFit[] mSpline;
private CurveFit mArcSpline;
float stagger_offset = 0.0F;
float stagger_scale = 1.0F;
private int[] mInterpolateVariables;
private double[] mInterpolateData;
private double[] mInterpolateVelocity;
double[] mCycleData = new double[18];
double[] mmCycleVelocity;
private String[] mAttributeNames;
private int[] mAttributeInterpCount;
private int MAX_DIMENSION = 4;
private float[] buff;
private ArrayList<MotionPaths> mMotionPaths;
private int mDrawPath;
private float[] mVelocity;
private ArrayList<Key> mKeyList;
private HashMap<String, TimeCycleSplineSet> mTimeCycleAttributesMap;
private HashMap<String, SplineSet> mAttributesMap;
private HashMap<String, KeyCycleOscillator> mCycleMap;
private KeyTrigger[] mKeyTriggers;
String[] attributeTable;
핵심은, MotionLayout#setupMotionViews
의 내부 리소스가 갱신되도록 호출해주는 방법입니다. 버전에 따라 더 빠른 처리가 가능하겠지만, alpha2 작업시는 MotionLayout#setTransition
를 MotionLayout Setup을 재호출하여 문제를 해결할 수 있습니다. 그로 인해 MotionScene 및 레이아웃의 대대적인 작업도 동반되었습니다.
MotionLayout의 셋업처리를 미리 알았다면 많은 트러블 슈팅이 줄었을 텐데… 눈물이 흐릅니다.
안드로이드에서 elevation
은 그림자를 노출하는 역할을 합니다. 아쉽게도 elevation
은 Android 5.0부터 추가되었습니다. (많은 안드로이드 개발자들의 애증이 담긴 기능입니다.) 보통 4.x의 단말에도 elevation
Field에 값을 부여하더라도 서비스가 죽는 경우는 없습니다. 그림자 효과가 노출이 되지 않을 뿐 특별히 문제가 되는 경우는 적습니다.
그러나, Samsung 4.4.x
에서 MotionLayout 사용시는 이야기가 다릅니다. 결과부터 이야기드리면 100% Crash입니다. 더 슬픈 것은 현재 이슈가 해결 혹은 진행 중인 상태가 아닙니다. 그러니 Android 4.x를 포기
하는 것이 해결법입니다. MotionLayout 내부 문제로 단시간에 해결될 것으로는 보이지 않습니다.
Issue Tracker > ConstraintLayout 2.0.0-alpha2 broke compatability
실제로 위의 영상은 추가적인 MotionLayout 처리와 + Custom Behavior 를 이용한 화면입니다.
MotionLayout
을 실제 프로덕션에 적용해볼 기회가 있어서 좋았습니다. Alpha 버전을 넣을 수 있는 경험도 했습니다. 그리고, 아직 안정 버전이 아니라서 생기는 다양한 트러블 슈팅과 버그로 수많은 고통도 함께했습니다.
Android 개발 시 애니메이션 처리에 대해 아쉬움이 있었지만, MotionLayout
을 이용해 더 쉽게 애니메이션을 적용할 수 있어 기대되는 라이브러리입니다.
comments powered by Disqus
Subscribe to this blog via RSS.
LazyColumn/Row에서 동일한 Key를 사용하면 크래시가 발생하는 이유
Posted on 30 Nov 2024