마스터Q&A 안드로이드는 안드로이드 개발자들의 질문과 답변을 위한 지식 커뮤니티 사이트입니다. 안드로이드펍에서 운영하고 있습니다. [사용법, 운영진]

리사이클러뷰에 배열 데이터 대입 후 출력하려고 하는데, 출력이 안됩니다.

0 추천
class ChapterActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_chapter)

        ...여기에 있던 코드는 책의 각 제목이면서, 동시에 리사이클러뷰의 각 항목들로써, 버튼이기도 합니다. 생략....
        val recy23 = findViewById<RecyclerView>(R.id.recycler_view1)
        val adapter = SampleAdapter5(dataSet1)
        recy23.adapter = SampleAdapter5(dataSet1)



        val recy24 = findViewById<RecyclerView>(R.id.recycler_view2)
        val dataSet2 : ArrayList<String> = arrayListOf()
        recy24.adapter = SampleAdapter6(dataSet2)
        for (i in 1..30) {   <---★ 이렇게 할 때에는 화면에 출력이 잘되는데
            dataSet2.add("$i 장")
        }

        adapter.setOnItemClickListener(object : SampleAdapter5.OnItemClickListener {
            override fun onItemClick(v: View?, position: Int) {
                

                when (position) {
                    0 -> {           ↓★버튼도 반응을 아예 안합니다.
                        for (i in 1..50) { <---★이렇게 하면 출력이 안됩니다...
                            dataSet2.add("$i 장")

                            //notifyItemChanged(position)
                        }
                    }
                }

            }
        })


 
    class SampleAdapter5(private val dataSet: ArrayList<String>) : RecyclerView.Adapter<SampleAdapter5.NumberViewHolder>() {      

        private var mData: ArrayList<String>? = null
        init {
            mData = dataSet
        }

       
       interface OnItemClickListener {
            fun onItemClick(v: View?, position: Int)
        }
   
        private var mListener: OnItemClickListener? = null

      
        fun setOnItemClickListener(listener: OnItemClickListener?) {
            mListener = listener
        }
        //★


        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NumberViewHolder {       
            val layoutView: LinearLayout = LayoutInflater.from(parent.context).inflate(R.layout.activity_bible3927, parent, false) as LinearLayout
            
            return NumberViewHolder(layoutView)
        }

        override fun getItemCount(): Int = mData!!.size    
        override fun onBindViewHolder(holder: NumberViewHolder, position: Int) {        
            holder.number.text = mData!![position]          
                  
        }



        inner class NumberViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {      
            val number = itemView.findViewById(R.id.textView111) as TextView           
            
            init {
                itemView.setOnClickListener(object : View.OnClickListener {           
                    override fun onClick(v: View) {
                        val pos = adapterPosition           
                        if (pos != RecyclerView.NO_POSITION) {      
                            //dataSet[pos] = "item clicked. pos=$pos"         
                            if (mListener != null) {
                                mListener!!.onItemClick(v, pos)     
                            }
                        }
                    }
                })
            }
        }                                   
    }


    
    class SampleAdapter6(private val dataSet: ArrayList<String>) : RecyclerView.Adapter<SampleAdapter6.NumberViewHolder>() {      

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NumberViewHolder {       
            val layoutView: LinearLayout = LayoutInflater.from(parent.context).inflate(R.layout.activity_bible3927_1, parent, false) as LinearLayout
           
            return NumberViewHolder(layoutView)
        }

        override fun getItemCount(): Int = dataSet.size    

        override fun onBindViewHolder(holder: NumberViewHolder, position: Int) {        

            holder.number.text = dataSet[position]          
        }

        inner class NumberViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {      
            val number = itemView.findViewById(R.id.textView222) as TextView           
        }                                   
    }
}
}

 

리사이클러뷰에 배열 데이터를 대입한 후에, 화면에 출력시키려고 했는데, 출력이 안되고 있습니다.

 

저는 두 개의 리사이클러뷰를 갖고 있는데, 첫 번째 리사이클러뷰의 아이템을 클릭하면, 

 

