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

리사이클러뷰 뷰홀더에서 registerForActivityResult

0 추천
A액티비티에 리사이클러뷰가 있고, 그 리사이클러뷰의 뷰홀더에 있는 버튼을 클릭시

registerForActivityResult를 해서 B액티비티로 이동합니다. B액티비티에서 버튼클릭시 setresult를 날려서

뷰홀더에 있는 버튼을 변경을 해야되는데요 뷰홀더에서 할방법이 있나요..?
수원통학러 (3,570 포인트) 님이 2022년 5월 2일 질문

2개의 답변

0 추천

Adapter나 ViewHodler는 단순히 UI를 보여주는 컴포넌트 들이므로, 그용도로만 사용하는게 맞습니다. 이런 이벤트 처리는 이벤트 핸들러를 이용해서 외부에서 처리해야 합니다. 이런 질문에는 관련 코드를 올리시는게 답변을 훨씬 쉽게 하는데, 올리신 코드가 없는 관계로 간략하게 포인트만 말씀드릴게요.

상태는 top-down, 이벤트는 bottom-up의 원칙을 최대한 지키는 것이 모발앱에서는 필요합니다. 

class MyAdapter(val items: List<Item>, val onItemClicked: (Item) -> Unit) : RecyclerView.Adapter {

}

 위와 같은 형태로 상태에 해당하는 items는 Adapter -> ViewHolder처럼 아래로 전달되게 하고 이벤트에 해당하는 onItemClicked람는  ViewHolder->Adapter->Activity/Fragment로 위로 향하게 만드는 것이 원칙입니다.

Adapter에 전달된 이벤트 핸들러를 ViewHolder에 넘겨서 처리하세요. 뷰쪽에서는 아랫처럼 trail 람다를 사용해서 처리할 수 있습니다. 

val myAdapter = MyAdpter(emptyList()) { item ->
    showActivityB()
}

 

showActivityB함수에서는 ResultAPI를 사용해서 결과를 받아 items를 변경한 후 어댑터를 다시 전달한 후 갱신해주면 됩니다.
https://developer.android.com/training/basics/intents/result

spark (227,530 포인트) 님이 2022년 5월 2일 답변
spark님이 2022년 5월 2일 수정
0 추천

간단하게 Todo 예제로 해당 기능을 만들어 봤습니다.

먼저 Todo 클래스입니다.

data class Todo(
    val id: String,
    val name: String,
    val done: Boolean = false
) : Serializable

 

TodoAdapter
 

class TodoAdapter(
    private val onItemClicked: (Todo) -> Unit
): ListAdapter<Todo, TodoAdapter.TodoViewHolder>(DIFF_CALLBACK) {
    companion object {
        private val DIFF_CALLBACK = object: DiffUtil.ItemCallback<Todo>() {
            override fun areItemsTheSame(oldItem: Todo, newItem: Todo): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: Todo, newItem: Todo): Boolean {
                return oldItem == newItem
            }

        }
    }

    class TodoViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        private val text1: CheckedTextView by lazy { itemView.findViewById(android.R.id.text1) }

        fun bind(todo: Todo) {
            text1.text = todo.name
            text1.isChecked = todo.done
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TodoViewHolder {
        val itemView = LayoutInflater.from(parent.context).inflate(android.R.layout.simple_list_item_checked, parent,false)
        return TodoViewHolder(itemView)
    }

    override fun onBindViewHolder(holder: TodoViewHolder, position: Int) {
        val todo = getItem(position)
        holder.bind(todo)
        holder.itemView.setOnClickListener {
            onItemClicked(todo)
        }
    }
}

보시다시피, 이벤트 처리는 외부에서 처리할 수 있도록 리스너 함수를 연결해 주었습니다.

아이템을 클릭하면 편집화면에서 완료 체크버튼을 누를 수 있는 TodoEditActivity를 띄울 겁니다. 이렇게 하기위해서 아래처럼,
ActivityResultContract을 상속받는 클래스를 하나 만듭니다.

class GetTodoResult : ActivityResultContract<Todo, Todo?>() {

    companion object {
        const val TODO_ITEM = "todoItem"
    }

    override fun createIntent(context: Context, input: Todo): Intent {
        val intent = Intent(context, TodoEditActivity::class.java)
        intent.putExtra(TODO_ITEM, input)
        return intent
    }

    override fun parseResult(resultCode: Int, intent: Intent?): Todo? {
        if (resultCode != Activity.RESULT_OK) return null
        return intent?.getSerializableExtra(TODO_ITEM) as? Todo
    }
}

 

TodoEditActivity입니다. Todo 리스트에서 선택한 아이템의 완료 체크박스를 변경한 후 저장할 수 있도록 했습니다.
 

class TodoEditActivity : AppCompatActivity() {

    private val binding by lazy { ActivityTodoEditBinding.inflate(layoutInflater) }

    private lateinit var todo: Todo

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        setupViews()
        bindTodo()
    }

    private fun setupViews() {
        binding.apply {
            doneCheckBox.setOnCheckedChangeListener { _, checked ->
                todo = todo.copy(done = checked)
            }

            saveBtn.setOnClickListener {
                saveAndExit()
            }
        }
    }

    private fun saveAndExit() {
        val intent = Intent()
        intent.putExtra(GetTodoResult.TODO_ITEM, todo)
        setResult(Activity.RESULT_OK, intent)

        this.finish()
    }

    private fun bindTodo() {
        todo = intent.getSerializableExtra(GetTodoResult.TODO_ITEM) as Todo

        binding.apply {
            nameTxt.text = todo.name
            doneCheckBox.isChecked = todo.done
        }
    }
}

마지막으로 MainActivity입니다. Todo 리스트를 보여주고 클릭하면  TdoEditActivity를 띄운 후 변경시에 리스트를 갱신합니다.
 

class MainActivity : AppCompatActivity() {

    private var todoItems: List<Todo> = listOf(
        Todo("1", "Buy milk"),
        Todo("2", "Finish homework"),
        Todo("3", "Workout"),
        Todo("4", "Walk"),
        Todo("5", "Gathering with friends")
    )

    private val binding by lazy { ActivityMainBinding.inflate(layoutInflater) }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(binding.root)

        setupView()
    }

    private val todoAdapter by lazy {
        TodoAdapter { todo ->
            todoItemClicked(todo)
        }
    }

    private fun setupView() {
        binding.apply {
            todoListView.adapter = todoAdapter
            todoAdapter.submitList(todoItems)
        }
    }

    private val getTodo = registerForActivityResult(GetTodoResult()) { todo: Todo? ->
        if (todo != null)  todoUpdated(todo)
    }

    private fun todoItemClicked(todo: Todo) {
        getTodo.launch(todo)
    }

    private fun todoUpdated(todo: Todo) {
        todoItems = todoItems.map {
            if (it.id == todo.id) todo else  it
        }
        todoAdapter.submitList(todoItems)
    }
}

 

spark (227,530 포인트) 님이 2022년 5월 2일 답변
spark님이 2022년 5월 2일 수정
...