와챠의 우당탕탕 코딩 일기장

[Android/Kotlin]BottomSheetDialog/round corner/아래에 뜨는 다이얼로그로 선택하는 스피너 본문

코딩 일기장/Android(Kotlin)

[Android/Kotlin]BottomSheetDialog/round corner/아래에 뜨는 다이얼로그로 선택하는 스피너

minWachya 2022. 2. 19. 12:32
반응형

아래 화면은 '동네 고영희' 앱 UI입니다!!!

 

결과 화면

  • 스피너(TextView임) 클릭하면 아래에서 다이얼로그(BottomSheetDialog)가 나옴
  • 아이템 선택 시 선택한 아이템으로 스피너(TextView) text 변경
  • 아래로 드래그 시 다이얼로그 내려감(기본 기능임)
  • 클릭 효과 적용

 


만들어보자~~~

 

요약:

  1. spinner 처럼 생긴 TextView 만들기
  2. res>drawable>spinner_bottom.xml 생성:레이아웃 맨 위의 오른쪽, 왼쪽 모서리를 둥글게 하기 위함
  3. res>layout>spinner_custom_layout.xml 생성: BottomSheetDialog Layout 만들기
  4. res>values>themes>themes.xml에 아래 style 추가: 뒷배경 투명 처리
  5. 1번의 TextView 클릭 시 bottomSheetDialog 보이기 + dialog에 sytle 추가

 

1, spinner 처럼 생긴 TextView 만들기

xml에서 Textview 부분만 가져왔다.

// TNR
<TextView
            android:id="@+id/tnrSpinner"
            android:hint="@string/cat_add_tnr_hint"	// 수술 여부 선택
            android:textSize="16sp"
            style="@style/Widget.AppCompat.Spinner.Underlined"	// 밑줄+스피너처럼 보이게 함
            android:backgroundTint="@color/beige_E3DECF"	// 밑줄 색 변경
            android:layout_height="55dp"/>

 

 

2, res>drawable>spinner_bottom.xml 생성:

레이아웃 맨 위의 오른쪽, 왼쪽 모서리를 둥글게 하기 위함

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

    <corners
        android:topLeftRadius="15dp"
        android:topRightRadius="15dp"
        />
</shape>

 

3, res>layout>spinner_custom_layout.xml 생성:

BottomSheetDialog Layout 만들기

완성 모양 보기 편하라고 visibilitygone 삭제하고 text 추가한 화면, 원래는 x빼고 새하얌

  • 체크 포인트!!
  •  ConstraintLayout
    • app:behavior_peekHeight: 기본 높이 설정
    • app:behavior_hideable="true": dialog 고정 조건, 항상 표시할 경우 peekHeight
    • app:behavior_draggable="true": 사용자가 dialog의 높이를 손가락으로 조절 가능
    • android:background="@drawable/spinner_bottom": 모서리 둥글게
    • app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
  • TextView
    • 나는 이 BottomSheetDialog를 여러 곳에서 사용할 건데, 그때마다 아이템 수에 맞게 TextView를 동적 생성하는 게 귀찮아서... 최대 아이템 크기인 6에 맞춰서 TextView도 6개로 제작
    • 아이템 추가할 때마다 TextView의 visibility, text 수정하려고 함
    • 터치 효과를 위해 android:background="?attr/selectableItemBackground"​ 추가
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/bottomSheetDashBoardLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    app:behavior_hideable="true"
    app:behavior_draggable="true"
    android:background="@drawable/spinner_bottom"
    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginStart="20dp"
        android:layout_marginTop="24dp"
        android:textColor="@color/brown_473A22"
        android:textSize="16sp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <ImageView
        android:id="@+id/imgClose"
        android:layout_width="14dp"
        android:layout_height="14dp"
        android:src="@drawable/ic_close"
        android:layout_marginTop="29dp"
        android:layout_marginEnd="20dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/text1"
        android:visibility="gone"
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/brown_473A22"
        android:textSize="14sp"
        android:layout_marginTop="31dp"
        android:paddingStart="20dp"
        android:paddingBottom="15dp"
        android:paddingTop="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/title"
        tools:ignore="RtlSymmetry" />

    <TextView
        android:id="@+id/text2"
        android:visibility="gone"
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/brown_473A22"
        android:textSize="14sp"
        android:paddingStart="20dp"
        android:paddingBottom="15dp"
        android:paddingTop="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text1"
        tools:ignore="RtlSymmetry" />

    <TextView
        android:id="@+id/text3"
        android:visibility="gone"
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/brown_473A22"
        android:textSize="14sp"
        android:paddingStart="20dp"
        android:paddingBottom="15dp"
        android:paddingTop="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text2"
        tools:ignore="RtlSymmetry" />

    <TextView
        android:id="@+id/text4"
        android:visibility="gone"
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/brown_473A22"
        android:textSize="14sp"
        android:paddingStart="20dp"
        android:paddingBottom="15dp"
        android:paddingTop="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text3"
        tools:ignore="RtlSymmetry" />

    <TextView
        android:id="@+id/text5"
        android:visibility="gone"
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/brown_473A22"
        android:textSize="14sp"
        android:paddingStart="20dp"
        android:paddingBottom="15dp"
        android:paddingTop="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text4"
        tools:ignore="RtlSymmetry" />

    <TextView
        android:id="@+id/text6"
        android:visibility="gone"
        android:background="?attr/selectableItemBackground"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textColor="@color/brown_473A22"
        android:textSize="14sp"
        android:paddingStart="20dp"
        android:paddingBottom="15dp"
        android:paddingTop="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text5"
        tools:ignore="RtlSymmetry" />
    
    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="15dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/text6"/>