두 번째 리사이클러뷰에 1장, 2장, 3장, 4장, 5장.... 이런식으로 쭉 출력이 되도록 하고 싶었습니다.

 

근데 왜그런지는 잘 몰라도, 그냥 for문을 사용했을 때에는 정상적으로 화면에 1부터 30까지 잘 출력되는데,

 

어댑터에 있는 배열의 인덱스 값을 onCreate() 함수 내부로 가져와서 사용할 때에는  for문이 먹히질 않습니

 

다. mListener가 뭘 의미하는 것인지... 이것도 잘 모르겠습니다. mListener가 문제인건지, 아니면 for문에 문제

 

가 있었던건지... 뭔지... 잘 모르겠습니다. 아니면, 버튼 클릭 이벤트가 잘못 되어있던 것인지...

상쾌한 (1,890 포인트) 님이 2021년 1월 10일 질문
상쾌한님이 2021년 1월 10일 수정

2개의 답변

+1 추천
 
채택된 답변

전체코드를 볼 수 없으나 예상되는 부분은 이렇습니다.

adapter를 구현할 때 보통 adapter를 생성하면서 parameter로 배열을 던집니다.
그러면 그 배열이 adapter 내에서
전역 변수에 매칭이 되어서 
UI를 그릴 때 사용됩니다.
그러나 이후에 배열을 다시 할당할 때는
기존 데이터를 날린 뒤에 다시 할당해야 합니다.

예를들어 Adapter 내에 replaceItems(listArray) 같은 함수를 만들어
ArrayLIst.clear() 하고
다시 세팅해야 합니다.
유사한 케이스가 Paging을 할 때,
addItems(listArray) 함수를 만들어서 추가될 데이터만 던지면
ArrayList.addAll(listArray) 처럼 추가된 데이터를 추가하기도 합니다.

혹은 추천하지 않지만, 
항상 새롭게 adapter를 새롭게 생성해서 배열을 던지는 것이죠.

제가 추천하고 싶은 것은 유투브나 구글링을 통해서
가장 유사한 동작하는 샘플을 구해서,
그것을 완전히 이해하는 작업이 선행되어야 할 것 같습니다.

지금 같은 수준이라면,
제 생각엔
전체 구조를 똑같이 가져가면서 데이터만 바꾸어 적용해야 할 것 같습니다.

잘하고 못하고를 떠나서,
지금 하고 있는 방식은 효율적이지 못합니다.
개념에 대한 이해가 없는데 이곳 저곳을 수정하면,
코드의 복잡도가 증가해서
이곳 저곳에서 오류가 발생하고,
답변자들도 코드만 봐서는 바로 짚어주지 못하게 됩니다.

영어 강좌라도, 유투브 같으면 따라할 수 있고
github에 소스코드를 공개해서
실행되는 것을 찾아서 그것과 유사하게 구현을 하는게 가장 빠르고
실력도 확실하게 올라가는 방법입니다.

그리고 샘플을 찾을 때는
그 샘플이 언제 만들어졌는지도 봐야 합니다.
샘플이나 유투브 동영상이 1년 이내에 만들어진 것을 참고하기 바랍니다.
구글이나 유투브 검색에서 날짜구간을 지정할 수 있습니다.

한국어로 된 강좌는 물론 좋은 경우도 많지만,
모집단이 작아서 가장 유사한 샘플을 찾기 위해서는
영어까지 고려하길 바랍니다.

