[Android] dual thumb SeekBar - SeekBar Custom / RangeSlider

andkjyk·2022년 1월 16일
0

파트너찾기의 필터 화면을 보면

thumb가 양쪽에 있어 범위를 지정할 수 있는 Bar가 필요한데,

  1. thumb가 원형(default)이 아닌 사각형으로 custom된 형태
  2. trackColor와 inactive trackColor custom
  3. 아래 구력 부분에는 thumb 밑에 ‘1년’이라고 사용자의 동작을 감지하여 텍스트를 띄워주는 듯

이런 특징들이 있어서 고민 중



~~방법1. android-range-seek-bar library 사용~~

https://github.com/YahooArchive/android-range-seek-bar

양 옆에 min, max라고 써있고, thumb custom에 한계가 있어서 적용 불가

방법2. crystal-range-seekbar library 사용

https://github.com/syedowaisali/crystal-range-seekbar

이 정도까지 custom 했는데,

  1. step 속성이 안먹힘
  2. 양끝에 padding이 생김

등의 문제로 일단 보류하고 방법3을 진행중이다.

여기까지 한 코드는 아래에

  1. bar의 height custom 불가
<com.crystal.crystalrangeseekbar.widgets.CrystalRangeSeekbar
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:paddingHorizontal="0dp"
                app:layout_constraintTop_toBottomOf="@id/partner_filter_age_tv"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                android:layout_marginTop="8dp"
                app:corner_radius="0"
                app:min_value="20"
                app:max_value="60"
                app:bar_color="@color/color_10"
                app:steps="10"
                app:bar_highlight_color="@color/primary"
                app:left_thumb_image="@drawable/seekbar_thumb_img"
                app:right_thumb_image="@drawable/seekbar_thumb_img"
                app:data_type="_integer"/>

방법3. RangeSlider custom하기

Material Design

괜찮은 자료가 없어서 위 공식문서 참고해서 custom중이다.

일단은 1/14까지 빠른 진행을 위해 방법2로 해놓고 수정하는 방향으로..


방법4. Jay-Goo 라이브러리

https://github.com/Jay-Goo/RangeSeekBar

Top 15 Seekbar and Slider Github UI Libraries and Components [Java, Swift, Kotlin]

성공!!!! 제이구님 감사합니다..

