안드로이드

[안드로이드] 안으로 둥근 뷰 만들기 (Negative Corner Radius)

tolylim 2022. 7. 21. 15:17

 

 

안드로이드에서 테두리가 밖으로 둥근 뷰는 많이 만들어봤지만 안으로 둥근 뷰는 한번도 만들어 본 적이 없었다.

처음에는 사각형을 만든 후에 테두리에 흰색 원을 겹쳐서 만들어보았지만 그렇게 하면 뷰를 활용하기에 어려움이 있었다.

(ex) 그림자 효과를 넣을 수 없음, 배경색이 단색이 아닌 이미지나 그라데이션이 들어갈 경우 티가 남

 

 

 

찾아보니 안드로이드에 CornerTreament라는 것이 있는데 이것을 활용해서 쉽게 만들 수 있었다!

 

https://developer.android.com/reference/com/google/android/material/shape/CornerTreatment

 

CornerTreatment  |  Android Developers

CornerTreatment public class CornerTreatment extends Object java.lang.Object    ↳ com.google.android.material.shape.CornerTreatment Known direct subclasses CutCornerTreatment A corner treatment which cuts or clips the original corner of a shape with a

developer.android.com

 

 

 

CornerTreatment의 getCornerPath를 오버라이드 해서 작업을 해준 후 View에 확장함수를 만들어서 background를 적용하듯이 사용하였다. 

CornetTreatment의 getCornerPath와 View의 확장함수는 아래 블로그를 참고하여 구현하였다.

 

https://blog.stylingandroid.com/material-shape-cutting-corners/

 

Material Shape: Cutting Corners

One of the foundations of Material Design 2.0 is the ability to define shapes to reinforce the branding being applied to our apps. At first glance what we are able to control appears to be somewhat…

blog.stylingandroid.com

 

 

 

ConcaveRoundedCornerTreatment.kt

class ConcaveRoundedCornerTreatment : CornerTreatment() {

    override fun getCornerPath(
        shapePath: ShapePath,
        angle: Float,
        interpolation: Float,
        radius: Float
    ) {
        val interpolatedRadius = radius * interpolation
        shapePath.reset(0f, interpolatedRadius, ANGLE_LEFT, ANGLE_LEFT - angle)
        shapePath.addArc(
            -interpolatedRadius,
            -interpolatedRadius,
            interpolatedRadius,
            interpolatedRadius,
            ANGLE_BOTTOM,
            -angle
        )
    }

    companion object {
        const val ANGLE_LEFT = 180f
        const val ANGLE_BOTTOM = 90f
    }
}

 

MainActivity.kt

package com.example.myapplication

import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Color
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.TypedValue
import android.view.View
import androidx.core.content.ContextCompat
import com.example.myapplication.databinding.ActivityMainBinding
import com.google.android.material.shape.MaterialShapeDrawable
import com.google.android.material.shape.ShapeAppearanceModel
import kotlin.math.roundToInt

private const val TYPE_LEFT = 0
private const val TYPE_RIGHT = 1

class MainActivity : AppCompatActivity() {

    private lateinit var binding: ActivityMainBinding

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        binding = ActivityMainBinding.inflate(layoutInflater)
        setContentView(binding.root)

        val cornerSize = dp2px(20f, applicationContext)

        binding.view1.setShapeBackground(cornerSize, R.color.yellow, TYPE_LEFT)
        binding.view2.setShapeBackground(cornerSize, R.color.green, TYPE_RIGHT)
    }

    private fun dp2px(dp: Float, context: Context) = TypedValue.applyDimension(
        TypedValue.COMPLEX_UNIT_DIP, dp, context.resources.displayMetrics
    )
}

private fun View.setShapeBackground(cornerSize: Float, color: Int, type: Int) {
    background = if(type == TYPE_LEFT) {
        MaterialShapeDrawable(
            ShapeAppearanceModel.builder()
                .setBottomLeftCornerSize(cornerSize)
                .setTopLeftCornerSize(cornerSize)
                .setTopLeftCorner(ConcaveRoundedCornerTreatment())
                .setBottomLeftCorner(ConcaveRoundedCornerTreatment())
                .build()
        ).apply {
            fillColor = ColorStateList.valueOf(
                ContextCompat.getColor(
                    context,
                    color
                )
            )
        }
    } else {
        MaterialShapeDrawable(
            ShapeAppearanceModel.builder()
                .setBottomRightCornerSize(cornerSize)
                .setTopRightCornerSize(cornerSize)
                .setTopRightCorner(ConcaveRoundedCornerTreatment())
                .setBottomRightCorner(ConcaveRoundedCornerTreatment())
                .build()
        ).apply {
            fillColor = ColorStateList.valueOf(
                ContextCompat.getColor(
                    context,
                    color
                )
            )
        }
    }
}

 

 

왼쪽에 위치한 노란색 뷰는 setBottomLeftCornerSize/setTopLeftCornerSize와 setBottomLeftCorner, setTopLeftCorner를 사용하여 왼쪽 위, 왼쪽 아래가 안으로 둥글게 구현하였고,

오른쪽에 위치한 초록색 뷰는 setBottomRightCornerSize/setTopRightCornerSize와 setBottomRightCorner, setTopRightCorner를 사용하여 오른쪽 위, 오른쪽 아래가 안으로 둥글게 구현하였다.

 

모든 테두리를 동일한 사이즈로 설정하려면 setAllCornerSizes를 사용하면 되고

모든 테두리를 안으로 둥글게 구현하려면 setAllCorners에 ConcaveRoundedCornerTreatment를 넣어주면 된다.

 

 

 

추가로 EdgeTreatment를 활용하여 모서리에 효과를 줄 수도 있다.

MaterialShapeDrawable(
    ShapeAppearanceModel.builder()
        .setBottomLeftCornerSize(cornerSize)
        .setTopLeftCornerSize(cornerSize)
        .setTopLeftCorner(ConcaveRoundedCornerTreatment())
        .setBottomLeftCorner(ConcaveRoundedCornerTreatment())
        .setTopEdge(TriangleEdgeTreatment(cornerSize, true))
        .setBottomEdge(TriangleEdgeTreatment(cornerSize, true))
        .build()
).apply {
    fillColor = ColorStateList.valueOf(
        ContextCompat.getColor(
            context,
            color
        )
    )
}