본문 바로가기

소소한 팁

[Andorid] 커스텀 토스트 만들기

안드로이드에서 제공하는 기본 토스트바는 띄워지는 시간 minimum 이 2초이고 글짜색이나 배경색, 토스트 위치를 변경하는게 쉽지않다. 따라서 토스트를 커스텀하기 위해서는 커스텀 토스트바를 만들어야 한다.

 

구글링을 해보니 토스트 객체에서 뷰를 가져와 해당 뷰에 백그라운드와 텍스트 색상을 입히는 방식이었다.

내가 짠 코드는 다음과 같다.

 

[ kotlin - extension ]

fun Context.showCustomToast(@StringRes msg: Int) {
    val toast = Toast.makeText(this, msg, Toast.LENGTH_SHORT).apply {
        (view as ViewGroup).apply {
            with(this.getChildAt(0) as TextView) {
                setTextColor(Color.WHITE)
                gravity = Gravity.CENTER
            }
            background.setColorFilter(Color.BLACK, PorterDuff.Mode.SRC_IN) // 타원안에만 색상 적용 - 크래시
        }
        setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL, 0, CommonUtils.dpToPx(100))
    }.apply { show() }

    Timer().schedule(object : TimerTask() {
        override fun run() {
            toast.cancel()
        }
    }, 1000) // 1초 뒤 토스트 종료
}

 

 

위와 같이 토스트를 커스텀으로 작성해 잘 실행되는걸 확인하고 넘어갔는데 어느날 갑자기 파베 Crashlytics 에서 크래시가 났다고 알림이 왔다. 왜그런가 하고 봤더니 background 가 null 인데 setBackground 함수를 호출해서 NPE가 난거였다. 검색해보니 몇몇 popluar 하지 않은 기기에서는 toast 의 background 가 null 일 수 있다고 한다. 그래서 다시 구글링을 통해 레이아웃을 커스텀하게 만들어서 인플레이트해야한다는 글을 보고 그대로 구현했다.

 

[ kotlin - extension ]

fun Context.showCustomToast(@StringRes msg: Int) {
    val toastView = LayoutInflater.from(this).inflate(R.layout.custom_toast_layout, null)
    val tvText = toastView.findViewById<TextView>(R.id.tv_toast)
    tvText.setText(msg)

    val toast = Toast(this).apply {
        view = toastView
        setGravity(Gravity.BOTTOM or Gravity.CENTER_HORIZONTAL, 0, CommonUtils.dpToPx(100))
    }.apply { show() }

    Timer().schedule(object : TimerTask() {
        override fun run() {
            toast.cancel()
        }
    }, 1000) // 1초 뒤 토스트 종료
}

 

[ custom_toast_layout ]

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/custom_toast_container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal"
    android:background="@drawable/custom_toast_layout_bg"> // 배경 세팅

    <TextView
        android:id="@+id/tv_toast"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:textColor="@android:color/white" />

</LinearLayout>

 

[ custom_toast_layout_bg ]

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid
        android:color="@color/colorOnPrimary" /> // 배경색

    <corners
        android:radius="32dp" /> // 코너 라운드

    <padding
        android:left="24dp"
        android:bottom="16dp"
        android:right="24dp"
        android:top="16dp" /> // 패딩

</shape>

레이아웃을 커스텀하게 만든 뒤 백그라운드에 drawable 로 배경색, 라운드, 패딩 처리 등을 처리하고 해당 레이아웃을 토스트에 인플레이트 하는 방식이다. 

이렇게 구현해서 배포한 뒤로는 크래시가 안나고 정상 작동하는걸 확인했다.

 

ps. 여담으로 안드로이드 버전 11에서는 백그라운드에서 위와 같은 방법으로 토스트를 호출하지 못하게 막는다고 한다. 안드로이드 11 만 타겟하는 프로젝트에만 적용된다고 하는데 일단 이런 이슈들이 있다는 것 정도만 알고 넘어가면 될 것 같다.