[안드로이드 java] dual/multi thumbs seekbar -> RangeSlider를 이용해 만들기](https://beeyoo0o0ncha.tistory.com/24)

How to make double seekbar in android?

[Android ] SeekBar 커스터마이징](https://its-blog.tistory.com/79)

<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/partner_filter_age_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@+id/partner_filter_gender_layout"
android:layout_marginTop="31dp">

        <TextView
            android:id="@+id/partner_filter_age_min_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_age_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_age_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_age_tv"
            android:layout_marginStart="23dp"
            android:text="@string/partner_filter_age_min"
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_age_min_unit_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_age_min_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_age_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_age_tv"
            android:text="@string/partner_filter_age_unit"
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_age_to_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_age_min_unit_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_age_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_age_tv"
            android:text=" ~ "
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_age_max_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_age_to_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_age_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_age_tv"
            android:text="@string/partner_filter_age_max"
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_age_max_unit_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_age_max_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_age_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_age_tv"
            android:text="@string/partner_filter_age_unit"
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <com.jaygoo.widget.RangeSeekBar
            android:id="@+id/partner_filter_age_range_sb"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            app:layout_constraintTop_toBottomOf="@id/partner_filter_age_tv"
            app:layout_constraintStart_toStartOf="@id/partner_filter_age_tv"
            app:layout_constraintEnd_toEndOf="@id/partner_filter_age_tv"
            app:rsb_gravity="bottom"
            app:rsb_mode="range"
            app:rsb_progress_color="@color/primary"
            app:rsb_thumb_drawable="@drawable/age_range_thumb"
            app:rsb_min="20"
            app:rsb_max="60"
            app:rsb_min_interval="0"
            app:rsb_tick_mark_mode="other"
            app:rsb_steps="4"
            app:rsb_step_auto_bonding="true"
            app:rsb_step_height="2dp"
            app:rsb_step_width="2dp"
            app:rsb_step_color="@color/transparent"/>

        <TextView
            android:id="@+id/partner_filter_age_min_bottom_tv"
            android:layout_width="wrap_content"
            android:layout_height="34dp"
            android:gravity="center"
            app:layout_constraintTop_toBottomOf="@id/partner_filter_age_range_sb"
            app:layout_constraintStart_toStartOf="parent"
            android:layout_marginStart="2dp"
            android:text="@string/partner_filter_age_min_bottom"
            android:textColor="@color/black"
            android:textSize="11sp"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_age_max_bottom_tv"
            android:layout_width="wrap_content"
            android:layout_height="34dp"
            android:gravity="center"
            app:layout_constraintTop_toBottomOf="@id/partner_filter_age_range_sb"
            app:layout_constraintEnd_toEndOf="parent"
            android:layout_marginEnd="2dp"
            android:text="@string/partner_filter_age_max_bottom"
            android:textColor="@color/black"
            android:textSize="11sp"
            android:fontFamily="@font/applesdgothicneoregular"/>

</androidx.constraintlayout.widget.ConstraintLayout>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/partner_filter_ball_capability_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/partner_filter_age_layout"
        android:layout_marginTop="31dp">
        <TextView
            android:id="@+id/partner_filter_ball_capability_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:text="@string/partner_filter_ball_capability"
            android:textStyle="bold"
            android:textSize="14sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/black"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_ball_capability_min_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_ball_capability_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_ball_capability_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_ball_capability_tv"
            android:layout_marginStart="23dp"
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:text="@string/partner_filter_ball_capability_year_min_bottom"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_ball_capability_to_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_ball_capability_min_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_ball_capability_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_ball_capability_tv"
            android:text=" ~ "
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <TextView
            android:id="@+id/partner_filter_ball_capability_max_tv"
            android:layout_width="wrap_content"
            android:layout_height="35dp"
            app:layout_constraintStart_toEndOf="@id/partner_filter_ball_capability_to_tv"
            app:layout_constraintTop_toTopOf="@id/partner_filter_ball_capability_tv"
            app:layout_constraintBottom_toBottomOf="@id/partner_filter_ball_capability_tv"
            android:text="@string/partner_filter_ball_capability_year_max_bottom"
            android:textSize="13sp"
            android:textAlignment="center"
            android:gravity="center"
            android:textColor="@color/primary"
            android:fontFamily="@font/applesdgothicneoregular"/>

        <com.jaygoo.widget.RangeSeekBar
            android:id="@+id/partner_filter_ball_capability_range_sb"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            app:layout_constraintTop_toBottomOf="@id/partner_filter_ball_capability_tv"
            app:layout_constraintStart_toStartOf="@id/partner_filter_ball_capability_tv"
            app:layout_constraintEnd_toEndOf="@id/partner_filter_ball_capability_tv"
            app:rsb_gravity="bottom"
            app:rsb_mode="range"
            app:rsb_progress_color="@color/primary"
            app:rsb_thumb_drawable="@drawable/age_range_thumb"
            app:rsb_min="0"
            app:rsb_max="10"
            app:rsb_min_interval="0"
            app:rsb_tick_mark_mode="other"
            app:rsb_steps="10"
            app:rsb_step_auto_bonding="true"
            app:rsb_step_height="2dp"
            app:rsb_step_width="2dp"
            app:rsb_step_color="@color/transparent"/>

        <TextView
            android:id="@+id/partner_filter_ball_capability_min_bottom_tv"
            android:layout_width="wrap_content"
            android:layout_height="34dp"
            android:gravity="center"
            app:layout_constraintTop_toBottomOf="@id/partner_filter_ball_capability_range_sb"
            app:layout_constraintStart_toStartOf="parent"
            android:text="@string/partner_filter_ball_capability_year_min_bottom"
            android:textColor="@color/black"
            android:textSize="11sp"
            android:fontFamily="@font/applesdgothicneoregular"/>

    </androidx.constraintlayout.widget.ConstraintLayout>
    

onCreateView or initAfterBinding(umc템플릿 BaseFragment상속 시) 에 다음과 같이 구현

val ageRangeSeekbar = binding.partnerFilterAgeRangeSb
        val ballCapacityRangeSeekBar = binding.partnerFilterBallCapabilityRangeSb

        ageRangeSeekbar.setProgress(20f,60f)
ballCapacityRangeSeekBar.setProgress(0f, 10f)

ageRangeSeekbar.setOnRangeChangedListener(object: OnRangeChangedListener{
override fun onRangeChanged(view: RangeSeekBar?, leftValue: Float, rightValue: Float, isFromUser: Boolean) {
if(leftValue.toInt()% 10 == 0){
binding.partnerFilterAgeMinTv.text= leftValue.toInt().toString()
                }
if(rightValue.toInt()% 10 == 0){
binding.partnerFilterAgeMaxTv.text= rightValue.toInt().toString()
                }
//                changeSeekBarIndicator(rangeSeekbar.leftSeekBar, leftValue)
//                changeSeekBarIndicator(rangeSeekbar.rightSeekBar, rightValue)
}

override fun onStartTrackingTouch(view: RangeSeekBar?, isLeft: Boolean) {

            }

override fun onStopTrackingTouch(view: RangeSeekBar?, isLeft: Boolean) {

            }
        })

ballCapacityRangeSeekBar.setOnRangeChangedListener(object: OnRangeChangedListener{
override fun onRangeChanged(view: RangeSeekBar?, leftValue: Float, rightValue: Float, isFromUser: Boolean) {

if(leftValue.toInt()% 1 == 0){
var leftUnit = "년"
                    if(leftValue < 1f){// == 0f로 하면 0과 1사이에서 0개월이 아닌 0년으로 뜨는 구간이 생김
leftUnit = "개월"
}
binding.partnerFilterBallCapabilityMinTv.text= leftValue.toInt().toString()+ leftUnit
}

if(rightValue.toInt()% 1 == 0){
var rightUnit = "년"
                    if(rightValue < 1f){
rightUnit = "개월"
}
binding.partnerFilterBallCapabilityMaxTv.text= rightValue.toInt().toString()+ rightUnit
}
            }

override fun onStartTrackingTouch(view: RangeSeekBar?, isLeft: Boolean) {

            }

override fun onStopTrackingTouch(view: RangeSeekBar?, isLeft: Boolean) {

            }
        })

참고 자료
[안드로이드 java] dual/multi thumbs seekbar -> RangeSlider를 이용해 만들기](https://beeyoo0o0ncha.tistory.com/24)

How to make double seekbar in android?

[Android ] SeekBar 커스터마이징](https://its-blog.tistory.com/79)

0개의 댓글