[Android] ConstraintSet으로 View의 제약조건 수정하기

2022. 12. 1. 17:26·Android/UI
반응형

ConstraintSet은 xml에서 ConstraintLayout이 하는 동작을 코드 상으로 할 수 있게 해주는 클래스이다.

앱에서 어떤 조건에 따라 한 레이아웃을 화면 상단에 배치하거나 하단에 배치하는 등의 연출이 가능하다.

 

예제로 버튼을 클릭했을 때 검정색 뷰의 위치를 옮겨보려고 한다.

상단, 중단, 하단 텍스트가 존재하고 각 텍스트마다 이동 버튼이 존재한다.

이동 버튼을 클릭하면 검정색의 뷰가 해당 텍스트 아래로 이동된다.

참고 자료


ConstraintSet 예제

class MainActivity : AppCompatActivity() {
    private lateinit var mBinding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        mBinding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(mBinding.root)

        mBinding.btnTop.setOnClickListener {
            setConstraint("TOP")
        }

        mBinding.btnMiddle.setOnClickListener {
            setConstraint("MIDDLE")
        }

        mBinding.btnBottom.setOnClickListener {
            setConstraint("BOTTOM")
        }
    }

    /**
     * layout 이동 메서드
     */
    private fun setConstraint(location: String) {
        val constraints = ConstraintSet()
        constraints.clone(mBinding.clMain)
        constraints.removeFromVerticalChain(mBinding.clMove.id)
        constraints.removeFromHorizontalChain(mBinding.clMove.id)

        when(location) {
            "TOP" -> setConstraintConnect(constraints, mBinding.tvTop.id)
            "MIDDLE" -> setConstraintConnect(constraints, mBinding.tvMiddle.id)
            "BOTTOM" -> setConstraintConnect(constraints, mBinding.tvBottom.id)
        }

        constraints.applyTo(mBinding.clMain)
    }

    /**
     * 전달받은 targetId를 기준으로 clMove에 제약조건 적용 메서드
     */
    private fun setConstraintConnect(constraints: ConstraintSet, targetId: Int) {
        // clMove의 app:layout_constraintStart_toStartOf="targetId"와 같음
        constraints.connect(
            mBinding.clMove.id,
            ConstraintSet.START,
            targetId,
            ConstraintSet.START
        )

        // clMove의 app:layout_constraintTop_toBottomOf="targetId"와 같음
        constraints.connect(
            mBinding.clMove.id,
            ConstraintSet.TOP,
            targetId,
            ConstraintSet.BOTTOM
        )

        // marginTop 5 부여
        constraints.setMargin(
            mBinding.clMove.id,
            ConstraintSet.TOP,
            // dp 값 적용
            TypedValue.applyDimension(
                TypedValue.COMPLEX_UNIT_DIP,
                5f,
                resources?.displayMetrics
            ).toInt()
        )
    }
}

ConstraintSet의 clone에 ConstraintLayout을 넘겨주면 해당 ConstraintLayout의 정보(제약 조건)를 복사하게 된다.

정보를 얻게 된 ConstraintSet 객체는 넘겨받은 ConstraintLayout의 자식 뷰들을 제어할 수 있게 된다.

 

이 정보로 알 수 있는 것은 내가 옮기고자 하는 뷰가 있다면, 그 뷰의 부모 Layout이 ConstraintLayout이어야 한다는 것이다.

(+ ConstraintLayout의 자식 뷰만 제어가 가능하다.)

/**
 * layout 이동 메서드
 */
private fun setConstraint(location: String) {
    val constraints = ConstraintSet()
    constraints.clone(mBinding.clMain)
    constraints.removeFromVerticalChain(mBinding.clMove.id)
    constraints.removeFromHorizontalChain(mBinding.clMove.id)

    when(location) {
        "TOP" -> setConstraintConnect(constraints, mBinding.tvTop.id)
        "MIDDLE" -> setConstraintConnect(constraints, mBinding.tvMiddle.id)
        "BOTTOM" -> setConstraintConnect(constraints, mBinding.tvBottom.id)
    }

    constraints.applyTo(mBinding.clMain)
}

