본문 바로가기
2023년 이전/ReativeX

RxAndroid - RecyclerView

by JeongUPark 2020. 2. 14.
반응형

[출처 - RxJava 프로그래밍 : 리액티브 프로그래밍 기초부터 안드로이드 까지 한번에]

본 글은 'RxJava 프로그래밍 : 리액티브 프로그래밍 기초부터 안드로이드 까지 한번에' 를 학습하면서 정리한 글입니다.

 

ReativeX를 이용하여 Android의 RecyclerView를 만들어 보겠습니다.

 

RecyclerView는 간단하게 설명해서 ListView의 각 item들이 생성되고 삭제되는 비효율적인 문제를 해결하기 위해 나온 View로 쓰지 않는 View를 재 활용하는 View 입니다. (자세한 내용은 여기서 확인해 보시면 좋을것 같습니다.)

 

만약 build.gradle에 v7라이브러리를 쓰고 있다면 다음을 추가하고

implementation 'com.android.support:recyclerview-v7:28.0.0'

androidx를 사용하고 있다면

implementation 'androidx.recyclerview:recyclerview:1.0.0'

를 추가합니다.

 

그리고 code를 작성합니다. (code를 작성하면 느낀건 RecyclerView 자체는 별 문제 없고 action에 대한 부분은 ReactiveX 방식으로 coding 한듯 합니다.)

 

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/main_recyclerview"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

i_test_item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="horizontal" android:layout_width="match_parent"
    android:layout_height="wrap_content">

    <ImageView
        android:id="@+id/i_img"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>
    <TextView
        android:id="@+id/i_name"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"/>

</LinearLayout>

Item

import android.graphics.drawable.Drawable

class RecyclerItem(i : Drawable, n : String){
    var name : String = n
    var img : Drawable = i
}

Holder

import android.view.View
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.Observable
import kotlinx.android.synthetic.main.i_test_item.view.*

class MyViewHolder(itemView : View) : RecyclerView.ViewHolder(itemView) {

    val nameTView = itemView.i_name
    val iconimg = itemView.i_img

    fun getClickObserver(item :RecyclerItem) : Observable<RecyclerItem>{
        return Observable.create { e-> itemView.setOnClickListener {
            viwe-> e.onNext(item)
        } }
    }

}

Adapter

import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import io.reactivex.subjects.PublishSubject

class MyViewAdapter () : RecyclerView.Adapter<MyViewHolder>(){

    private val mItems : ArrayList<RecyclerItem> = ArrayList<RecyclerItem>()
    val mPublishSubject: PublishSubject<RecyclerItem> = PublishSubject.create()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
        return MyViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.i_test_item,parent,false))
    }

    override fun getItemCount(): Int {
        return mItems.size
    }

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
        val item = mItems[position]
        holder.iconimg.setImageDrawable(item.img)
        holder.nameTView.text = item.name
        holder.getClickObserver(item).subscribe(mPublishSubject)
    }

    fun updateItems(items : ArrayList<RecyclerItem>){
        mItems.addAll(items)
    }
    fun updateItems(item: RecyclerItem){
        mItems.add(item)
    }
}

Activity

import android.content.Intent
import android.content.pm.ResolveInfo
import android.os.Bundle
import android.widget.Toast
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.trello.rxlifecycle3.components.support.RxAppCompatActivity
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.rxkotlin.toObservable
import io.reactivex.schedulers.Schedulers
import kotlinx.android.synthetic.main.activity_main.*


class MainActivity : RxAppCompatActivity() {
    val TAG: String = "Test_RxAndroid"

    lateinit var mRecyclerView : RecyclerView
    lateinit var mAdapter: MyViewAdapter
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        mRecyclerView = main_recyclerview
        mRecyclerView.layoutManager = LinearLayoutManager(this)
        mAdapter = MyViewAdapter()
        mRecyclerView.adapter = mAdapter
        mAdapter.mPublishSubject.subscribe { s->toast(s.name) }
    }

    override fun onStart() {
        super.onStart()

        getItemObservable()
            .observeOn(AndroidSchedulers.mainThread())
            .subscribe{
                item->
                mAdapter.updateItems(item)
                mAdapter.notifyDataSetChanged()
            }
    }
    fun toast(title: String){
        Toast.makeText(this,title,Toast.LENGTH_SHORT).show()
    }
    fun getItemObservable():Observable<RecyclerItem>{
        val pm = packageManager
        val i = Intent(Intent.ACTION_MAIN,null)
        i.addCategory(Intent.CATEGORY_LAUNCHER)
        return pm.queryIntentActivities(i,0).toObservable()
            .sorted(ResolveInfo.DisplayNameComparator(pm))
            .subscribeOn(Schedulers.io())
            .observeOn(Schedulers.io())
            .map { item ->
                val img = item.activityInfo.loadIcon(pm)
                val title = item.activityInfo.loadLabel(pm).toString()
                return@map RecyclerItem(img, title)
            }
    }
}

결과는 핸드폰에 깔리 app의 icon과 app 이름으로 된 리스트들이 recyclerview로 나타냅니다.

그리고 아이템을 클릭하면 title이름이 toast로 나타납니다.

 

 

code를 간단히 설명하면

1. onCreate에서 RecyclerView를 셋팅합니다.

2. onStart에서 getItemObservable을 통하여 폰의 application들의 icon과 이름을 획득합니다.

3. 획득한 데이터들을 AndroidMainThread에서 RecyclerView에 update 합니다.(notifyDataSetChanged를 하면 mAdapter의 데이터를 갱신해줍니다.)

4. 클릭 시  MyViewHolder의getClickObserver에서 ClickListner가 동작하고, 그 값으로 다시 subscribe가 동작하여 toast 함수가 동작합니다.

 

결과 화면은 다음과 같습니다.

 

반응형