Android Studio

Android - Firebase Storage에 사진 올리기 (1/2)

내손은개발 🐾 2024. 3. 15. 21:15

이번에는 게시글을 작성하고 작성한 게시글이 recyclerView를 통해 나타나게 하려고 한다.

생각한 게시글은 image 한 장과 게시글.

 

메인 화면에 floatingActionButton을 만들어주고 누르면 게시글을 작성하는 페이지로 넘어가는 것을 만들면

 

 

 

1) 메인 Fragment에 RecyclerView와 FloatingActionButton 추가

원하는 Fragment에 생성하면 된다.

코드

더보기

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/homeRecyclerView"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/writeButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        android:layout_margin="30dp"
        android:backgroundTint="@color/grey"
        android:src="@drawable/icon_add"/>

 

2) 버튼을 눌렀을 때 나타날 Fragment 생성

원하는 이름의 Fragment와 layout 생성하시고 연결하면 된다.ㅇㅇ..

간단해서 생략

 

3) 지지난번에 만든 navigation을 활용해서 페이지 연결

모르겠으면 저어번에 올린 게시글 참고

 

 

 

 

4) Fragment 화면 이동

binding.writeButton.setOnClickListener {
	val action = HomeFragmentDirections.actionHomeFragmentToWriteFragment()
	findNavController().navigate(action)
}

 

저번에 설명을 작성했나 모르겠지만 다시 작성(?)하자면

2번 줄의 코드를 설명하자면

HomeFragmentDirections : navigation의 HomeFragment(내가 메인으로 둔 Fragment)

actionHomeFragmentToWriteFragment : action / HomeFragment / To / WriteFragment 이렇게 띄어서 읽어보면 편하다.

3번처럼 이어줬다면 navigation에 <action ...> 이런것이 생겼는데 그 action의 id를 가져온 것이다!

설명을 너무 못해서 제가 이해한거 이해하려하지말고 참고한 자료 보시길..

 

[Kotlin] Fragment Navigation 화면 전환 방법

Fragment Navigation 이란? Android Jetpack의 Navigation Component를 사용하여 페이지 이동을 쉽게 구현하게 해주는 방법이다. * 공식 문서 * https://developer.android.com/guide/navigation/navigation-getting-started 1. Navigation

cocococo.tistory.com

 

그냥 게시글을 작성하게 만드는 것은 안된다.

저번 글에서 인증된 사용자만 게시글에 권한이 있게 만들어 주었다.

그러면 조건문으로 auth에 인증된 사용자만 접근 가능하게 하려면

        binding.writeButton.setOnClickListener {
            if(Firebase.auth.currentUser != null){
                val action = HomeFragmentDirections.actionHomeFragmentToWriteArticleFragment()
                findNavController().navigate(action)
            }
            Snackbar.make(view,"로그인 후 사용해주세요.",Snackbar.LENGTH_SHORT).show()
        }

if 조건문으로 Firebase.auth.currentUser가 null이 아니면 접근 가능하게 한다.

 

 

5) 갤러리에서 사진 가져오기

페이지 이동까지 마쳤으면 갤러리에 접근 허용과 사진을 선택했을 때 사진을 가져올 수 있어야한다.

밑에 공식문서를 읽고 가면 좋다.

 

저장소 액세스 프레임워크를 사용하여 파일 열기  |  Android 개발자  |  Android Developers

이 페이지는 Cloud Translation API를 통해 번역되었습니다. 저장소 액세스 프레임워크를 사용하여 파일 열기 컬렉션을 사용해 정리하기 내 환경설정을 기준으로 콘텐츠를 저장하고 분류하세요. Androi

developer.android.com

 

5_0) UI 수정

사진을 눌렀을 때 가져올 수 있게 writeFragment layout 추가

사진 받을 부분은 ImageView로 받았다.

+ 버튼으로 이미지를 받고 x 버튼은 사진을 다시 지우는 버튼

 

개인 id 사항

선택된 이미지 띄우는 ImageView =  selectedImageView

이미지 넣는 + 버튼 : plusImageButton

이미지 지우는 x 버튼 : clearImageButton

 

 

밑에 코드에서 찾으실까봐 필요한 부분 id만 올렸다.

나머지는 개인 취향껏

 

 

5_1) 갤러리 접근 허용

 

 

공유 저장소 미디어 파일에 액세스  |  Android Developers

DataStore offers a more modern way of storing local data. You should use DataStore instead of SharedPreferences. Read the DataStore guide for more information. 이 페이지는 Cloud Translation API를 통해 번역되었습니다. 공유 저장소 미디

