[Android] 반사효과 이미지 뷰
Mgmix
·2021. 7. 18. 20:44
안드로이드의 ImageView 를 사용함에 있어서, 바닥의 물에 비친 반사효과가 필요할 때가 있었는데
구현 과정을 코드로 정리 해보았습니다.
구현 화면
구현하려는 효과는 위의 이미지와 같이, 이미지가 물에 비친듯한 효과로 반사되어 보여주도록 구현 할 예정입니다.
일단 해당 효과를 구현할 ImageView 클래스를 만들어 주어야합니다.
AppCompatImageView 를 상속한 ReflectionImageView 클래스를 생성 해줍니다.
이미지 뷰 이므로 전달받은 이미지 리소스를 적용 할 수있도록, setImageDrawable() 과 setImageResource() 를 오버라이드 해줍니다.
효과를 적용하는 메소드를 생성하고, 기본적인 예외 처리등의 코드를 작성 해주도록 합니다.
초기 코드
class ReflectionImageView(context: Context, attrs: AttributeSet) :
AppCompatImageView(context, attrs) {
override fun setImageDrawable(drawable: Drawable?) {
if (drawable == null) return
if (drawable is BitmapDrawable) {
drawable.bitmap?.let { setReflectionEffect(it) }
}
}
override fun setImageResource(resId: Int) {
setReflectionEffect(BitmapFactory.decodeResource(resources, resId))
}
private fun setReflectionEffect(origin: Bitmap) {
super.setImageDrawable(BitmapDrawable(resources, origin))
}
}
구현 작업 순서를 간단하게 풀어서 생각해보면
- 원본 이미지를 출력
- 반사되어 비치는 영역 그려주기
- 반사 효과에 맞게 하단의 이미지를 반전
- 투명한 효과 적용 및 적절한 길이 조절
정도로 나눠서 구현을 진행 하도록 하겠습니다.
1. 원본 이미지 출력
private fun setReflectionEffect(origin: Bitmap) {
val width = origin.width
val height = origin.height
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawBitmap(origin, 0f, 0f, null)
super.setImageDrawable(BitmapDrawable(resources, bitmap))
}
2. 반사되어 비치는 영역 그리기
private fun setReflectionEffect(origin: Bitmap) {
val width = origin.width
val height = origin.height
// 하단 이미지 표기 위해, 원본 이미지 만큼 길이를 확장
val bitmap = Bitmap.createBitmap(width, height + height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawBitmap(origin, 0f, 0f, null)
// 이미지 그려주기
val reflectImage = Bitmap.createBitmap(origin, 0, 0, width, height, null, false)
canvas.drawBitmap(reflectImage, 0f, height.toFloat(), null)
super.setImageDrawable(BitmapDrawable(resources, bitmap))
}
3. 반사 효과에 맞게 하단의 이미지를 반전
반전을 시킬 때는 Matrix 의 preScale 또는 postScale 을 사용하여 Bitmap 의 Scale 을 조작해 줄 수있습니다.
private fun setReflectionEffect(origin: Bitmap) {
val width = origin.width
val height = origin.height
// 하단 이미지 표기 위해, 원본 이미지 만큼 길이를 확장
val bitmap = Bitmap.createBitmap(width, height + height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawBitmap(origin, 0f, 0f, null)
// 반사 영역에 그려질 이미지를 대칭으로 만들어준다.
val matrix = Matrix().apply { postScale(1f, -1f) }
// 이미지 그려주기, Matrix 적용
val reflectImage = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false)
canvas.drawBitmap(reflectImage, 0f, height.toFloat(), null)
super.setImageDrawable(BitmapDrawable(resources, bitmap))
}
4. 투명한 효과 적용과 적절한 길이 조절
점점 투명해 지는 듯한 효과를 주기 위해서, LinearGradient 를 사용한 그라데이션 효과와 그라데이션을 이미지에 합성하기 위한 PorterDuff.Mode 를 이용 한다. PotterDuff Mode 는 이미지에 대한 다양한 연산자를 통해서 (SRC, SRC_IN, DST, DST_IN, DST_OUT .. 등) 이미지를 합성을 도와줍니다.
그라데이션 shader 효과가 Source Image 로 들어가고, 반사될 이미지가 Destination Image 로 들어 가게되는데 DST_IN 연산자를 통해 겹치는 부분에서 Destination 의 색이 적용되고, 겹치지 않는 영역에서는 alpha 값이 0으로 적용됩니다.
private fun setReflectionEffect(origin: Bitmap) {
val width = origin.width
val height = origin.height
// 하단 이미지 표기 위해, 원본 이미지 만큼 길이를 확장
val bitmap = Bitmap.createBitmap(width, height + height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
canvas.drawBitmap(origin, 0f, 0f, null)
// 반사 영역에 그려질 이미지를 대칭으로 만들어준다.
val matrix = Matrix().apply { postScale(1f, -1f) }
// 이미지 그려주기, Matrix 적용
val reflectImage = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false)
canvas.drawBitmap(reflectImage, 0f, height.toFloat(), null)
// LinearGradient 를 통해 그라데이션 효과 적용
val effect = LinearGradient(0f, origin.height.toFloat(), 0f, bitmap.height.toFloat(),
0x70ffffff, 0x00000000, Shader.TileMode.MIRROR)
val paint = Paint().apply {
shader = effect
xfermode = PorterDuffXfermode(PorterDuff.Mode.DST_IN)
}
// Canvas 에 그리기
canvas.drawRect(0f, height.toFloat(), width.toFloat(), bitmap.height.toFloat(), paint)
super.setImageDrawable(BitmapDrawable(resources, bitmap))
}
전체가 모두 비치는게 약간은 부자연스럽기 때문에, 비치는 이미지를 절반 정도의 크기로 보여주도록 합니다.
// 하단 이미지 표기 위해, 비치는 이미지를 원본의 절반 크기로 수정
val bitmap = Bitmap.createBitmap(width, height + height/2, Bitmap.Config.ARGB_8888)
'Programming > Android' 카테고리의 다른 글
안드로이드(모바일) 환경에서의 강제 업데이트 (0) | 2021.08.28 |
---|---|
안드로이드 Kotlin Android Extension Deprecated 소식 (0) | 2020.10.21 |
Dagger Hilt 2.28-alpha 버전 사용시 주의 사항 [Bug] (0) | 2020.10.16 |
안드로이드 스튜디오 Build Error 발생시 해결 법 (3) | 2020.01.04 |
안드로이드 앱 아키텍처 가이드 (0) | 2019.10.07 |