[Android/Flutter 교육] 54일차

MSU·2024년 3월 19일

Android-Flutter

목록 보기
57/85
post-thumbnail

Snapshot : 데이터를 가지고 있음
Reference : 데이터에 접근할 수 있음

게시판 프로젝트

글 읽기

ReadContentFragment 이동하기 전에 번들에 글 번호 담기

// AddContentFragment.kt




    // 글 작성처리 메서드
    fun uploadContentData(){
        CoroutineScope(Dispatchers.Main).launch {

            // 서버에서의 첨부 이미지 파일 이름
            var serverFileName:String? = null

            // 첨부된 이미지가 있다면
            if(isAddPicture == true) {
                // 이미지의 뷰의 이미지 데이터를 파일로 저장한다.
                Tools.saveImageViewData(contentActivity, fragmentAddContentBinding.imageViewAddContent, "uploadTemp.jpg")
                // 서버에서의 파일 이름
                serverFileName = "image_${System.currentTimeMillis()}.jpg"
                // 서버로 업로드한다.
                ContentDao.uploadImage(contentActivity, "uploadTemp.jpg", serverFileName)
            }

            // 게시글 시퀀스 값을 가져온다.
            val contentSequence = ContentDao.getContentSequence() + 1
            // 게시글 시퀀스 값을 업데이트 한다.
            ContentDao.updateContentSequence(contentSequence)

            // 업로드할 정보를 담아준다.
            val contentIdx = contentSequence
            val contentSubject = addContentViewModel.textFieldAddContentSubject.value!!
            val contentType = addContentViewModel.gettingContentType().number
            val contentText = addContentViewModel.textFieldAddContentText.value!!
            val contentImage = serverFileName
            val contentWriterIdx = contentActivity.loginUserIdx

            val simpleDateFormat = SimpleDateFormat("yyyy-MM-dd")
            val contentWriteDate = simpleDateFormat.format(Date())

            val contentState = ContentState.CONTENT_STATE_NORMAL.number

            val contentModel = ContentModel(contentIdx, contentSubject, contentType, contentText, contentImage, contentWriterIdx, contentWriteDate, contentState)
            // 업로드한다.
            ContentDao.insertContentData(contentModel)

            // 키보드를 내려준다.
            Tools.hideSoftInput(contentActivity)

            // 글 번호를 번들에 담는다.
            val readBundle = Bundle()
            readBundle.putInt("contentIdx", contentIdx)

            // ReadContentFragment로 이동한다.
            contentActivity.replaceFragment(ContentFragmentName.READ_CONTENT_FRAGMENT, true, true, readBundle)
        }
    }

글 번호를 이용해 글 데이터를 가져와 반환하는 메서드

// ContentDao.kt



        // 글 번호를 이용해 글 데이터를 가져와 반환한다.
        suspend fun selectContentData(contentIdx:Int):ContentModel?{
            // 글 데이터 객체를 담을 변수
            var contentModel:ContentModel? = null

            val job1 = CoroutineScope(Dispatchers.IO).launch {
                // 컬렉션에 접근할 수 있는 객체를 가져온다.
                val collectionReference = Firebase.firestore.collection("ContentData")
                // 컬렉션이 가지고 있는 문서들 중에 contentIdx 필드가 지정된 글 번호값하고 같은 Document들을 가져온다.
                val querySnapshot = collectionReference.whereEqualTo("contentIdx", contentIdx).get().await()
                // 가져온 글 정보를 객체에 담아서 반환받는다.
                // contentIdx가 같은 글은 존재할 수가 없기 때문에 첫 번째 객체를 바로 추출해서 사용한다.
                // toObject : 지정한 클래스를 가지고 객체를 만든 다음 가져온 데이터의 필드의 이름과 동일한 이름의 프로퍼티에 필드의 값을 담아준다.
                contentModel = querySnapshot.documents[0].toObject(ContentModel::class.java)
            }
            job1.join()

            return contentModel
        }
        

글 데이터 불러오기

