[Android/Flutter 교육] 56일차

MSU·2024년 3월 21일

Android-Flutter

목록 보기
59/85
post-thumbnail

게시판 프로젝트

검색 기능

검색 기능을 구현할 때 보통 쿼리문의 Like절을 사용하는데,
NoSql에서는 관계형데이터베이스와 같은 Like절이 없다.
따라서 게시글을 모두 불러온 후 검색어에 해당되는 게시글을 체크하는 과정이 필요하다.

따로 찾아보니 like절 대신 정규표현식을 사용하는 방법도 있다고 한다.
https://stackoverflow.com/questions/3305561/how-to-query-mongodb-with-like

검색결과를 가져와 리사이클러뷰 갱신하는 메서드

// MainFragment.kt



    // 검색 결과의 데이터를 가져와 검색 화면의 리사이클러뷰를 갱신한다.
    fun gettingSearchData(){
        // 검색어를 가져온다.
        // SearchView에 있는 입력요소(editText)를 추출하여 사용자가 입력한 내용을 가져온다.
        val keyword = fragmentMainBinding.searchViewMain.editText.text.toString()

        // 검색 결과를 가지고 있는 리스트를 비워준다.
        searchList.clear()

        CoroutineScope(Dispatchers.Main).launch {
            // 현재 게시판에 해당하는 게시글을 모두 가져온다.
            val tempList = ContentDao.gettingContentList(contentType)
            // 사용자 정보를 가져온다.
            userList = UserDao.getUserAll()
            // 가져온 게시글 데이터 중에서 검색어를 포함하는 제목의 글 데이터만 담는다.
            tempList.forEach {
                if(it.contentSubject.contains(keyword)){
                    // 검색 결과를 담는 리스트에 담아준다.
                    searchList.add(it)
                }
            }

            // 리사이클러뷰를 갱신한다.
            fragmentMainBinding.recyclerViewMainSearch.adapter?.notifyDataSetChanged()
        }
    }

검색 화면의 리사이클러뷰 어댑터 수정

