|

Android ConstraintLayout 완전 정리 (15) – ConstraintLayout Flow 사용법: Chip과 태그를 자동 줄바꿈 배치하기

Android ConstraintLayout 완전 정리 (15) – ConstraintLayout Flow 사용법: Chip과 태그를 자동 줄바꿈 배치하기
ConstraintLayout Flow는 여러 chip이나 tag View를 줄바꿈 배치할 때 유용합니다.

ConstraintLayout Flow는 여러 View를 한 줄 또는 여러 줄로 배치하고 싶을 때 쓰는 helper입니다. chip, tag, 필터 버튼처럼 View 개수는 정해져 있지만 화면 폭에 따라 줄바꿈이 필요한 UI에 잘 맞습니다.

Flow는 RecyclerView처럼 데이터를 반복해서 그리는 목록 컴포넌트가 아닙니다. 이미 XML에 있는 여러 View를 constraint_referenced_ids로 참조하고, 그 View들을 Flow가 정한 방식으로 배치한다고 이해하면 쉽습니다.


ConstraintLayout Flow 핵심 정리

  • Flow는 여러 참조 View를 줄바꿈 배치하는 helper입니다.
  • 참조할 View는 app:constraint_referenced_ids에 적습니다.
  • flow_wrapMode로 줄바꿈 방식을 정합니다.
  • flow_horizontalGap, flow_verticalGap으로 간격을 조절합니다.
  • flow_maxElementsWrap으로 한 줄에 둘 최대 개수를 제한할 수 있습니다.
  • 동적 데이터 목록이 길어지는 화면은 RecyclerView가 더 적합합니다.
ConstraintLayout Flow로 chip View를 자동 줄바꿈 배치하는 이미지
Flow는 여러 참조 View를 한 줄 또는 여러 줄로 배치할 수 있는 helper입니다.

Flow는 어떤 문제를 해결하나

ConstraintLayout에서 버튼 3개 정도를 가로로 놓는 문제라면 chain만으로도 충분합니다. 하지만 chip이 5개, 6개가 되고 화면 폭에 따라 두 줄로 넘어가야 한다면 chain만으로는 XML이 복잡해집니다.

Flow는 이런 고정 View 묶음을 하나의 helper가 배치하도록 도와줍니다. 각 chip은 독립된 View이고, Flow는 그 View들의 배치 규칙을 담당합니다.

<androidx.constraintlayout.helper.widget.Flow
    android:id="@+id/tagFlow"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="tagAndroid,tagKotlin,tagXml,tagLayout"
    app:flow_wrapMode="chain"
    app:flow_horizontalGap="8dp"
    app:flow_verticalGap="8dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

핵심은 Flow 안에 chip View를 넣는 것이 아니라는 점입니다. Flow는 컨테이너라기보다 참조 id를 보고 배치에 참여하는 helper에 가깝습니다.


wrapMode none, chain, aligned 차이

ConstraintLayout Flow wrapMode none chain aligned 차이 이미지
wrapMode는 Flow가 참조 View를 한 줄, 여러 줄, 행과 열 정렬 중 어떤 방식으로 배치할지 결정합니다.

공식 Flow 레퍼런스는 referenced widgets를 세 가지 방식으로 배치할 수 있다고 설명합니다. none은 한 방향의 chain처럼 보고, chain은 공간이 부족할 때 여러 chain으로 감싸며, aligned는 행과 열 정렬을 더 의식하는 방식입니다.

  • none: 한 줄 또는 한 방향 배치를 먼저 생각할 때 사용합니다.
  • chain: chip이 여러 줄로 자연스럽게 넘어가야 할 때 자주 검토합니다.
  • aligned: 행과 열이 맞아 보이는 그리드형 배치가 필요할 때 검토합니다.

gap으로 chip 사이 간격 정리하기

ConstraintLayout Flow horizontalGap과 verticalGap 간격 설명 이미지
horizontalGap은 같은 줄의 좌우 간격, verticalGap은 줄 사이 간격을 조절합니다.

Flow에서 간격은 읽기 쉬운 UI를 만드는 데 중요합니다. chip이 너무 붙으면 터치 영역과 가독성이 나빠지고, 너무 멀어지면 하나의 그룹처럼 보이지 않습니다.

<androidx.constraintlayout.helper.widget.Flow
    android:id="@+id/filterFlow"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="chipAll,chipAndroid,chipKotlin,chipXml"
    app:flow_wrapMode="chain"
    app:flow_horizontalGap="10dp"
    app:flow_verticalGap="12dp" />

flow_horizontalGap은 같은 줄에서 좌우 간격을 조절하고, flow_verticalGap은 줄 사이 간격을 조절합니다. 두 값을 분리해서 생각하면 디자이너가 원하는 chip 간격을 맞추기 쉽습니다.


maxElementsWrap으로 한 줄 개수 제한하기

