밍쯔와 안작고 안귀여운 에러들🖤

[Android] Custom RatingBar - star : png to svg 본문

Develop/Android | iOS

[Android] Custom RatingBar - star : png to svg

밍쯔 2023. 11. 2. 15:34

[문제]

png로 android 기존의 rating bar를 사용하면

깨지는 경우가 너무 많다,,,,,!! 여러모로 퀄리티를 높이기 위해 png가 아닌 svg로 사용할 수 있도록 작업했다.

 

[해결]

https://github.com/Malligan/RatingBarSvg

 기본적으로는 위의 git을 참고했다.

 

!! 문제 !!

다만 문제는 위의 코드를 그대로 사용하면

wrap_content가 제대로 적용되지 않고 높이가 svg보다 훨씬 큰걸 알 수 있다.

이를 해결하기 위해 view의 사이즈를 결정하는 함수 내 height 결정값을 변경해줬고,

default wrap_content의 사이즈가 제대로 적용된 것을 볼 수 있다.

 

아래는 적용한 custom RatingBar의 적용하는 소스 코드다.

 

activity/fragment xml

<com.....RatingBarSvg
    android:progressDrawable="@drawable/ic_star_large_pink"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:numStars="5"
    android:stepSize="1"/>

 

ic_star_large_pink(selector) xml

<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
    <item 
    	android:id="@android:id/background" 
    	android:drawable="@drawable/ic_star_large_gray" />
    <item 
    	android:id="@android:id/secondaryProgress" 
        android:drawable="@drawable/ic_star_large_gray" />
    <item 
    	android:id="@android:id/progress" 
        android:drawable="@drawable/ic_star_pink_large" />
</layer-list>

 

Custom RatingBarSvg

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.Bitmap
import android.graphics.BitmapShader
import android.graphics.Canvas
import android.graphics.Shader
import android.graphics.drawable.*
import android.graphics.drawable.shapes.RoundRectShape
import android.graphics.drawable.shapes.Shape
import android.util.AttributeSet
import android.view.Gravity
import androidx.appcompat.graphics.drawable.DrawableWrapper
import androidx.appcompat.widget.AppCompatRatingBar

class RatingBarSvg @JvmOverloads constructor(
        context: Context,
        attrs : AttributeSet? = null,
        defStyleAttr : Int = R.attr.ratingBarStyle
) : AppCompatRatingBar(context, attrs, defStyleAttr) {
    private var mSampleTile : Bitmap? = null
    private val drawableShape : Shape
        get() {
            val roundedCorners = floatArrayOf(5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f)
            return RoundRectShape(roundedCorners, null, null)
        }

    init {
        val drawable = tileify(progressDrawable, false) as LayerDrawable
        progressDrawable = drawable
    }

    /**
     * Converts a drawable to a tiled version of itself. It will recursively
     * traverse layer and state list drawables.
     */
    @SuppressLint("RestrictedApi")
    private fun tileify(drawable: Drawable, clip : Boolean) : Drawable {
        if (drawable is DrawableWrapper) {
            var inner: Drawable? = drawable.wrappedDrawable
            if (inner != null) {
                inner = tileify(inner, clip)
                drawable.wrappedDrawable = inner
            }
        } else if (drawable is LayerDrawable) {
            val numberOfLayers = drawable.numberOfLayers
            val outDrawables = arrayOfNulls<Drawable>(numberOfLayers)

            for (i in 0 until numberOfLayers) {
                val id = drawable.getId(i)
                outDrawables[i] = tileify(
                        drawable.getDrawable(i),
                        id == android.R.id.progress || id == android.R.id.secondaryProgress
                )
            }

            val newBg = LayerDrawable(outDrawables)

            for (i in 0 until numberOfLayers) {
                newBg.setId(i, drawable.getId(i))
            }

            return newBg

        } else if (drawable is BitmapDrawable) {
            val tileBitmap = drawable.bitmap
            if (mSampleTile == null) {
                mSampleTile = tileBitmap
            }

            val shapeDrawable = ShapeDrawable(drawableShape)
            val bitmapShader = BitmapShader(
                    tileBitmap,
                    Shader.TileMode.REPEAT, Shader.TileMode.CLAMP
            )
            shapeDrawable.paint.shader = bitmapShader
            shapeDrawable.paint.colorFilter = drawable.paint.colorFilter
            return if (clip)
                ClipDrawable(
                        shapeDrawable, Gravity.LEFT,
                        ClipDrawable.HORIZONTAL
                )
            else
                shapeDrawable
        } else {
            return tileify(getBitmapDrawableFromVectorDrawable(drawable), clip)
        }

        return drawable
    }

    private fun getBitmapDrawableFromVectorDrawable(drawable: Drawable): BitmapDrawable {
        val bitmap = Bitmap.createBitmap(
                drawable.intrinsicWidth + (2).toInt(),
                drawable.intrinsicHeight,
                Bitmap.Config.ARGB_8888
        )
        val canvas = Canvas(bitmap)
        drawable.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
        drawable.draw(canvas)
        return BitmapDrawable(resources, bitmap)
    }

    @Synchronized
    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        if (mSampleTile != null) {
            val width = mSampleTile!!.width * numStars
            setMeasuredDimension(
                    resolveSizeAndState(width, widthMeasureSpec, 0),
                    resolveSizeAndState(mSampleTile!!.height, heightMeasureSpec, 0)
            )
        }
    }
}