ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • [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.kt

    data 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 
    1. Setting
      - Android Manifest에서 Internet Permission 허가. 
      - build.gradle(Module.app)에서 retrofit2 implementation 설정. 

    2. .kt 작성

      - api 통신을 통해 담을 data model 생성. 

      - retrofit2 객체 생성.  

      - http method를 정의한 retrofitInterface.kt 작성

       



    • RecyclerVIew 
    1. Setting.
      - build.gradle에 (Module.app)에서 recyclerview implementation하기. 

    2. .xml 작성
      - RecyclerView xml 생성

      - layoutManger 선언.

      - RecyclerView에 들어갈 itemList.xml 작성

       
    3. .kt 작성 
      - RecyclerAdapter.kt 작성.
      - ViewHolder 생성.
      - RecyclerView.Adapter가 override하는 3가지 함수 작성.
      1. oncreateViewHolder
      2. getItemCount
      3. onBIndViewHolder


    똑같은 순서대로 진행하지 않더라도 위와 같은 작업들을 하시면 
    원하는 기능들을 구현할 수 있을 것입니다! 






     







    LIST

    댓글

Designed by Tistory.