ConstraintLayout Flow maxElementsWrap으로 한 줄 개수를 제한하는 이미지
maxElementsWrap은 한 줄에 배치할 최대 참조 View 수를 제한할 때 유용합니다.

화면이 넓어도 한 줄에 너무 많은 chip이 늘어서면 읽기 어려울 수 있습니다. 이때 flow_maxElementsWrap으로 한 줄에 배치할 개수를 제한할 수 있습니다.

<androidx.constraintlayout.helper.widget.Flow
    android:id="@+id/categoryFlow"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="categoryA,categoryB,categoryC,categoryD,categoryE"
    app:flow_wrapMode="chain"
    app:flow_maxElementsWrap="3" />

이 설정은 화면 폭이 충분해도 3개 이후부터 다음 줄로 넘기는 식의 배치를 만들 때 유용합니다. 단, 실제 결과는 Flow의 orientation, style, bias, parent 폭과 함께 봐야 합니다.


필터 chip UI 예시

ConstraintLayout Flow로 필터 chip UI를 배치하는 실전 예시
필터 chip처럼 View 수가 정해진 UI는 Flow로 간결하게 배치할 수 있습니다.

Flow가 가장 이해하기 쉬운 예시는 필터 chip UI입니다. 정렬 대상이 데이터 목록이 아니라 XML에 미리 선언된 몇 개의 View라면 Flow가 코드를 단순하게 만들어 줍니다.

  • 카테고리 필터 chip
  • 설정 화면의 선택 버튼 묶음
  • 검색 조건 태그
  • 상세 화면의 고정 badge 묶음

상품 옵션 chip 예시

ConstraintLayout Flow로 상품 옵션 chip을 줄바꿈 배치하는 예시
상품 옵션처럼 선택지 수가 정해진 UI도 Flow 적용 후보가 될 수 있습니다.

상품 상세 화면에서도 Flow를 생각해 볼 수 있습니다. 색상, 사이즈, 배송 옵션처럼 선택지 수가 정해져 있고, 화면 폭이 좁아질 때 자연스럽게 다음 줄로 넘어가야 하는 UI라면 Flow가 잘 맞습니다.

<androidx.constraintlayout.helper.widget.Flow
    android:id="@+id/productOptionFlow"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    app:constraint_referenced_ids="optionBlack,optionWhite,optionSmall,optionMedium,optionLarge,optionToday"
    app:flow_wrapMode="chain"
    app:flow_horizontalGap="8dp"
    app:flow_verticalGap="10dp" />

이 예시의 핵심은 option View들이 데이터 목록처럼 무한히 늘어나는 구조가 아니라는 점입니다. 미리 선언한 몇 개의 선택지를 보기 좋게 배치하는 문제이므로 Flow가 적합합니다.


프로필 badge 예시

ConstraintLayout Flow로 프로필 badge 묶음을 줄바꿈 배치하는 예시
길이가 다른 badge가 섞인 화면에서는 Flow의 wrapMode와 gap을 함께 봐야 합니다.

프로필 화면의 badge 묶음도 Flow 예시로 좋습니다. 짧은 badge와 긴 badge가 섞이면 한 줄에 몇 개가 들어갈지 화면 폭에 따라 달라지기 때문에, gap과 wrapMode를 함께 확인해야 합니다.

  • 짧은 badge만 있을 때보다 긴 badge가 섞일 때 줄 수가 늘어날 수 있습니다.
  • badge를 한 줄에 너무 많이 밀어 넣기보다 줄바꿈 결과를 의도적으로 설계하는 편이 좋습니다.
  • badge 개수가 서버 데이터에 따라 크게 늘어나는 화면이라면 RecyclerView나 별도 chip group 구성을 다시 검토합니다.

Flow와 RecyclerView는 다르게 선택한다

ConstraintLayout Flow와 RecyclerView 사용 기준 비교 이미지
Flow는 고정된 View 묶음에, RecyclerView는 데이터 기반 목록에 더 잘 맞습니다.

Flow를 RecyclerView 대체로 생각하면 위험합니다. Flow는 고정된 여러 View의 배치 helper입니다. 서버에서 받아오는 데이터가 길어지고, 스크롤과 재사용이 필요하고, item 개수가 계속 바뀐다면 RecyclerView가 더 자연스럽습니다.

  • Flow가 어울리는 경우: View 개수가 적고 고정되어 있으며, 화면 폭에 맞춰 줄바꿈만 필요하다.
  • RecyclerView가 어울리는 경우: 데이터 개수가 많거나 계속 바뀌고, 스크롤과 item 재사용이 필요하다.

긴 chip이 섞일 때 확인할 점

ConstraintLayout Flow에서 긴 chip 텍스트와 화면 폭에 따라 줄바꿈이 달라지는 이미지
긴 텍스트가 섞이면 실제 화면 폭에서 줄바꿈 결과를 확인해야 합니다.