// ReadContentFragment.kt


    // 현재 글 번호를 담을 변수
    var contentIdx = 0
    
    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        // Inflate the layout for this fragment

        // fragmentReadContentBinding = FragmentReadContentBinding.inflate(inflater)
        fragmentReadContentBinding = DataBindingUtil.inflate(inflater, R.layout.fragment_read_content, container, false)
        readContentViewModel = ReadContentViewModel()
        fragmentReadContentBinding.readContentViewModel = readContentViewModel
        fragmentReadContentBinding.lifecycleOwner = this

        contentActivity = activity as ContentActivity

        // 글 번호를 담는다.
        contentIdx = arguments?.getInt("contentIdx")!!

        settingToolbar()
        settingBackButton()
        settingInputForm()

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

        CoroutineScope(Dispatchers.Main).launch {
            // 현재 글 번호에 해당하는 글 데이터를 가져온다.
            val contentModel = ContentDao.selectContentData(contentIdx)
            // 가져온 데이터를 보여준다.
            readContentViewModel.textFieldReadContentSubject.value = contentModel?.contentSubject
            readContentViewModel.textFieldReadContentType.value = when(contentModel?.contentType){
                ContentType.TYPE_FREE.number -> ContentType.TYPE_FREE.str
                ContentType.TYPE_HUMOR.number -> ContentType.TYPE_HUMOR.str
                ContentType.TYPE_SOCIETY.number -> ContentType.TYPE_SOCIETY.str
                ContentType.TYPE_SPORTS.number -> ContentType.TYPE_SPORTS.str
                else -> ""
            }
            readContentViewModel.textFieldReadContentNickName.value = "홍길동"
            readContentViewModel.textFieldReadContentDate.value = contentModel?.contentWriteDate
            readContentViewModel.textFieldReadContentText.value = contentModel?.contentText
        }

    }

idx를 이용해 사용자 정보를 가져오는 메서드가 아직없으므로 메서드를 작성한다.

// UserDao.kt


        // 사용자 번호를 통해 사용자 정보를 가져와 반환한다.
        suspend fun gettingUserInfoByUserIdx(userIdx:Int):UserModel?{
            var userModel:UserModel? = null

            val job1 = CoroutineScope(Dispatchers.IO).launch {
                // UserData 컬렉션 접근 객체를 가져온다.
                val collectionReference = Firebase.firestore.collection("UserData")
                // userIdx 필드가 매개변수로 들어오는 userIdx와 같은 문서들을 가져온다.
                val querySnapshot = collectionReference.whereEqualTo("userId", userIdx).get().await()
                // 가져온 문서 객체들이 들어있는 리스트에서 첫 번째 객체를 추출한다.
                // 아이디가 동일한 사용자는 없기 때문에 무조건 하나만 나오기 때문
                userModel = querySnapshot.documents[0].toObject(UserModel::class.java)
            }
            job1.join()

            return userModel
        }

사용자 닉네임을 불러올 수 있도록 수정해준다.

// ReadContentFragment.kt


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

        CoroutineScope(Dispatchers.Main).launch {
            // 현재 글 번호에 해당하는 글 데이터를 가져온다.
            val contentModel = ContentDao.selectContentData(contentIdx)
            // 글을 작성한 사용자의 번호를 통해 사용자 정보를 가져온다.
            val userModel = UserDao.gettingUserInfoByUserIdx(contentModel?.contentWriterIdx!!)

            // 가져온 데이터를 보여준다.
            readContentViewModel.textFieldReadContentSubject.value = contentModel.contentSubject
            readContentViewModel.textFieldReadContentType.value = when(contentModel.contentType){
                ContentType.TYPE_FREE.number -> ContentType.TYPE_FREE.str
                ContentType.TYPE_HUMOR.number -> ContentType.TYPE_HUMOR.str
                ContentType.TYPE_SOCIETY.number -> ContentType.TYPE_SOCIETY.str
                ContentType.TYPE_SPORTS.number -> ContentType.TYPE_SPORTS.str
                else -> ""
            }
            readContentViewModel.textFieldReadContentNickName.value = userModel?.userNickName
            readContentViewModel.textFieldReadContentDate.value = contentModel.contentWriteDate
            readContentViewModel.textFieldReadContentText.value = contentModel.contentText
        }

    }

서버에서 이미지 불러오기

이미지도 불러와야 하므로 서버에 저장한 이미지를 불러오는 메서드를 작성해야 한다.
Glide를 이용하면 서버로부터 해당 이미지를 찾아 Uri객체를 전달받아 이미지뷰에 넣어주는 작업을 해준다.
https://github.com/bumptech/glide

build.gradle.kts(:app)에 추가


