-
[Kotlin] RecyclerView , Retrofit2 이용하기.Android 2019. 2. 10. 16:28
이번 포스팅은 Kotlin으로 RecyclerView와 Retrofit2 이용하기 입니다.
ReyclerView를 이용하기 위한 사전 작업은 'RecylcerView'위의 포스팅에 명시되어있습니다.
본 포스팅으로 retrofit2과 RecyclerView를 사용할 수 있도록 작성하였습니다.
> step 1
RecyclerView로 화면에 뿌려질 view를 xml로 작성합니다.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<android.support.v4.widget.NestedScrollView
android:id="@+id/scrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyler_view"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:nestedScrollingEnabled="false"
android:overScrollMode="never"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
tools:listitem="@layout/list_photo_view"/>
</android.support.v4.widget.NestedScrollView>
</LinearLayout>가장 먼저 acitivity_main xml을 작성합니다.
제가 작성할 RecyclerView 안에는 많은 데이터들이 있어
저는 RecyclerView 위에 NestedScrollView로 감싸주었습니다.
#TMI
- RecyclerView 안에 스크롤움직임을 부드럽게 하기 위해서는 아래의 코드를 작성하면 됩니다.
android:nestedScrollingEnabled="false"
- RecyclerView 위, 아래 양 끝에서 스크롤되지 않을 때 나타나는 효과를 없애기 위해서는 아래의 코드를 작성합니다.
android:overScrollMode="never"
- RecyclerView안에 View의 형태를 위해 Layoutmanager를 설정해줘야 합니다.
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
- RecyclerView안에 listitem을 명시해줍니다.
tools:listitem="@layout/list_photo_view"/>
list_photo_view.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/list_photo_view"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="horizontal"
android:layout_marginLeft="8dp"
android:layout_marginTop="8dp"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/image_photo"
android:layout_width="80dp"
android:layout_height="80dp"
app:srcCompat="@drawable/ic_launcher_foreground"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10dp"
android:orientation="vertical">
<TextView
android:id="@+id/text_Title"
android:maxLines="1"
android:ellipsize="end"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Title1"/>
<TextView
android:id="@+id/text_size"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="1024x600"/>
<TextView
android:id="@+id/text_url"
android:text="http://image_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<LinearLayout
android:orientation="horizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<TextView
android:id="@+id/text_date"
android:text="2014/01/02"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<TextView
android:layout_marginLeft="5dp"
android:id="@+id/text_time"
android:text="16:30"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>저는 위와 같이 명시해 주었습니다.
본인이 사용할 데이터에 알맞은 item list를 정의해주면 됩니다.
#TMI
textView안에 담는 데이터의 길이가 길어서 ... 표시를 하고 싶을때는 아래의 코드를 작성하면 됩니다.
android:maxLines="1" //textview 안에 maxlines을 1줄로 지정하겠다.
android:ellipsize="end" // 화면의 범위가 넘어가는 text가 있을 시 end 부분에 ... 표시를 하겠다.> step 2.
retrofit2를 통해 가져올 datamodel을 정의합니다.
kotlin 같은 경우는 data class로 java의 data model 같이 get,set 필요 없이 간단한게 정의할 수 있습니다.
저 같은 경우는 아래와 같이 지정하였습니다.
PhotoModel.ktdata class PhotoModel (
var stat : String,
var page : Int,
var totak_page : Int,
var photos : ArrayList<Photo>
)
data class Photo(
var date_token : String,
var title : String,
var width :Int,
var url : URL,
var height : Int
)서버에서 내려주는 api가 어떤 모습인지에 따라 개인의 data model을 만들어주시면 됩니다.
> step 3.
api 통신하기.
저는 api와 통신하기 위해 retrofit2 Library를 이용하였습니다.
- Retrofit2 Setting하기.
1. build.gradle(Module:app)에 아래와 같이 추가해줍니다.
dependencies {
//retrofit2
implementation 'com.squareup.retrofit2:retrofit:2.4.0'
implementation 'com.squareup.retrofit2:converter-gson:2.4.0'
implementation 'com.google.code.gson:gson:2.8.2'2. AndroidManifest.xml 에 인터넷 권한을 허가해줍니다.
<uses-permission android:name="android.permission.INTERNET"/>
여기까지만 하시면 Retrofit2를 이용하기 위한 준비는 끝났습니다.
- Retrofit2 사용하기.
1. retrofit2 객체 만들기.
MainActivity.kt
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com)
.addConverterFactory(GsonConverterFactory.create())
.build()baseUrl에는 본인이 통신할 API url을 작성하시면 됩니다.
제가 만든 data model 과는 다르지만 저는 예시로 "https://api.github.com/users"을 이용하였습니다.
2. RetrofitInterface 설정.
작성할 url을 어떤 식으로 받아올지 Http method를 정의합니다.RetrofitInterface.kt
interface RetrofitInterface {
@GET("/users")
fun requestAllData() : Call<PhotoModel>
}예시로 사용한 url은 https://api.github.com/users 위와 같은 모양으로 되어있습니다.
기본 baseUrl에 http get 방식으로 우리가 만든 photoModel로 서버에 call 합니다.
3. API 호출하기.
MainActivity
val retrofitService = retrofit.create(RetrofitInterface::class.java)
retrofitService.requestAllData().enqueue(object : Callback<PhotoModel>{
override fun onResponse(call: Call<PhotoModel>, response: Response<PhotoModel>) {
if (response.isSuccessful) {
val body = response.body()
body?.let { //text_text.text = body.toString response 잘 받아왔는지 확인.setAdapter(it.photos)
}
}
}
override fun onFailure(call: Call<PhotoModel>, t: Throwable) {
Log.d("this is error",t.message)
}
})RetrofitInterface에서 만든 GET방식으로 api를 호출합니다.
만약 response를 잘 받아 왔는지 확인 하고 싶으면 isSuccessful 조건안에
임의로 정한 textview안에 response.body를 출력하시면 됩니다.
만약 제대로 api가 들어왔다면
이제 recyclerview adapter를 만들면 됩니다.
> step 4
RecyclerAdapter 만들기.
RecyclerAdapter.kt
class RecyclerAdapter(val postList : ArrayList<Photo>, val context : Context)
: RecyclerView.Adapter<RecyclerAdapter.ViewHolder>(){
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
return RecyclerAdapter.ViewHolder(LayoutInflater.from(context)
.inflate(R.layout.list_photo_view,parent ,false))
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
holder.bind(postList[position],context)
}
override fun getItemCount(): Int {
return postList.count()
}
class ViewHolder (itemView: View? ) : RecyclerView.ViewHolder(itemView!!){
val photo = itemView?.findViewById<ImageView>(R.id.image_photo)
val title = itemView?.findViewById<TextView>(R.id.text_Title)
val size = itemView?.findViewById<TextView>(R.id.text_size)
val url = itemView?.findViewById<TextView>(R.id.text_url)
val date = itemView?.findViewById<TextView>(R.id.text_date)
fun bind(itemPhoto : Photo? , context: Context){
val urlString = itemPhoto?.url.toString()
if(!urlString.isEmpty()){
photo?.setImageResource(R.mipmap.ic_launcher)
}else{
photo?.visibility = View.GONE
}
title?.text = itemPhoto?.title
size?.text = itemPhoto?.width.toString()
size?.append("x${itemPhoto?.height}")
url?.text=urlString
date?.text=itemPhoto?.date_token
}
}
}- RecyclerAdapter의 역할
api로 호출한 데이터와 list_photo_view로 만든 view를 연결하는 bridge 역할을 합니다. - RecyclerAdapter
RecyclerAdapter 는 RecyclerView.Adapter상속합니다.
RecyclerView.Adapter를 상속하기 위해서는 ViewHolder 가 필요합니다. - ViewHolder
ViewHolder 화면에 보여줄 list_photo_view의 component들을 알려줍니다.
bind라는 함수를 통하여 내가 사용할 데이터와 view들을 연결시켜줍니다.
- ViewHodler가 만들어졌으면 RecyclerView.Adapter가 오버라이드하는 함수 3가지를 만들어줍니다.
1. onCreateViewHolder()
: ViewHolder을 생성하며 화면이 최초로 나타날때 list_photo_view를 inflate해줍니다.
2. onBindViewHolder()
: ViewHolder와 데이터의 position에 데이터를 결합한다.
: 받아올 photoList position에 해당하는 데이터와 ViewHolder를 결합합니다.
3, getItemCount()
: ArrayList로 받은 데이터의 개수를 반환해줍니다.
>step5
MainActivity에서 adapter생성하기.
MainActivity.kt
private fun setAdapter(photoList : ArrayList<Photo>){
val mAdapter = RecyclerAdapter(photoList,this)
recyler_view.adapter = mAdapter
recyler_view.layoutManager = LinearLayoutManager(this)
}RecyclerAdapte클래스를 생성하여 photolist를 데이터로 받는 mAdapter 변수 선언.
MainActivity의 전체 모습은 아래와 같습니다.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
loadData()
}
private fun setAdapter(photoList : ArrayList<Photo>){
val mAdapter = RecyclerAdapter(photoList,this)
recyler_view.adapter = mAdapter
recyler_view.layoutManager = LinearLayoutManager(this)
recyler_view.setHasFixedSize(false)
}
private fun loadData() {
val retrofit = Retrofit.Builder()
.baseUrl("https://api.github.com")
.addConverterFactory(GsonConverterFactory.create())
.build()
val retrofitService = retrofit.create(RetrofitInterface::class.java)
retrofitService.requestAllData().enqueue(object : Callback<PhotoModel>{
override fun onResponse(call: Call<PhotoModel>, response: Response<PhotoModel>) {
if (response.isSuccessful) {
val body = response.body()
body?.let {
setAdapter(it.photos)
}
}
}
override fun onFailure(call: Call<PhotoModel>, t: Throwable) {
Log.d("this is error",t.message)
}
})
}
}#Conclusion
Retrofit2를 이용하여 데이터를 가져오고
RecyclerView를 이용해 가져온 데이터들을 화면에 예쁘게 보여주기 위해서
이 둘은 종종 같이 사용됩니다.
크게 이 둘을 사용하기 위해서는 아래와 같은 Flow를 거치고 아래와 같은 Code를 작성합니다.
- Retrofit2
- Setting
- Android Manifest에서 Internet Permission 허가.
- build.gradle(Module.app)에서 retrofit2 implementation 설정. .kt 작성
- api 통신을 통해 담을 data model 생성.
- retrofit2 객체 생성.
- http method를 정의한 retrofitInterface.kt 작성
- RecyclerVIew
- Setting.
- build.gradle에 (Module.app)에서 recyclerview implementation하기. - .xml 작성
- RecyclerView xml 생성- layoutManger 선언.
- RecyclerView에 들어갈 itemList.xml 작성
.kt 작성
- RecyclerAdapter.kt 작성.
- ViewHolder 생성.
- RecyclerView.Adapter가 override하는 3가지 함수 작성.
1. oncreateViewHolder
2. getItemCount
3. onBIndViewHolder
똑같은 순서대로 진행하지 않더라도 위와 같은 작업들을 하시면
원하는 기능들을 구현할 수 있을 것입니다!LIST'Android' 카테고리의 다른 글
[Android]안드로이드 토글 버튼 만들기. (0) 2019.05.27 [Android] 안드로이드 커스텀 버튼 만들기. (1) 2019.05.23 [Android]startActivity()와 startActivityForResult() 차이. (1) 2019.05.07 [Android]Timber 사용하기. (0) 2019.03.31 RecyclerView (0) 2018.12.15