프로젝트를 진행하다 webp 이미지 파일을 표현하기 위해 Fresco의 SimpleDraweeView를 사용하여 동적 Image를 표현하였다.
사용하는 속성들을 알아보기 위해 구글링을 좀 해보았는데 레퍼런스가 다른 것들에 비해 적어 한눈에 보기 위해 포스팅한다.
Fresco는 Facebook에서 공개한 이미지로딩 라이브러리
2.3(진저브레드) 이상 버전의 안드로이드를 지원
Fresco의 장점
- 간편한 이미지 변환
Fresco의 단점
- ImageView가 아닌 DraweeView를 사용해야 함
- DraweeView가 wrap_content를 지원하지 않아 요청할 이미지의 정확한 사이즈를 지정해야 함
특이사항
- SimpleDraweeView는 ImageView를 부모 클래스로 가지고 있다 하지만, 절대로 setImageDrawable 같은 직접적으로 접근하는 것을 사용하지 않아야 한다고 한다.
build.gradle 종속성 추가
dependencies {
...
implementation 'com.facebook.fresco:fresco:2.5.0'
//앱의 필요에 따라 선택적 모듈 추가
implementation 'com.facebook.fresco:imagepipeline-okhttp3:2.1.0'
implementation 'com.facebook.fresco:animated-base:2.5.0'
// 애니메이션 GIF 지원용
implementation 'com.facebook.fresco:animated-gif:2.6.0'
// 애니메이션 WebP 지원용
implementation 'com.facebook.fresco:animated-webp:2.6.0'
implementation 'com.facebook.fresco:webpsupport:2.6.0'
// Android 지원 라이브러리 제공(이미 이와 유사한 종속성이 있을 수 있음)
implementation 'com.android.support:support-core-utils:24.2.1'
}
프로젝트의 Application 클래스(java)를 생성해 Fresco를 초기화
public class MyApplication extends Application {
@Override
public void onCreate(){
super.onCreate();
Fresco.initialize(this);
}
}
AndroidManifest.xml에 uses-permission 추가 및 application 추가
<manifest
...
>
<uses-permission android:name="android.permission.INTERNET" />
<application
...
android:label="@string/app_name"
android:name=".MyApplication"
>
...
</application>
...
</manifest>
android:name이라는 attribute는 애플리케이션 프로세스가 시작될 때, 애플리케이션의 다른 어떤 컴포넌트보다 먼저 객체화한다. 이 attribute를 통해 Fresco가 초기화
Fresco 사용을 위해서는 ImageView 대신 facebook에서 만든 DraweeView를 사용
layout_width나 layout_height에 wrap_content를 지정할 수 없기 때문에 이미지의 크기에 맞춰 지정
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/draweeView"
android:layout_width="50dp"
android:layout_height="50dp"
//필요속성 지정해 사용
app:placeholderImage="@drawable/my_drawable" //이미지 로딩중 사용할 이미지
app:placeholderImageScaleType="fitXY" //위 설명의 scale type
app:failureImage="@drawable/error" //실패시 사용 이미지
app:failureImageScaleType="centerInside"
app:actualImageResource="" //리소스 경로 사용
app:actualImageUri="" //이미지 uri 사용
app:actualImageScaleType="fitXY" //이미지 scale type
app:roundedCornerRadius="20dp" //코너 round
app:roundTopLeft="true" //각 방향별 라운드
app:roundTopRight="false"
app:roundBottomLeft="false"
app:roundBottomRight="true"
app:roundAsCircle="true" // 원형 이미지 //default false
/>
아래는 programmatic 예시
//Kotlin
val hierarchy: GenericDraweeHierarchy = GenericDraweeHierarchyBuilder(resources)
.setFadeDuration(300)
.setBackground(getDrawable(R.drawable.ic_launcher))
.setPlaceholderImage(getDrawable(R.drawable.ic_launcher))
.setFailureImage(getDrawable(R.drawable.ic_launcher))
.build()
draweeView.hierarchy = hierarchy
//Java
GenericDraweeHierarchy hierarchy = new GenericDraweeHierarchyBuilder(getResources())
.setFadeDuration(300)
.setBackground(getDrawable(R.drawable.ic_launcher))
.setPlaceholderImage(getDrawable(R.drawable.ic_launcher))
.setFailureImage(getDrawable(R.drawable.ic_launcher))
.build();
draweeView.setHierarchy(hierarchy);
xml에서 uri를 지정해주면 작동하지만 동적 Image (webp, gif)가 원하는 대로 이미지가 재생되지 않을 수 있다.
webp와 gif는 별도 설정을 하지 않으면 재생되지 않는다.
//Kotlin
val uri = "https://res.cloudinary.com/demo/image/upload/fl_awebp,q_40/bored_animation.webp"
val controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true) // 자동 webp 실행
.build()
simple_draweeview.controller = controller
//Java
String uri = "https://res.cloudinary.com/demo/image/upload/fl_awebp,q_40/bored_animation.webp";
DraweeController controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true)
.build();
simple_draweeview.setController(controller);
코드 상에서 controller를 지정해주면 webp나 gif가 재생된다.
필요에 따른 속성 코드 종류
//작업 판단 Listener
val listener = object : BaseControllerListener<Any?>() {
override fun onFinalImageSet(id: String, imageInfo: Any?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
//이미지 다운로드가 다 끝났을 시 작업
}
val controller = Fresco.newDraweeControllerBuilder()
.setUri(uri)
.setAutoPlayAnimations(true)
.setTapToRetryEnabled(true) // 클릭시 재실행
.setOldController(simple_draweeview.getController())
//새로운 컨트롤러를 빌드할 때, setOldController를 호출. 불필요한 메모리 할당을 방지 목적
.setControllerListener(listener)
.build()
simple_draweeview.controller = controller
Webp나 Gif가 시작되거나 끝났을 때를 판단해야 할 때
AnimatedDrawable2로 Cast 해 AnimationListener를 사용해 동적 Image의 시작과 끝을 판단
val controller = Fresco.newDraweeControllerBuilder()
.setUri("URI 지정")
.setControllerListener(object : BaseControllerListener<Any?>() {
override fun onFinalImageSet(id: String, imageInfo: Any?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
animatable.apply {
val an : AnimatedDrawable2 = animatable as AnimatedDrawable2
an.setAnimationListener(object : AnimationListener {
override fun onAnimationStart(drawable: AnimatedDrawable2) {
//애니메이션 시작 전 작업
}
override fun onAnimationStop(drawable: AnimatedDrawable2) {
//애니메이션 재생 후 작업
}
override fun onAnimationReset(drawable: AnimatedDrawable2) {}
override fun onAnimationRepeat(drawable: AnimatedDrawable2) {}
override fun onAnimationFrame(drawable: AnimatedDrawable2, frameNumber: Int) {
//반복 webp 용
//if (drawable.frameCount - 1 == frameNumber)
// animatable?.jumpToFrame(5)
}
})
}
animatable?.start()
}
})
.setAutoPlayAnimations(true)
.build()
반복 카운트를 지정해야 할 때
//Loof Count Class 생성
class LoopCountModifyingBackend(
@Nullable animationBackend: AnimationBackend?,
private val mLoopCount: Int) : AnimationBackendDelegate<AnimationBackend>(animationBackend) {
override fun getLoopCount(): Int {
return mLoopCount
}
}
val controller = Fresco.newDraweeControllerBuilder()
.setUri("URI 지정")
.setControllerListener(object : BaseControllerListener<Any?>() {
override fun onFinalImageSet(id: String, imageInfo: Any?, animatable: Animatable?) {
super.onFinalImageSet(id, imageInfo, animatable)
animatable.apply {
val an : AnimatedDrawable2 = animatable as AnimatedDrawable2
an.setAnimationListener(object : AnimationListener {
...
//지정해준 숫자만큼 반복
an.animationBackend = LoopCountModifyingBackend(an.animationBackend, 1)
})
}
animatable?.start()
}
})
.setAutoPlayAnimations(true)
.build()
Webp나 Gif에 roundedCornerRaidus가 적용되지 않는 문제
png와 같은 일반 이미지 파일에서는 잘 적용되던 roundedCornerRaidus가 webp나 gif에 적용되지 않을 수 있다.
해결 방법으로 SimpleDraweeView를 CardView에 집어넣거나 FrameLayout에 둘러 씌우면 라운드가 먹는다.
<androidx.cardview.widget.CardView
android:layout_width="50dp"
android:layout_height="50dp"
app:cardCornerRadius="10dp"
app:cardElevation="0dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/simple_draweeview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.cardview.widget.CardView>
Fresco
https://frescolib.org/docs/index.html
https://medium.com/@tonyjs/android-image-loading-library-fresco-c3a0f4679ea7
https://velog.io/@cmplxn/적고가지-않기에는-아쉬운-것들
'Android > Library' 카테고리의 다른 글
Ktor (0) | 2023.12.03 |
---|---|
GoogleMap 다중 Marker Clustering, Custom Cluster (0) | 2022.02.21 |
GeoCoding 위도 경도 <-> 주소 변환하기 (4) | 2022.02.20 |
Google Map API 사용 방법 및 예제 (0) | 2022.02.18 |
Epoxy, Epoxy에 Databinding 사용 (RecyclerView 쉽게 사용) (0) | 2022.02.14 |