지난 코드랩에서 retrofit을 이용한 api 호출방법, Moshi 라이브러리를 이용한 json → 코틀린 객체 역직렬화, api 호출 코루틴으로 리팩토링 등을 진행했다. 이제는 서버로부터 받는 응답값을 파싱 해 사용해보자.
web url로부터 image를 가져오고 표시하는 방법
을 배우게 된다.서버로부터 사진을 받아 ui에 표시하는 과정은 상당히 직관적으로 보이지만, 실제로는 몇가지 작업이 필요하다.
이미지를 서버로부터 다운 받아야하고 내부적으로 저장 한 다음, 압축되어 있는 포맷에서 안드로이드가 사용할 수 있는 이미지 포맷으로 변경하는 과정이 필요하다
어떻게 효율적으로 이미지를 서버로부터 받아올 수 있을지 배울 수 있다.
글라이드는 안드로이드에서 사용할 수 있는 빠르고 효율적인 미디어 관리 및 이미지 로딩 프레임워크다. 미디어 디코딩, 메모리 또는 디스크 저장, 그리고 리소스 풀링등을 간단하고 쉽게 사용할 수 있도록 인터페이스화 시켜두었다.
글라이드는 기본적으로 아래 2가지를 필요로한다.
다운로드 받아야 할 이미지가 있는 url
ImageView 객체
gradle 파일에 아래처럼 의존성을 추가하자.
implementation "com.github.bumptech.glide:glide:$version_glide"
우선 Glide를 사용 해보기 위해 api가 내려주는 image url list중에 첫번째 데이터를 이용해 화면에 띄워보는 작업을 해보자. 아래 코드를 추가하자.
private val _property = MutableLiveData<MarsProperty>()
val property: LiveData<MarsProperty>
get() = _property
MutableLiveData로 감싸서 private으로 선언한다. 그리고 일반 LiveData로 감싸서 ViewModel외부에서 참조할 경우 read만 가능 하게 선언 하는것이다.
지난 학습 내용에서 코루틴으로 적용 해두었던 api 호출 로직의 응답부분에 다음처럼 추가해주자.
val listResult = MarsApi.retrofitService.getProperties()
if (listResult.size > 0) {
_property.value = listResult[0]
}
Databinding을 해둔 fragment_overview의 TextView를 다음처럼 변경하자
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{viewModel.property.imgSrcUrl}"
viewModel.property.imgSrcUrl
값이 들어가게 될 것이다.화면에 url이 잘 표시된다면 이제 해당 url을 이용해 이미지 데이터를 받아서 화면에 뿌려줄 것이다. 이번 코드랩에서는 binding Adapter
를 이용해서 ImageView의 xml 속성에서 url을 가져올 수 있게 구현 할것이다.
@BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) {
}
이렇게 정의 해주면 실제로 다음과 같이 사용 가능하다.
app:imageUrl="@{viewModel.property.imgSrcUrl}"
viewModel.propery.imgSrcUrl은 String value이며 해당 value가 bindImage 메서드의 imgUrl 파라미터로 전달되어 bindImage 메서드가 동작하는 로직이다.
bindImage메서드 내에서 imgUrl을 이용해 서버에 image data를 호출하는 로직을 추가 해주자.
fun bindingImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
Glide.with(imgView.context)
.load(imgUri)
.into(imgView)
}
}
Glide는 이미지를 서버로부터 받아오는 동안 placeHolder 이미지를 표시하고, 로드 실패시 에러 이미지를 표시하는 기능을 지원해준다. Glide를 이용한 로직을 변경해보자.
Glide.with(imgView.context)
.load(imgUri)
.apply(RequestOptions()
.placeholder(R.drawable.loading_animation)
.error(R.drawable.ic_broken_image))
.into(imgView)
단순히 호출하고 저장하는 로직이외에 apply메서드가 추가되었다.
apply메서드는 요청시 옵션을 추가해줄 수 있게 해준다. 파라미터로 RequestOptions 객체를 전달 해주면 내부적으로 requestOptions을 적용 시키는 로직을 확인할 수 있다.
api 호출중 일때 표시할 placeholder img와 error가 발생한 경우 표시할 img를 추가해줬다.
이제 리싸이클러뷰를 이용해서 이미지를 그리드뷰로 표시해보자. 먼저 overView 화면에서 그리드형태로 데이터를 표시하기 위해 viewModel에 List 타입의 라이브데이터를 생성한다.
private val _properties = MutableLiveData<List<MarsProperty>>()
val properties: LiveData<List<MarsProperty>>
get() = _properties
그리고 받아온 첫번째 데이터만 갱신해주고 있던 try{} 블럭내의 코드를 List로 받아올 수 있게 변경해준다.
try {
_properties.value = MarsApi.retrofitService.getProperties()
_response.value = "Success: Mars properties retrieved"
} catch (e: Exception) {
_response.value = "Failure: ${e.message}"
}
xml layout을 다루는 코드는 정리에서 제외했다.
지난 코드랩에서 학습 했던 내용을 다시 복습한다는 느낌으로 Diffutils를 이용하는 리싸이클러뷰 어댑터를 생성하자
class PhotoGridAdapter : androidx.recyclerview.widget.ListAdapter<MarsProperty, PhotoGridAdapter.MarsPropertyViewHolder>(DiffCallback) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): PhotoGridAdapter.MarsPropertyViewHolder {
return MarsPropertyViewHolder(GridViewItemBinding.inflate(LayoutInflater.from(parent.context)))
}
override fun onBindViewHolder(holder: PhotoGridAdapter.MarsPropertyViewHolder, position: Int) {
val marsProperty = getItem(position)
holder.bind(marsProperty)
}
companion object DiffCallback : DiffUtil.ItemCallback<MarsProperty>() {
override fun areItemsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: MarsProperty, newItem: MarsProperty): Boolean {
return oldItem.id == newItem.id
}
}
class MarsPropertyViewHolder(private var binding: GridViewItemBinding): RecyclerView.ViewHolder(binding.root) {
fun bind(marsProperty: MarsProperty) {
binding.property = marsProperty
binding.executePendingBindings()
}
}
}
getItem(position) 메서드를 호출하면 데이터 리스트를 ListAdpater에서 관리 해주기 때문에 position에 해당하는 MarsProperty 객체를 얻을 수 있다.
이제 리싸이클러뷰에서 사용할 수 있게 BindingAdapter를 만들어주자.
@BindingAdapter("listData")
fun bindRecyclerView(recyclerView: RecyclerView,
data: List<MarsProperty>?) {
val adapter = recyclerView.adapter as PhotoGridAdapter
adapter.submitList(data)
}
<RecyclerView>
....
app:listData = "@{viewModel.properties}"
...
</RecyclerView>