developer.android.com

공식문서

추가적으로 API 33이전의 API를 사용하면 위에 사진 맨 밑줄처럼 EXTERNAL_STORAGE도 함께 허용해야 한다.

밑에 사진 설명

 

 

[Android/Kotlin] 갤러리 접근 권한(Premission) 설정하기 - 커스텀 갤러리(1)

안드로이드 앱을 개발하다 보면 특정 권한을 얻어야지만 사용이 가능한 기능들이 있습니다. Android6.0 (API 수준 23) 마쉬멜로우 이전 버전은 메니페스트에 권한을 넣어주면 사용이 가능했지만, 마

tg-world.tistory.com

위에 블로그 내용을 참고했습니다.

 

 

 

5_2) 사진 가져오기

 위에 공식문서 밑에 좋은 도구가 있다.

 

사진 선택 도구  |  Android Developers

DataStore offers a more modern way of storing local data. You should use DataStore instead of SharedPreferences. Read the DataStore guide for more information. 이 페이지는 Cloud Translation API를 통해 번역되었습니다. 사진 선택 도구 컬

developer.android.com

 

출처: 공식문서

위에처럼 나오게 사용할 것이다.

공식문서

>>그냥 복붙

import해주니 빨간 부분은 사라졌다. ActivityResultContracts에서 가져오나보다.

import androidx.activity.result.contract.ActivityResultContracts

 

pickMedia 가져온 부분 밑에보면 '단일 미디어 항목 선택'과 '여러 미디어 항목 선택'이 있는데 사진 한장만 올릴 생각이라서 단일 미디어의 ImageOnly를 사용했다.

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentWriteBinding.bind(view)
			
        //추가
        pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
    }
}

 

전체코드

더보기
package com.example.kotlin_sai.ui.article

import android.os.Bundle
import android.util.Log
import android.view.View
import androidx.activity.result.PickVisualMediaRequest
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import com.example.kotlin_sai.R
import com.example.kotlin_sai.databinding.FragmentWriteBinding

class WriteFragment:Fragment(R.layout.fragment_write) {

    private lateinit var binding:FragmentWriteBinding

    val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        // Callback is invoked after the user selects a media item or closes the
        // photo picker.
        if (uri != null) {
            Log.d("PhotoPicker", "Selected URI: $uri")
        } else {
            Log.d("PhotoPicker", "No media selected")
        }
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding = FragmentWriteBinding.bind(view)

        pickMedia.launch(PickVisualMediaRequest(ActivityResultContracts.PickVisualMedia.ImageOnly))
    }
}

이제 실행하면 끝

 

avd에 사진이 없어서 초록이 사진 찍어서 사진 추가했다.

앗 두장은 눈감았네

 

사진을 누르면

Logcat에 로그가 잘 찍힌다.

갤러리의 이미지를 눌렀을 때 uri가 잘 나온다는 뜻은 그 이미지uri 주소만 잘 연결시켜주면 쉽게 이미지를 가져올 수 있다는 뜻?!

먼저, uri를 저장할 변수를 생성한다.

import android.net.Uri

class WriteFragment 
...생략
    private val seletedUri: Uri? = null

 

위에서 생성한 pickMedia uri를 seletedUri로 변경

