Android Jetpack

Safe args로 Fragment 간 데이터 전달하기

wdadaww 2023. 1. 26. 02:34

 저번에 했던 Jetpack Navigation 이 적용된 프로젝트에서  Safe Args를 사용하여 Fragment 간의 데이터를 주고받는 방법과  WebView를 이용해서 책에 정보를 Parcelable로 이용해 보여주는 방법까지 알아보겟습니다.


1. Safe Args을 추가

  • build.gradle(프로젝트)
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
    repositories {
        google()
    }
    dependencies {
        classpath("androidx.navigation:navigation-safe-args-gradle-plugin:2.5.3")
    }
}
  • build.gradle(앱 모듈)
plugins {

    //Safe Args
    id "androidx.navigation.safeargs.kotlin"

Navigation  |  Android 개발자  |  Android Developers

 

Navigation  |  Android 개발자  |  Android Developers

컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Navigation Navigation은 Android 애플리케이션 내에서 '대상' 사이를 탐색하는 프레임워크로, 대상이 Fragment, Activity

developer.android.com

 


 

2.  Parcelable을 추가

  • build.grdle(앱 모듈)
plugins {

    id "kotlin-parcelize"
}

 

Parcelable 구현 생성기  |  Android 개발자  |  Android Developers

 

Parcelable 구현 생성기  |  Android 개발자  |  Android Developers

Parcelable 구현 생성기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. kotlin-parcelize 플러그인은 Parcelable 구현 생성기를 제공합니다. Parcelable 지원을 포함하려

developer.android.com

  • Book
package com.example.kakaobook.ui.view.data.model


import android.os.Parcelable
import com.squareup.moshi.Json
import com.squareup.moshi.JsonClass
import kotlinx.parcelize.Parcelize

@Parcelize
@JsonClass(generateAdapter = true)
data class Book(
    @Json(name = "authors")
    val authors: List<String>,
    @Json(name = "contents")
    val contents: String,
    @Json(name = "datetime")
    val datetime: String,
    @Json(name = "isbn")
    val isbn: String,
    @Json(name = "price")
    val price: Int,
    @Json(name = "publisher")
    val publisher: String,
    @Json(name = "sale_price")
    val salePrice: Int,
    @Json(name = "status")
    val status: String,
    @Json(name = "thumbnail")
    val thumbnail: String,
    @Json(name = "title")
    val title: String,
    @Json(name = "translators")
    val translators: List<String>,
    @Json(name = "url")
    val url: String
) : Parcelable

-> @Parcelize로 클래스에 주석을 달면 다음  같이 Parcelable 구현이 자동으로 생성됩니다.

 


  • 데이터를 전달받을 Fragment를 생성해준다.
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".ui.view.ui.view.BookFragment">

    <WebView
        android:id="@+id/webview"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>

 -> Webview를 통해  전달받을 ui를 구현햇습니다.


  • booksearch_nav_graph.xml 로 가서 새로만들엇던 Frgment를 New Destination해주자
  • 데이터를 전달받을 Fragment_book 에 Argument를 추가한다.

전달할 데이터의 타입이 기초 타입인 경우는 전달할 데이터 타입에 맞춰서 선택해 주면 되고,

그 외의 경우에는 Custom Parcelable을 선택한다. 선택하면 [Select Class]란 창이 뜨는데 내가 정의한 데이터 클래스 중 Parcelable의 구현인 클래스만 뜨게 된다. 전달하고 싶은 데이터의 타입을 선택해 주면 된다.

 

-> 위의 이미지에서 fragment_search, fragment_favoirte가 fragment_book으로 데이터 전달이 이루어진다고 가정했고,

fragment_book에 Argument(전달받을 데이터)를 추가해주엇다.


  • SearchFragment 에서 검색결과를 수행햇을때 fragment_book에 데이터를 전달해주도록합니다.           

1.BookSearchAdpater에서  뷰홀더 클래스안에 setonItemclicklister를 만들어준다.

package com.example.kakaobook.ui.view.ui.view.adapter

import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter
import com.example.kakaobook.databinding.ItemBookViewBinding
import com.example.kakaobook.ui.view.data.model.Book

class BookSearchAdapter : ListAdapter<Book, BookSearchViewHolder>(diffUtilCallback) {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BookSearchViewHolder {
        return BookSearchViewHolder(
            ItemBookViewBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        )
    }

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

        holder.bind(currentList[position])
        holder.itemView.setOnClickListener {
            onItemClickListener?.let { it(currentList[position])}
        }
    }
    private var onItemClickListener: ((Book) -> Unit)? = null
    fun setOnItemClickListener(listener: (Book) -> Unit) {
        onItemClickListener = listener
    }

    companion object {
        private val diffUtilCallback = object : DiffUtil.ItemCallback<Book>() {
            override fun areItemsTheSame(oldItem: Book, newItem: Book): Boolean {
                return oldItem == newItem
            }

            override fun areContentsTheSame(oldItem: Book, newItem: Book): Boolean {
                return oldItem.isbn == newItem.isbn
            }
        }
    }
}