// MainFragment.kt



    // 검색 화면의 RecyclerView의 어댑터
    inner class SearchRecyclerViewAdapter : RecyclerView.Adapter<SearchRecyclerViewAdapter.SearchViewHolder>(){
        inner class SearchViewHolder(rowMainBinding: RowMainBinding) : RecyclerView.ViewHolder(rowMainBinding.root){
            val rowMainBinding:RowMainBinding

            init {
                this.rowMainBinding = rowMainBinding

                this.rowMainBinding.root.layoutParams = ViewGroup.LayoutParams(
                    ViewGroup.LayoutParams.MATCH_PARENT,
                    ViewGroup.LayoutParams.WRAP_CONTENT
                )
            }
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SearchViewHolder {
            val rowMainBinding = RowMainBinding.inflate(layoutInflater)
            val mainViewHolder = SearchViewHolder(rowMainBinding)
            return mainViewHolder
        }

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

        override fun onBindViewHolder(holder: SearchViewHolder, position: Int) {
            holder.rowMainBinding.textViewRowMainSubject.text = searchList[position].contentSubject

            // 사용자의 수 만큼 반복한다.
            userList.forEach {
                // 사용자 번호와 작성자 번호가 같으면 출력하고 중단한다.
                if(it.userIdx == searchList[position].contentWriterIdx){
                    holder.rowMainBinding.textViewRowMainNickName.text = it.userNickName
                    return@forEach
                }
            }

            // 항목을 눌렀을 때 동작할 리스너를 연결해준다.
            holder.rowMainBinding.root.setOnClickListener {
                // 필요한 데이터를 담아준다.
                val readBundle = Bundle()
                readBundle.putInt("contentIdx", searchList[position].contentIdx)
                // 글 읽는 화면으로 이동한다.
                contentActivity.replaceFragment(ContentFragmentName.READ_CONTENT_FRAGMENT, true, true, readBundle)
            }
        }
    }

검색 결과를 가져오는 메서드 호출

// MainFragment.kt


    // SearchView 설정
    fun settingSearchBar(){
        fragmentMainBinding.apply {
            searchBarMain.apply {
                // SearchBar에 보여줄 메시지
                hint = "여기를 눌러 검색해주세요"
                // 메뉴
                inflateMenu(R.menu.menu_main_search_menu)
                setOnMenuItemClickListener {
                    // 글 작성 화면이 나타나게 한다.
                    contentActivity.replaceFragment(ContentFragmentName.ADD_CONTENT_FRAGMENT, true, true, null)
                    true
                }
            }
            
            searchViewMain.apply { 
                // SearchView에 보여줄 메시지
                hint = "검색어를 입력해주세요"
                // 검색 시 사용하는 키보드의 엔터키를 누르면 동작하는 리스너
                editText.setOnEditorActionListener { view, actionId, event ->
                    if(event != null && event.action == KeyEvent.ACTION_DOWN){
                        // 검색 결과를 가져와 보여주는 메서드를 호출한다.
                        gettingSearchData()
                    }
                    // 반환값이 true면 키보드가 고정되고, false면 키보드가 내려간다.
                    false
                }
            }
        }
    }

사용자에 따른 툴바 메뉴 다르게 보여주기

처음 글을 불러올 때 메뉴를 모두 가려주기

// ReadContentFragment.kt


    // 툴바 설정
    fun settingToolbarReadContent(){
        fragmentReadContentBinding.apply {
            toolbarReadContent.apply {
                // 타이틀
                title = "글 읽기"
                // 네비게이션
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    backProcesss()
                }
                // 메뉴
                inflateMenu(R.menu.menu_read_content)
                // 모든 메뉴를 보이지 않는 상태로 둔다.
                // 글 정보를 가져온 다음 메뉴를 노출시킨다.
                menu.findItem(R.id.menuItemReadContentReply).isVisible = false
                menu.findItem(R.id.menuItemReadContentModify).isVisible = false
                menu.findItem(R.id.menuItemReadContentDelete).isVisible = false

글 데이터를 가져올 때 사용자에 따라 메뉴 다르게 보여주기

// ReadContentFragment.kt




    // 입력 요소에 값을 넣어준다.
    fun settingInputForm(){

        // 이미지뷰를 안보이게 한다.
        fragmentReadContentBinding.imageViewReadContent.visibility = View.INVISIBLE
        // 입력 요소에 띄어쓰기를 넣어준다.
        readContentViewModel.textFieldReadContentSubject.value = " "
        readContentViewModel.textFieldReadContentText.value = " "
        readContentViewModel.textFieldReadContentDate.value = " "
        readContentViewModel.textFieldReadContentType.value = " "
        readContentViewModel.textFieldReadContentNickName.value = " "


        CoroutineScope(Dispatchers.Main).launch {

            // 현재 글 번호에 해당하는 글 데이터를 가져온다.
            val contentModel = ContentDao.selectContentData(contentIdx)

            // 로그인한 사용자가 글을 작성한 사용자와 같다면 수정과 삭제 메뉴를 보여준다.
            if(contentActivity.loginUserIdx == contentModel?.contentWriterIdx){
                fragmentReadContentBinding.toolbarReadContent.menu.findItem(R.id.menuItemReadContentModify).isVisible = true
                fragmentReadContentBinding.toolbarReadContent.menu.findItem(R.id.menuItemReadContentDelete).isVisible = true
            }
            // 다르다면
            else {
                fragmentReadContentBinding.toolbarReadContent.menu.findItem(R.id.menuItemReadContentReply).isVisible = true
            }

글작성자 계정으로 로그인 한 경우 수정,삭제 메뉴만 보인다.

다른 계정으로 로그인 한 경우 댓글 메뉴만 보인다.

게시글 삭제 기능(글 상태 변경)

실제 데이터를 삭제하지 않고 글의 상태를 변경해준다.

특정 필드의 값만 수정하고 싶을때 객체를 전달하면 모든 필드가 업데이트가 되고,
객체가 아닌 맵을 셋팅하여 전달하면 특정 필드만 업데이트 할 수 있다.

글의 상태를 변경하는 메서드

// ContentDao.kt


        // 글의 상태를 변경하는 메서드
        suspend fun updateContentState(contentIdx:Int, newState:ContentState){
            val job1 = CoroutineScope(Dispatchers.IO).launch {
                // 컬렉션에 접근할 수 있는 객체를 가져온다.
                val collectionReference = Firebase.firestore.collection("ContentData")
                // 컬렉션이 가지고 있는 문서들 중에 contentIdx 필드가 지정된 글 번호값하고 같은 Document들을 가져온다.
                val query = collectionReference.whereEqualTo("contentIdx", contentIdx).get().await()

                // 저장할 데이터를 담을 HashMap을 만들어준다.
                val map = mutableMapOf<String, Long>()
                map["contentState"] = newState.number.toLong()
                // 저장한다.
                // 가져온 문서 중 첫 번째 문서에 접근하여 데이터를 수정한다.
                query.documents[0].reference.set(map)
            }
            job1.join()
        }

삭제 메뉴 버튼을 누르면 메서드 호출

// ReadContentFragment.kt



    // 툴바 설정
    fun settingToolbarReadContent(){
        fragmentReadContentBinding.apply {
            toolbarReadContent.apply {
                // 타이틀
                title = "글 읽기"
                // 네비게이션
                setNavigationIcon(R.drawable.arrow_back_24px)
                setNavigationOnClickListener {
                    backProcesss()
                }
                // 메뉴
                inflateMenu(R.menu.menu_read_content)
                // 모든 메뉴를 보이지 않는 상태로 둔다.
                // 글 정보를 가져온 다음 메뉴를 노출시킨다.
                menu.findItem(R.id.menuItemReadContentReply).isVisible = false
                menu.findItem(R.id.menuItemReadContentModify).isVisible = false
                menu.findItem(R.id.menuItemReadContentDelete).isVisible = false

                setOnMenuItemClickListener {
                    // 메뉴의 id로 분기한다.
                    when(it.itemId){
                        // 댓글
                        R.id.menuItemReadContentReply -> {
                            // 댓글을 보여줄 BottomSheet를 띄운다.
                            showReplyBottomSheet()
                        }
                        // 수정하기
                        R.id.menuItemReadContentModify -> {
                            // 수정 화면이 보이게 한다.
                            contentActivity.replaceFragment(ContentFragmentName.MODIFY_CONTENT_FRAGMENT, true, true, null)
                        }
                        // 삭제하기
                        R.id.menuItemReadContentDelete -> {
                            // 삭제 확인을 받기 위한 다이얼로그를 띄워준다.
                            MaterialAlertDialogBuilder(contentActivity).apply {
                                setTitle("삭제하기")
                                setMessage("삭제하면 복원할 수 없습니다")
                                setNegativeButton("취소", null)
                                setPositiveButton("삭제"){ dialogInterface: DialogInterface, i: Int ->
                                    CoroutineScope(Dispatchers.Main).launch {
                                        // 글 상태를 삭제 상태로 변경한다.
                                        ContentDao.updateContentState(contentIdx, ContentState.CONTENT_STATE_REMOVE)
                                        // 글 목록 화면으로 돌아간다.
                                        backProcesss()
                                    }
                                }
                                show()
                            }
                        }
                    }
                    true
                }
            }
        }
    }

profile
안드로이드공부

0개의 댓글