private val pickMedia = registerForActivityResult(ActivityResultContracts.PickVisualMedia()) { uri ->
        if (uri != null) { //이미지가 선택되면 이미지uri가 null이 아님
            //selectedImageView에 이미지 띄우기
            selectedUri = uri //추가
            binding.selectedImageView.setImageURI(uri)
        }

 

이제 게시물을 올렸을 때가 필요하다.

binding.uploadTextView.setOnClickListener { //submitButton +버튼 눌렀을 때
            if (selectedUri != null) {
                val fileName = "${UUID.randomUUID()}.png"
                Firebase.storage.reference.child("articles/photo").child(fileName)
                    .putFile(selectedUri ?: return@setOnClickListener)
                    .addOnCompleteListener{
                        if(it.isSuccessful){
                            Log.e("aa","success")
                        }else{
                            it.exception?.printStackTrace()
                        }
                    }
            } else {
                Snackbar.make(view, "이미지를 선택해주세요.", Snackbar.LENGTH_SHORT).show()
            }
        }

 

한 줄씩 분해

더보기
val fileName = "${UUID.randomUUID()}.png"

 firebase의 storage에 생기는 id를 혹시라도 겹칠까봐 랜덤하게 생성해서 .png를 붙인채 저장한다.

Firebase.storage.reference.child("articles/photo").child(fileName)

 이제 Firebase의 storage에 articles파일 아래 photo파일안에 위에 생성한 fileName을 올려준다.

.putFile(selectedUri ?: return@setOnClickListener)

 null값의 예외처리

.addOnCompleteListener{
	if(it.isSuccessful){
		Log.e("upload","success!!!!")
	}
}

 올리는 것에 성공하게된다면 로그를 찍어보기위해 만듬

else {
	Snackbar.make(view, "이미지를 선택해주세요.", Snackbar.LENGTH_SHORT).show()
}

 이미지 값이 null 선택되지 않았다면 Snackbar 띄우기

Firebase의 Storage에 가보면 규칙에 체크한 부분이 false로 되어있다. 권한설정을 저번에 말했는데 로그인한 유저(인증한 유저)로 바꾸면 좋지만 일단 확인하기 위해 true로 바꾼다.

사진을 선택한 뒤 게시물 등록 버튼을 누르면

 

Storage에 잘 올라온다.

 

 

하지만 이제 시작이다..

올린 사진의 저장소 위치 상태가 ??

이렇게 되면 Android에서 주소를 받아오기 힘들다

 

 

 

        binding.uploadTextView.setOnClickListener { //submitButton +버튼 눌렀을 때
            if (selectedUri != null) {
                val fileName = "${UUID.randomUUID()}.png"
                Firebase.storage.reference.child("articles/photo").child(fileName)
                    .putFile(selectedUri ?: return@setOnClickListener)
                    .addOnCompleteListener{
                        if(it.isSuccessful){
                            Firebase.storage.reference.child("articles/photo").child(fileName)
                                .downloadUrl
                                .addOnSuccessListener {
                                    Log.e("imageUri",it.toString())

                                }.addOnFailureListener {

                                }
                        }else{
                            it.exception?.printStackTrace()
                        }
                    }
            } else {
                Snackbar.make(view, "이미지를 선택해주세요.", Snackbar.LENGTH_SHORT).show()
            }
        }

로그에 uri주소가 잘 나오고

uri를 눌러보면 내가 넣은 눈뜬 내 셀카가 잘 나온다.

 

 

 

기능 정리를 어떻게 할까하다가 layout에 버튼들을 가지고 setOnclickListener을 전부 추가할 것이라서 표로 나타냈다.

setOnClickListener 구현



간략히 설명하자면 지난번 글의 navigation에 다시 homeFragment로 돌아가는 action 추가/이어주기 
 아까 등록한 action으로 페이지 이동 / 위에서 설명한 게시물 등록

선택된 이미지, uri를 지워야한다.

처음 들어왔을 때 선택하지 않거나 선택한 이미지를 지웠을 경우에 볼 수 있는 버튼이기 때문에 if문으로 이미지가 없다는 조건하에 다시 이미지를 선택할 수 있게 해준다.

밑에 메서드로 선언한 이유는 시작했을 때도 나와야하고 +버튼을 눌렀을 경우에도 뜨기 때문에 코드를 중복할 필요없이 메서드 호출로 코드 중복을 최소화하기 위함이다.
게시물은 다음 포스팅

 

 

 

 

 

 

 

 

+) navigation 기능인 Animations 활용하기

 

  • enterAnim : action으로 이동해서 새로운 화면이 띄어질 때
  • exitAnim : action으로 이동해서 원래있던 화면이 exit할 때
  • popEnterAnim : 원래 화면으로 돌아갈 때의 원래 화면의 animation
  • popExitAnim : 새로운 화면이 exit할 때의 animation

 

 

 

 

 

 

 

 

 

 

 

 

 

 

ref


 

[Kotlin] Fragment Navigation 화면 전환 방법

Fragment Navigation 이란? Android Jetpack의 Navigation Component를 사용하여 페이지 이동을 쉽게 구현하게 해주는 방법이다. * 공식 문서 * https://developer.android.com/guide/navigation/navigation-getting-started 1. Navigation

cocococo.tistory.com

 

 

[Android/Kotlin] 갤러리 접근 권한(Premission) 설정하기 - 커스텀 갤러리(1)

안드로이드 앱을 개발하다 보면 특정 권한을 얻어야지만 사용이 가능한 기능들이 있습니다. Android6.0 (API 수준 23) 마쉬멜로우 이전 버전은 메니페스트에 권한을 넣어주면 사용이 가능했지만, 마

tg-world.tistory.com