Will Kim (43,170 포인트) 님이 2021년 1월 10일 답변
상쾌한님이 2021년 1월 11일 채택됨
올바른 샘플을 찾는다면 하루만에도 원하는 걸 뚝딱 만들 수도 있다는 것을 생각하세요.
감사합니다. ^^ 지금 한 번 읽어보겠습니다. 말씀하신것 읽어보니, 제가 원하는 것을 다 아시고 말씀하시는것 같습니다. 속 뜻은 이해가 가는데, 어떻게 그 함수를 만들어야 할지는 감이 잘 잡히질 않습니다. 저는 책에서 arraylist로 많이 들었는데, listarray라고 거꾸로 말씀하시는 것에 대해서도 배운적이 있나 하는 생각이 들었습니다. 그래서 감이 잡히질 않고, 그것이 함수안에서의 매개변수를 가리키는 것인지 무엇을 가리키는 것인지 모르겠으며, 만약 함수 호출을 가리키는 것으로써, 괄호 안의 listarray가 인수를 나타내는 것이라면 이해할 수 있을것 같으나, 그 인수를 받아들이는 함수 쪽을 어떻게 선언해야 할지도 감이 오질 않습니다. 일단 조언해주신대로 영어권의 유튜브 채널을 참고해보러 가보겠습니다. ^^ 항상 친절하게 대해주셔서 감사합니다.
깃허브는 어떻게 사용하는지 잘 모르겠으나, 책에 그것을 사용하는 것에 대해서 다루고 있는게 있으니, 한 번 배워서 해보겠습니다. 깃허브는 제가 예전에도 그 사이트에 들어가봤는데, 겉으로 보기에 그런 역할을 하는 사이트 같은 직관적인게 없어 보여서 나가버렸는데, 소스코드를 공유하고 답해줄 수 있는 곳인가보군요.
listarray는 제가 그냥 변수명을 예시로 적은 겁니다.
신경쓰지 마시고요,
arraylist라고 보면 됩니다.

예를들면, 아래 링크에서 선택된 답변을 보면,
페이징을 위해서 어댑터에 addData 함수를 추가하는 게 나옵니다.
addData 함수 대신에 setData를 만들고, 맨앞에 어레이리스트를 클리어 해 주시면 되고,
처음에 onCreate에서 adapter를 생성하고,
이후에 선택시에는 setData 함수를 호출하게 바꾸세요.

그럼 Good luck 2 U~

https://stackoverflow.com/questions/51433106/kotlin-recyclerview-pagination
+1 추천

코드를 실행해 보기 전에는 정확한 것은 모르겠구요, 다만 어댑터에서 넘어오는 포지션이 0이 아닐 수도 있겠다는 생각은 듭니다.

Adapter에서 adapterPosition의 실제값을 확인해 보세요. 0이 맞는지.

 

개인적으로는 저 같으면 Adapter5에 아이템을 넘길 때 List<String>이 아니라 List<BookItem>이렇게 넘길 것 같습니다.

data class BookItem(
    val text: String,
    val numberOfChapters: Int
) {

    fun getChapterStrings(): List<String> = (0..numberOfChapters).map { "$it 장" }
}

fun interface ItemClickListener<T> {
    fun onItemClicked(item: T)
}

class BookAdapter(books: List<BookItem>) :
    RecyclerView.Adapter<BookViewHolder>() {

    private val items = ArrayList<BookItem>(books)
    private var mListener: ItemClickListener<BookItem>? = null

    fun setBooks(books: List<BookItem>) {
        items.clear()
        items.addAll(books)
        notifyDataSetChanged()
    }

    fun setOnItemClickListener(listener: ItemClickListener<BookItem>?) {
        mListener = listener
    }

    override fun getItemCount(): Int = items.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookViewHolder {
        val itemView = parent.inflate(layoutId = R.layout.activity_bible3927)
        return BookViewHolder(itemView, mListener)
    }

    override fun onBindViewHolder(
        holder: BookViewHolder,
        position: Int
    ) = holder.bind(items[position])
}