-> 클릭 이벤트를 받은 뷰홀더를 특정하는 역할을 어댑터가 수행해야 하기 때문에  setOnItemClickListener함수가 실행되었을때  책에 정보를 보여주도록한다.

 

 

2. SearchFragment

 private fun setupRecyclerView() {
        bookSearchAdapter = BookSearchAdapter()
        binding.SearchResult.layoutManager = LinearLayoutManager(context)
        binding.SearchResult.adapter = bookSearchAdapter

        bookSearchAdapter.setOnItemClickListener {
            findNavController().navigate(SearchFragmentDirections.actionFragmentSearchToFrgmentBook(it))
        }
    }

-> 어댑터에 클릭이벤트를받앗을때  navigate에 바꿔줄 프래그먼트를 SearchFragment에서   BookFragment로  교체되는걸알수있다.

 

 

3. BookFragment

package com.example.kakaobook.ui.view.ui.view

import android.annotation.SuppressLint
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.webkit.WebViewClient
import androidx.fragment.app.Fragment
import androidx.navigation.fragment.navArgs
import com.example.kakaobook.databinding.FragmentBookBinding




class BookFragment : Fragment() {
    private var mbinding : FragmentBookBinding? = null
    private val binding get() = mbinding!!
    private val args by navArgs<BookFragmentArgs>()

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        mbinding = FragmentBookBinding.inflate(inflater, container, false)
        return binding.root
    }


    @SuppressLint("SetJavaScriptEnabled")
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        
        binding.webview.apply {
            webViewClient = WebViewClient()
            settings.javaScriptEnabled = true
            loadUrl(args.book.url)
        }

    }

    override fun onPause() {
        binding.webview.onPause()
        super.onPause()
    }

    override fun onResume() {
        binding.webview.onResume()
        super.onResume()
    }

    override fun onDestroyView() {
        mbinding = null
        super.onDestroyView()


    }

}

-> 먼저  by navArgs 를 사용해  args 를 나중에 초기화시키겟다. 

 

웹 애플리케이션 또는 웹페이지만 클라이언트 애플리케이션의 일부로 제공하려는 경우 WebView를 사용한다.

 webview에서 webviewClient를연결해준다.  webview에 연결된 Setting을 통해  javascript 가 사용가능 하도록 true로 설정해준다.

webview에서 웹페이지를 로드하려면 loadurl()을 사용해 전달받은  url 값을 webview에 표시해준다.

 

출처 :WebView에서 웹 앱 빌드  |  Android 개발자  |  Android Developers

 

WebView에서 웹 앱 빌드  |  Android 개발자  |  Android Developers

WebView에서 웹 앱 빌드 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. 웹 애플리케이션 또는 웹페이지만 클라이언트 애플리케이션의 일부로 제공하려는 경

developer.android.com