[Android] FlexboxLayout를 활용한 Chip EditText 만들기
Android

[Android] FlexboxLayout를 활용한 Chip EditText 만들기

728x90

 

 

 이번 프로젝트를 하며 EditText 앞에 Chip이 추가되는 방식의 화면을 구현해야 했다. 기존의 방식은 ChipGroup과 EditText를 사용해서 입력을 하면 ChipGroup에 추가되는 방식이었지만, 마지막 Chip 바로 옆에 입력창이 있었으면 했다. 이러한 방식으로는 도저히 생각나지 않아 다른 방법을 찾아본 결과 FlexboxLayout를 활용한 방법을 알게 되었다.

 FlexboxLayout은 웹에서 사용되던 CSS Flexible Box Layout Module을 안드로이드에 접목하여 개발한 라이브러리이다. 구글에서 만든 라이브러리인데 왜 이제야 알았을까. 뷰를 동적으로 추가해야 한다고 했을 때 자연스러운 모션을 보여줘야 한다면 FlexboxLayout이 좋은 선택지가 될 수 있다.

FlexboxLayout에 대한 자세한 설명은 github.com/google/flexbox-layout에 나와있다.

 

google/flexbox-layout

Flexbox for Android . Contribute to google/flexbox-layout development by creating an account on GitHub.

github.com

 

 

 

 이번에 구현해야 할 화면은 다음과 같다. 네이버 메일 어플에서 받는 사람에 이메일을 추가하는 방식과 같다.

 

 

1. dependency

FlexboxLayout과 Material Chip을 사용할 것이므로 아래 두 개의 의존성을 추가한다.

implementation "com.google.android:flexbox:2.0.1"
implementation 'com.google.android.material:material:1.3.0-alpha03'

 

2. view_chip.xml

<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.chip.Chip xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:theme="@style/Widget.MaterialComponents.Chip.Action"
    android:layout_height="wrap_content"
    android:layout_width="wrap_content"
    app:closeIconEnabled="true" />

 

3. activity_main.xml

 FlexboxLayout 안에 자식으로 EditText를 추가한다. 아래 Activity 코드에서 나오겠지만, Chip은 FlexboxLayout의 자식으로 추가하게 된다.

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <com.google.android.flexbox.FlexboxLayout
        android:id="@+id/flex_box_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:flexWrap="wrap"
        app:alignItems="stretch"
        app:alignContent="stretch" >

        <EditText
            android:id="@+id/edit_text"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:hint="입력"
            android:imeOptions="actionDone"
            android:maxLines="1"
            android:singleLine="true"
            android:inputType="text|textCapWords"
            app:layout_flexGrow="1"/>

    </com.google.android.flexbox.FlexboxLayout>
</FrameLayout>

 

 

4. Activity

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        edit_text.setOnKeyListener { v, keyCode, event ->
            if (event.action == KeyEvent.ACTION_DOWN && keyCode == KeyEvent.KEYCODE_ENTER) {
                val et = v as EditText
                val name = et.text.toString()

                flex_box_layout.addChip(name)

                et.text = null
            }
            return@setOnKeyListener false
        }
    }

    private fun FlexboxLayout.addChip(text: String) {
        val chip = LayoutInflater.from(context).inflate(R.layout.view_chip, null) as Chip
        chip.text = text

        val layoutParams = ViewGroup.MarginLayoutParams(ViewGroup.MarginLayoutParams.WRAP_CONTENT, ViewGroup.MarginLayoutParams.WRAP_CONTENT)
        layoutParams.rightMargin = dpToPx(4)

        chip.setOnCloseIconClickListener {
            removeView(chip as View)
        }

        addView(chip, childCount - 1, layoutParams)
    }
    
    private fun FlexboxLayout.getAllChips(): List<Chip> {
        return (0 until childCount).mapNotNull { index ->
            getChildAt(index) as? Chip
        }
    }

    private fun FlexboxLayout.clearChips() {
        val chipViews = (0 until childCount).mapNotNull { index ->
            val view = getChildAt(index)
            if (view is Chip) view else null
        }
        chipViews.forEach { removeView(it) }
    }
}

fun Context.dpToPx(dp: Int): Int
        = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp.toFloat(), resources.displayMetrics).roundToInt()

 예제 코드라서 최대한 간단하게 만들었다. 핵심은 addChip()이다. inflate를 통해 Chip 객체를 생성한 뒤, FlexboxLayout에 addview 해준다. 이때 주의할 점은, FlexboxLayout의 자식 중에 EditText를 포함하고 있기 때문에 addView 할 때 index를 childCount - 1로 설정해준다.

 

 이번에 FlexboxLayout을 처음 알게 되고 사용해봤는데 굉장히 재미있는 레이아웃인 것 같다. 잘만 사용한다면 기존의 자주 사용하던 ConstraintLayout이나 LinearLayout으로 구현하기 힘든 레이아웃들도 쉽게 구현할 수 있을 듯하다.

 

참고 : code.luasoftware.com/tutorials/android/android-tag-input-show-material-chip-as-tag-on-left-of-edittext/

 

 

728x90