같은 Flow 설정이라도 chip 텍스트 길이가 달라지면 실제 줄바꿈 결과가 달라집니다. 다국어, 사용자 입력, 서버에서 내려오는 라벨이 섞이는 화면에서는 좁은 화면 기준으로 한 번 더 확인해야 합니다.

  • 긴 chip이 한 줄을 거의 차지하면 다음 chip이 예상보다 빨리 다음 줄로 내려갈 수 있습니다.
  • 긴 텍스트를 허용할지, max width를 줄지, 말줄임 처리를 할지 UI 기준을 정해야 합니다.
  • Flow 설정만 보고 끝내지 말고 실제 화면 폭에서 Preview를 확인합니다.

자주 하는 실수

ConstraintLayout Flow에서 constraint_referenced_ids 누락으로 배치 대상에서 빠지는 예시
Flow가 기대대로 배치하지 않으면 참조 id 누락부터 확인해야 합니다.
  • constraint_referenced_ids에 id를 빠뜨려 일부 View가 Flow 배치에 참여하지 않는다.
  • Flow를 ViewGroup처럼 생각하고 안쪽에 child를 넣는 방식으로 이해한다.
  • 동적 데이터 목록까지 Flow로 해결하려고 한다.
  • gap을 주지 않아 chip이 너무 붙어 보인다.
  • wrapMode만 바꾸고 parent 폭, maxElementsWrap, orientation 조건을 함께 보지 않는다.
  • 긴 chip 텍스트가 들어오는 화면에서 좁은 폭 Preview를 확인하지 않는다.

한 번에 정리

  • ConstraintLayout Flow는 여러 참조 View를 줄바꿈 배치하는 helper다.
  • 참조 View는 constraint_referenced_ids에 적는다.
  • wrapMode는 none, chain, aligned 관점으로 배치 방식을 나눈다.
  • horizontalGap, verticalGap은 chip 사이 간격을 조절한다.
  • maxElementsWrap은 한 줄 최대 개수를 제한할 때 쓴다.
  • 상품 옵션, 프로필 badge처럼 고정 선택지 UI는 Flow 예시로 적합하다.
  • 데이터 기반 긴 목록은 Flow보다 RecyclerView를 먼저 검토한다.

고정된 chip이나 tag 묶음은 Flow, 길고 변하는 데이터 목록은 RecyclerView로 나눠 생각하세요.


마무리

Flow는 ConstraintLayout에서 여러 View를 다루는 방식 중 chain보다 한 단계 더 묶음 배치에 가까운 helper입니다. 특히 chip, tag, 필터 버튼처럼 화면 폭에 따라 줄바꿈이 필요한 UI를 만들 때 유용합니다.

공식 기준은 AndroidX Flow 레퍼런스를 확인했습니다. 다음 16편에서는 Layer helper로 여러 View를 함께 이동, 회전, 확대하는 방법을 이어서 정리하겠습니다.

Android ConstraintLayout 완전 정리 이전 글 모음

Android ConstraintLayout 완전 정리 (1) – ConstraintLayout 기본 사용법: LinearLayout과 다른 제약 기반 배치 이해하기

Android ConstraintLayout 완전 정리 (2) – ConstraintLayout start end top bottom 제약 정리: View를 원하는 위치에 붙이는 법

Android ConstraintLayout 완전 정리 (3) – ConstraintLayout goneMargin과 margin 차이: View.GONE일 때 간격이 바뀌는 이유

Android ConstraintLayout 완전 정리 (4) – ConstraintLayout width height 정리: wrap_content, 0dp, match_constraint 차이

Android ConstraintLayout 완전 정리 (5) – ConstraintLayout match_constraint 완전 정리: 0dp가 남은 공간을 채우는 원리

Android ConstraintLayout 완전 정리 (6) – ConstraintLayout bias 사용법: horizontalBias와 verticalBias로 위치 조절하기

Android ConstraintLayout 완전 정리 (7) – ConstraintLayout baseline 정렬: TextView 글자 기준선을 맞추는 방법

Android ConstraintLayout 완전 정리 (8) – ConstraintLayout dimensionRatio 사용법: 1:1, 16:9 이미지 비율 맞추기

Android ConstraintLayout 완전 정리 (9) – ConstraintLayout percent min max 크기 정리: 화면 크기에 맞게 View 조절하기

Android ConstraintLayout 완전 정리 (10) – ConstraintLayout chain 사용법: spread, spread_inside, packed 차이

Android ConstraintLayout 완전 정리 (11) – ConstraintLayout chain weight와 bias: 같은 폭, 다른 비율 배치 만들기

Android ConstraintLayout 완전 정리 (12) – ConstraintLayout Guideline 사용법: percent 기준선으로 화면 나누기

Android ConstraintLayout 완전 정리 (13) – ConstraintLayout Barrier 사용법: 텍스트 길이에 따라 동적 기준선 만들기

Android ConstraintLayout 완전 정리 (14) – ConstraintLayout Group과 Placeholder: 여러 View 숨기기와 위치 바꾸기

함께보면 좋은 글