20230901 뉴스 리더

기메단·2023년 9월 1일
0

TIL

목록 보기
33/44

두 개의 프래그먼트로 간단한 뉴스 리더 앱을 만들어보자.

NewsAdapter

class NewsAdapter(private val newsList:MutableList<NewsItem>,private val itemClick: (NewsItem) -> Unit) : RecyclerView.Adapter<NewsAdapter.Holder>() {


    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) : Holder {
        val binding = ItemBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }

    override fun onBindViewHolder(holder: Holder, position: Int) {
        val newsItem = newsList[position]
        holder.bind(newsItem)

        holder.itemView.setOnClickListener {
            itemClick(newsItem)
        }
    }

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

    inner class Holder(private val binding: ItemBinding) : RecyclerView.ViewHolder(binding.root) {
        fun bind(newsItem: NewsItem) {
            binding.titleText.text = newsItem.title
            binding.smallText.text = newsItem.article
            binding.root.setOnClickListener {
            }
        }
    }
}

NewsTitle 프래그먼트에서 recyclerView를 사용할 생각이기 때문에 Adapter를 만들어줬다.

NewsTitle

class NewsTitle : Fragment() {

    private lateinit var adapter: NewsAdapter
    private lateinit var binding: FragmentNewsTitleBinding
    private val newsData = NewsData()

	// 프래그먼트 뷰가 초기화되며, 정상적으로 초기화가 되었다면 뷰 객체를 반환한다.
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentNewsTitleBinding.inflate(inflater, container, false)
        return binding.root

    }

	// onCreateView()에서 뷰 객체가 반환된 직후에 호출되며, 뷰가 완전히 생성되었음을 보장한다.
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        val newsDummy = newsData.getNewsData()

		// bundle로 데이터 전달 
        // bundle : 문자열로 된 키와 여러가지의 타입의 값을 매핑하여 다양한 데이터 타입 전달 가능. 
        adapter = NewsAdapter(newsDummy) { clickedNewsItem ->
            val detailFragment = NewsDetail()
            val bundle = Bundle()
            bundle.putString("title", clickedNewsItem.title)
            bundle.putString("article", clickedNewsItem.article)
            detailFragment.arguments = bundle
            // arguments : arguments에 Bundle()을 넘겨줌으로써 데이터를 넘겨줄 수 있다.
            // arguments는 Bundle()을 파라미터로 받고 
            // Bundle()은 put~~() 메소드를 호출하여 데이터를 담아서 arguments에 넘김

            val transaction = parentFragmentManager.beginTransaction()
            transaction.replace(R.id.frame, detailFragment)
            transaction.addToBackStack(null)
            transaction.commit()
            // transaction : 프래그먼트를 추가/교체/삭제하는 작업을 제공
            // FragmentTransaction의 인스턴스는 FragmentManager의 beginTransaction() 메소드를 통해 얻을 수 있다. 
        }

		// recyclerView와 어댑터 연결 
        binding.titleRecycler.adapter = adapter
        binding.titleRecycler.layoutManager = LinearLayoutManager(context)
        
        
	    // recyclerView에 divider 만들어줌
        val dividerItemDecoration = DividerItemDecoration(binding.titleRecycler.context, LinearLayoutManager(context).orientation)
        binding.titleRecycler.addItemDecoration(dividerItemDecoration)
		
        // divider의 간격 조절 
        val spaceDecoration = VerticalSpaceItemDecoration(30)
        binding.titleRecycler.addItemDecoration(spaceDecoration)

    }

    inner class VerticalSpaceItemDecoration(private val verticalSpaceHeight: Int) :
        RecyclerView.ItemDecoration() {

        override fun getItemOffsets(
            outRect: Rect,
            view: View,
            parent: RecyclerView,
            state: RecyclerView.State
        ) {
            outRect.bottom = verticalSpaceHeight
            outRect.top = verticalSpaceHeight
        }
    }
}

NewsDetail

class NewsDetail : Fragment() {

    private lateinit var binding: FragmentNewsDetailBinding

	// 프래그먼트 뷰가 초기화되며, 정상적으로 초기화가 되었다면 뷰 객체를 반환한다.
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        binding = FragmentNewsDetailBinding.inflate(inflater, container, false)

        val textView1: TextView = binding.textView1
        val textView2: TextView = binding.textView2
        
        // 'arguments'는 번들 객체를 나타내며, 객체에 전달된 데이터가 포함되어 있다.
        // bundle로 전달된 데이터를 받아옴. 
        // null이 아닐 경우에만 코드가 실행된다. 
        arguments?.let {
            val titleDetail = it.getString("title")
            val articleDetail = it.getString("article")
            textView1.text = titleDetail
            textView2.text = articleDetail

        }
        return binding.root
    }
}

MainActivity

class MainActivity : AppCompatActivity() {

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

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

		// 'supportFragmentManager'를 사용하여 프래그먼트의 트랜잭션을 시작.
        // 트랜잭션은 fragment를 추가, 교체 or 제거하는 작업을 관리.
        if (savedInstanceState == null) {
            val titleFragment = NewsTitle()
            supportFragmentManager.beginTransaction()
                .replace(R.id.frame, titleFragment)
                .commit()
                // replace(R.id.frame) : 화면에 표시되는 프래그먼트를 
                // titleFragment 안에 있는 id값이 frame인 레이아웃으로 교체. 
                // commit() : 트랜잭션을 확정하고 적용. 
        }
    }
}

NewsItem

data class NewsItem(
    val title: String,
    val article: String) {

}

초라한 뉴스 리더 앱 완성!

아 그리고 코드엔 이상이 없는데 앱이 강제종료 된다던가 켜지지 않는다면 코드와 로그캣을 잘확인해보자. adapter 연결 코드를 엉뚱한 곳에 써놔서 한참을 헤맸다^.^

전체 코드 Kotlin_Pratice(News)

1개의 댓글

comment-user-thumbnail
2023년 9월 6일

잘 확인해볼게요 ! ^>^

답글 달기