객체를 생성하고 옮기려는 뷰(clMove)의 부모 Layout인 clMain을 clone에 전달했다.

 

이후 removeFromVertical(Horizontal)Chain을 사용했는데, 전달된 뷰의 수직(수평) 제약을 제거한 것이다.

해당 코드를 넣지 않으면 처음 xml에서 초기화된 제약 조건이 남아있기 때문에 원하는 결과가 안 나올 수 있다.

 

마지막에 applyTo로 전달받은 ConstraintLayout에 제약 조건을 적용한다.


/**
 * 전달받은 targetId를 기준으로 clMove에 제약조건 적용 메서드
 */
private fun setConstraintConnect(constraints: ConstraintSet, targetId: Int) {
    // clMove의 app:layout_constraintStart_toStartOf="targetId"와 같음
    constraints.connect(
        mBinding.clMove.id,
        ConstraintSet.START,
        targetId,
        ConstraintSet.START
    )

    // clMove의 app:layout_constraintTop_toBottomOf="targetId"와 같음
    constraints.connect(
        mBinding.clMove.id,
        ConstraintSet.TOP,
        targetId,
        ConstraintSet.BOTTOM
    )

    // marginTop 5 부여
    constraints.setMargin(
        mBinding.clMove.id,
        ConstraintSet.TOP,
        // dp 값 적용
        TypedValue.applyDimension(
            TypedValue.COMPLEX_UNIT_DIP,
            5f,
            resources?.displayMetrics
        ).toInt()
    )
}

connect를 이용해서 두 뷰 사이의 제약 조건을 설정할 수 있다.

해당 동작을 통해 검정색의 뷰가 TextView의 수평으로 시작점, 수직으로는 아래에 위치할 수 있게 된다.

 

setMargin을 이용하면 뷰 사이에 마진도 적용할 수 있다.


전체 코드

반응형

'Android > UI' 카테고리의 다른 글

[Android] RecyclerView에서 페이징 처리하기 #1  (0) 2022.12.16
[Android] DrawerLayout으로 Side Navigation 구현하기  (0) 2022.12.15
[Android] addView로 Layout에 View 추가하기  (0) 2022.11.28
[Android] TextWatcher로 텍스트 변경될 때마다 이벤트 처리하기  (0) 2022.11.23
[Android] Expandable RecyclerView 구현하기  (0) 2022.11.22
'Android/UI' 카테고리의 다른 글
  • [Android] RecyclerView에서 페이징 처리하기 #1
  • [Android] DrawerLayout으로 Side Navigation 구현하기
  • [Android] addView로 Layout에 View 추가하기
  • [Android] TextWatcher로 텍스트 변경될 때마다 이벤트 처리하기
O_Gyong
O_Gyong
안드로이드 기술 정리
  • O_Gyong
    O_Gyong's TECH
    O_Gyong
    • 분류 전체보기 (80) N
      • Android (57) N
        • ADB (4)
        • Architecture (1)
        • Data (5)
        • Network & Connecting (4)
        • Security & Privacy (3)
        • UI (24)
        • 기타 (2)
        • 이슈 처리 (14) N
      • Android Studio (5)
      • Firebase (2)
      • Git (3)
      • 작업 일지 (13)
  • 최근 글

  • 인기 글

  • 태그

    ADB
    SharedPreferences
    Bluetooth
    GIT
    MQTT
    Kotlin
    Android Studio
    해상도
    BLE
    Android
    loading
    kizitonwose
    in-app update
    Navigation
    recyclerview
    webview
    Room
    issue
    compose
    hilt
    Andoird
    Paging3
    firebase
    Pagination
    TabLayout
    CalendarView
    CameraX
    paging
    flow
    github
  • 링크

    • GitHub
  • 블로그 메뉴

    • 홈
    • 태그
    • 방명록
  • hELLO· Designed By정상우.v4.10.4
O_Gyong
[Android] ConstraintSet으로 View의 제약조건 수정하기
상단으로

티스토리툴바