</androidx.constraintlayout.widget.ConstraintLayout>

4, res>values>themes>themes.xml에 아래 style 추가: 뒷배경 투명 처리

우리는 모서리를 둥글게 하고, 3번에서 backgroud를 통해 모서리를 둥글게 적용했지만...

막상 실행하면 

https://stackoverflow.com/questions/10795078/dialog-with-transparent-background-in-android

위와 같은 문제 발생... corner 처리했는데도 그 뒷배경이 투명하지 않아서, 결국 모서리가 동그라미가 아니라 네모로 보이는...암튼 뒷배경을 이런 식으로 투명화 처리를 해줘야 한다.

<!--Custom BottomSheetDialog: corner 뒷배경 투명 처리-->
<style name="DialogCustomTheme" parent="android:Theme.Holo.Dialog.NoActionBar">
    <item name="android:windowBackground">@android:color/transparent</item>
    <item name="android:colorBackgroundCacheHint">@null</item>
</style>

 

5, 1번의 TextView 클릭 시 bottomSheetDialog 보이기 + dialog에 sytle 추가

  • 체크 포인트~
  • arrTextViewId: bottomSheetLayout의 TextView id 배열!! 이걸 사용해서 아이템 수만큼 TextView가 보일 수 있도록 설정했다. 이걸 설정하는 함수가 setBottomSheetView()!!
  • resources.getStringArray(R.array.cat_add3_tnr_array): res>values>spinner_array.xml을 만들어 배열을 만들어줘야 한다. 이때 title을 맨 위에 두어야 다이얼로그가 원하는 대로 만들어진다.
    • <?xml version="1.0" encoding="utf-8"?>
      <resources>
          <!--fragment_cat_add3-->
          <!--TNR 스피너 배멸-->
          <string-array name="cat_add3_tnr_array">
              <item>TNR</item>  <!--title-->
              <item>O</item>
              <item>X</item>
              <item>모름</item>
          </string-array>
          <!--선호 사료 스피너 배열-->
          <string-array name="cat_add3_food_array">
              <item>선호 사료</item>  <!--title-->
              <item>습식</item>
              <item>건식</item>
              <item>편식 안 함</item>
              <item>모름</item>
          </string-array>
      </resources>
package com.example.dongnaegoyang

import android.app.AlertDialog
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.*
import com.example.dongnaegoyang.databinding.FragmentCatAdd3Binding
import com.google.android.material.bottomsheet.BottomSheetDialog

private const val TAG = "mmmCatAddFragment3"

private var _binding: FragmentCatAdd3Binding? = null
private val binding get() = _binding!!

class CatAddFragment3 : Fragment() {
    // BottomDialog 위한 spinner_custom_layout.xml 아이디
    val arrTextViewId = arrayListOf(R.id.title, R.id.text1, R.id.text2, R.id.text3, R.id.text4, R.id.text5, R.id.text6)

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?,
    ): View {
        _binding = FragmentCatAdd3Binding.inflate(inflater, container, false)
        val view = binding.root

        // TNR 선택 스피너 설정
        val tnrBottomSheetView = layoutInflater.inflate(R.layout.spinner_custom_layout, null)
        val tnrBottomSheetDialog = BottomSheetDialog(requireContext(), R.style.DialogCustomTheme) // dialog에 sytle 추가
        val tnrArray = resources.getStringArray(R.array.cat_add3_tnr_array)
        tnrBottomSheetDialog.setContentView(tnrBottomSheetView)
        setBottomSheetView(tnrBottomSheetView, tnrArray, tnrBottomSheetDialog, binding.tnrSpinner)
        binding.tnrSpinner.setOnClickListener {
            tnrBottomSheetDialog.show()
        }

        // 선호 사료 선택 스피너 설정
        val foodBottomSheetView = layoutInflater.inflate(R.layout.spinner_custom_layout, null)
        val foodBottomSheetDialog = BottomSheetDialog(requireContext(), R.style.DialogCustomTheme) // dialog에 sytle 추가
        val foodArray = resources.getStringArray(R.array.cat_add3_food_array)
        foodBottomSheetDialog.setContentView(foodBottomSheetView)
        setBottomSheetView(foodBottomSheetView, foodArray, foodBottomSheetDialog, binding.foodSpinner)
        binding.foodSpinner.setOnClickListener {
            foodBottomSheetDialog.show()
        }

        return view
    }

    override fun onDestroyView() {
        super.onDestroyView()
        _binding = null
    }

    // 스피너 선택 설정
    private fun setBottomSheetView(bottomSheetView: View, arr: Array<String>, dialog: BottomSheetDialog, spinner: TextView) {
        for (i in arr.indices) {
            val textView = bottomSheetView.findViewById<TextView>(arrTextViewId[i])	// TextView를 차례로 가져옴
            textView.text = arr[i]	// text 추가
            textView.visibility = View.VISIBLE	// TextView가 보이게 함
            textView.setOnClickListener {	// 클릭 시 1번의 spinner인 척하는 TextView의 text 변경+dialog 내려가기
                spinner.text = arr[i]
                dialog.dismiss()
            }
        }
        // X버튼 누르면 닫힘
        val closeButton = bottomSheetView.findViewById<ImageView>(R.id.imgClose)
        closeButton.setOnClickListener {
            dialog.dismiss()
        }
    }

}
반응형
Comments