dependencies {

    implementation("androidx.core:core-ktx:1.12.0")
    implementation("androidx.appcompat:appcompat:1.6.1")
    implementation("com.google.android.material:material:1.11.0")
    implementation("androidx.constraintlayout:constraintlayout:2.1.4")
    testImplementation("junit:junit:4.13.2")
    androidTestImplementation("androidx.test.ext:junit:1.1.5")
    androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
    implementation("androidx.core:core-splashscreen:1.0.1")

    implementation(platform("com.google.firebase:firebase-bom:32.7.4"))
    implementation("com.google.firebase:firebase-analytics")

    implementation("com.google.firebase:firebase-firestore:24.10.3")
    implementation("com.google.firebase:firebase-storage:20.3.0")

    implementation("com.github.bumptech.glide:glide:4.16.0")
}

이미지 데이터를 받아와 이미지뷰에 보여주는 메서드 작성
이미지뷰에 보여주는 작업은 Dispatchers.Main으로 코루틴을 열어준다

// ContentDao.kt


        // 이미지 데이터를 받아오는 메서드
        suspend fun gettingContentImage(context:Context, imageFileName:String, imageView:ImageView){
            CoroutineScope(Dispatchers.IO).launch {
                // 이미지에 접근할 수 있는 객체를 가져온다.
                val storageRef = Firebase.storage.reference.child("iamge/$imageFileName")
                // 이미지의 주소를 가지고 있는 Uri 객체를 받아온다.
                val imageUri = storageRef.downloadUrl.await()
                // 이미지 데이터를 받아와 이미지 뷰에 보여준다.
                // 화면 관련된 작업이므로 Dispatchers.Main으로 코루틴을 한번 더 열어준다.
                CoroutineScope(Dispatchers.Main).launch{
                    Glide.with(context).load(imageUri).into(imageView)
                    // 이미지뷰가 나타나게 한다.
                    imageView.visibility = View.VISIBLE
                }
            }
            // 이미지는 용량이 매우 클 수 있다. 즉 이미지 데이터를 내려받는데 시간이 오래걸리 수도 있다.
            // 이에, 이미지 데이터를 받아와 보여주는 코루틴을 작업이 끝날 때 까지 대기하지 않는다.
            // 그 이유는 데이터를 받아오는데 걸리는 오랜 시간 동안 화면에 아무것도 나타나지 않을 수 있기 때문이다.
            // 따라서 이 메서드는 제일 마지막에 호출해야 한다.(다른 것들을 모두 보여준 후에...)
        }

글 데이터를 불러오는 과정에서 이미지뷰를 먼저 안보이게 한 뒤 이미지를 가져오면 이미지뷰를 보이게 메서드를 수정한다.

// ReadContentFragment.kt



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

        CoroutineScope(Dispatchers.Main).launch {

            // 이미지뷰를 안보이게 한다.
            fragmentReadContentBinding.imageViewReadContent.visibility = View.INVISIBLE

            // 현재 글 번호에 해당하는 글 데이터를 가져온다.
            val contentModel = ContentDao.selectContentData(contentIdx)
            // 글을 작성한 사용자의 번호를 통해 사용자 정보를 가져온다.
            val userModel = UserDao.gettingUserInfoByUserIdx(contentModel?.contentWriterIdx!!)

            // 가져온 데이터를 보여준다.
            readContentViewModel.textFieldReadContentSubject.value = contentModel.contentSubject
            readContentViewModel.textFieldReadContentType.value = when(contentModel.contentType){
                ContentType.TYPE_FREE.number -> ContentType.TYPE_FREE.str
                ContentType.TYPE_HUMOR.number -> ContentType.TYPE_HUMOR.str
                ContentType.TYPE_SOCIETY.number -> ContentType.TYPE_SOCIETY.str
                ContentType.TYPE_SPORTS.number -> ContentType.TYPE_SPORTS.str
                else -> ""
            }
            readContentViewModel.textFieldReadContentNickName.value = userModel?.userNickName
            readContentViewModel.textFieldReadContentDate.value = contentModel.contentWriteDate
            readContentViewModel.textFieldReadContentText.value = contentModel.contentText

            // 이미지 데이터를 불러온다.
            if(contentModel.contentImage != null){
                ContentDao.gettingContentImage(contentActivity, contentModel.contentImage!!, fragmentReadContentBinding.imageViewReadContent)
            }
        }

    }

스켈레톤 로딩화면

요즘은 로딩바 표시 대신 스켈레톤 로딩 화면을 사용한다.
데이터를 불러올 때 로딩중인 화면을 먼저 표시하고 데이터 불러오기가 완료되면
원래 화면을 표시한다.
따라서 로딩중인 화면을 별도로 직접 만들어주거나 라이브러리(Shimmer 등)를 사용하면 된다.

profile
안드로이드공부

0개의 댓글