class BookViewHolder(
    itemView: View, private val listener: ItemClickListener<BookItem>?
) : RecyclerView.ViewHolder(itemView) {

    private val number: TextView = findViewById(R.id.textView111)

    fun bind(item: BookItem) {
        number.text = item.text

        itemView.setOnClickListener {
            listener?.onItemClicked(item)
        }
    }


class ChapterActivity : AppCompatActivity() {

     adapter.setOnItemClickListener { book ->
          chapters.addAll(book.getChapterStrings())
     }
}


//ViewGroup.kt
fun ViewGroup.inflate(
    layoutInflater: LayoutInflater = LayoutInflater.from(context),
    @LayoutRes layoutId: Int,
    attachToParent: Boolean = false
): View = layoutInflater.inflate(layoutId, this, attachToParent)


//ViewHolder.kt
fun <T: View>RecyclerView.ViewHolder.findViewById(@IdRes id: Int): T = itemView.findViewById(id)

 

그리고 Chapter(장)와 Verse(절)의 어댑터는 전체 하나씩만 있으면 될 것 같습니다. 어댑터에 새로운 데이터를 주고 갱신하면 되니까요.

spark (227,830 포인트) 님이 2021년 1월 10일 답변
답변 감사합니다. ^^ 일단 한 번 해보고 오겠습니다. ^^ 답변을 정말 정성스럽게 써주신것 같습니다. ^^
제가 만들었던 클래스의 이름이 Chapter -> Book이 맞을 것 같네요.
Book:  이게 창세기부터 요한계시록에 해당하는 것으로 영어로는 Book이라고 합니다.
eg. Book of John (요한복음)
Chapter: 장
Verse: 절
지금 해보고 왔는데, 너무 수준이 고레벨인것 같으셔서 이해하기 힘들겠다는 생각이 들었습니다. 죄송합니다. 제가 초보라서... ㅠㅠ 이거 읽어보면서, 코틀린 공부를 다시하고 올까 하는 생각이 들었습니다. 공부하고 다시 돌아와서 해보겠습니다. 다른 분들 레시피를 많이 분석해본지라, 공부 다시 하는데 얼마 안걸릴것 같습니다. 감사합니다~~^^
제가 고레벨은 아니고 현업에 아주 자주 쓰이는 코드 스타일을 적용한 것 뿐입니다. 수정할 수 없는 클래스들 ViewGroup이나  RecyclerView.ViewHolder 같은 데에 함수를 추가할 필요할 필요가 있을 때 사용하는 기능인 확장함수(Extension function)를 하단의 ViewGroup.kt나 ViewHolder.kt에 추가한 거구요.
listener?.onItemClicked(item) -> 여기서 ?는 optional operator입니다. Null이 아닐 때만 ?.이후를 실행합니다.
이 둘은 코틀린을 사용하면 기본적으로 사용하게 되는 기능들이구요,

fun interface -> function interface라고 최근에 추가된 기능으로 함수가 하나만 있는 코들린  interface를 사용하게 되면 자바의 인터페이스와는 달리 SAM이라는 것의 제약때문에
adapter.setOnItemClickListener { book ->
         
     }
이런 형태의 람다 표현식을 사용할 수가 없습니다. 이걸 지원하기 위해 interface 앞에 fun키워드를 붙이면 자바 인터페이스를 사용할 때 처럼 람다 표현식을 바로 사용할 수 있습니다.


위의 코드에서 이 것  빼고는 자바와 크게 다르지 않다는 것이 개인적인 생각입니다.

그리고 코틀린을 배우실 거면 Jetbrain에서 제공하는 무료 코스가 있습니다. Intellij나 Android Stuio에 Edu Tool이라는 플러그인 설치하시면 커뮤니티코스에 코트린 무료 코스가 2개 등록되어 있습니다. 질문이 영어긴 하지만 해설판도 있을 거라 생각합니다.이걸 활용해 보세요. 혹 해석이 막히시면 제가 도와드리겠습니다.
이게 이제서야 이해가 가는군요... 저 당시에는 저게 무슨말인지 하나도 알아듣지 못했습니다. 어댑터 내에서는 리스트에 clear()를 여전히 사용하지 않고 있습니다. 가르쳐주신대로는 하지 못했지만, 그때 당시에 해결했었고, 지금은 읽고 도움이 되는 것이 있을까하여 생각해보는 중입니다. 뭔가 좋은 방식을 얻어갈 수 있을것 같은 느낌입니다. 감사합니다. ^^
...