# 목표
Kotlin을 이용한 안드로이드 어플에서 Firebase Storage로 이미지를 업로드하자 !
1. Firebase 환경 구축
1-1. Firebase 프로젝트 생성
1-2. 프로젝트 콘솔창에서 안드로이드 앱 생성
로컬에서 작업 중인 프로젝트의 패키지명을 입력하고 [앱 등록]
google-services.json 파일을 다운받아서 프로젝트의 app 폴더로 복붙해준다.
이때, 안드로이드 스튜디오에서 좌측 파일 목록 열람 방식을 Android → Project 방식으로 바꿔서 추가하면 된다.
프로젝트 파일 중 build.gradle (Project : ) 파일에 종속성을 추가한다.
프로젝트 파일 중 build.gradle (Module : ) 파일에 종속성을 추가한다.
나는 Firebase 여러 기능 중 Storage를 사용할 것이므로 build.gradle (Module : ) 파일에 아래 종속성도 추가해줬다.
implementation 'com.google.firebase:firebase-storage-ktx'
1-3. 안드로이드 스튜디오 설정
프로젝트로 돌아와서 Tools / Firebase를 클릭하면 우측에 다음 설정창이 생긴다.
여기서 Cloud Storage를 선택하고 (java말고 kotlin 선택) 시키는 대로 4개의 단계를 진행한다. (sync)
2. layout 만들기
[사진 선택] 버튼, 선택한 사진이 보일 이미지뷰를 포함한 layout을 하나 만들어준다.
나는 2개의 Fragment로 [ 사진 업로드 이전 -> 사진 업로드 이후 ] 화면을 다르게 구현하고 싶어서 Fragment용 layout에 만들었다.
그런데 두 Fragment들 가운데 훨씬 중요한 것은 사진 업로드 이전 Fragment이다. 여기서 사진 선택, 선택한 사진 보기, 사진 다시 선택하기 과정이 다 이루어지기 때문이다. (사진 업로드 이후 Fragment는 형식상..)
# fragment_search_before.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
tools:context=".activity.search.SearchBeforeFragment">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="17sp"
android:gravity="center"
android:text="검색하고 싶은 벌레 사진을 첨부해 보세요!"
android:textSize="15sp"
android:textColor="@color/main_grey"/>
<LinearLayout
android:id="@+id/Search_Before_Container_Image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_gravity="center"
android:gravity="center"
android:layout_marginTop="20sp"
android:layout_marginBottom="40sp"
android:background="@drawable/custom_button_white_grey2"
android:orientation="vertical">
<LinearLayout
android:id="@+id/Search_Before_Container_Select"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<ImageButton
android:id="@+id/Search_Before_Btn_Select"
android:layout_width="200sp"
android:layout_height="202sp"
android:padding="10sp"
android:backgroundTint="@color/white"
android:src="@drawable/img_search_upload"
android:scaleType="fitCenter"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="클릭하여 사진 첨부하기"
android:textSize="17sp"
android:textColor="@color/main_grey2"
android:fontFamily="@font/main_font_sb" />
</LinearLayout>
<ImageView
android:id="@+id/Search_Before_Image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:layout_marginTop="20sp"
android:layout_marginBottom="40sp"
android:visibility="gone" />
</LinearLayout>
<LinearLayout
android:id="@+id/Search_Before_Container_Edit"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="5"
android:gravity="bottom"
android:orientation="vertical"
android:visibility="gone">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/Search_Before_Btn_Edit"
android:layout_width="match_parent"
android:layout_height="50sp"
android:background="@drawable/custom_button_white_red"
android:text="다시 선택하기"
android:textSize="20sp"
android:textColor="@color/main_red"
android:fontFamily="@font/main_font_b"/>
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>
Fragment를 얹을 Activity의 layout은 다음과 같다.
# activity_search.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#FFFFFF"
tools:context=".activity.search.SearchActivity">
<androidx.appcompat.widget.Toolbar
android:id="@+id/Toolbar_Search"
android:layout_width="match_parent"
android:layout_height="70dp"
android:background="@color/white"
android:elevation="5sp"
app:titleTextColor="@color/black"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/Toolbar_Search_Text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@color/black"
android:lineSpacingExtra="3.3sp"
android:layout_gravity="center_horizontal"
android:text="이 벌레 뭐지 ?"
android:textSize="25sp"
android:fontFamily="@font/main_font_sb"/>
</androidx.appcompat.widget.Toolbar>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical"
android:layout_marginTop="70dp"
android:paddingHorizontal="30sp">
<FrameLayout
android:id="@+id/Search_MainView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="8"
android:gravity="bottom"
android:paddingBottom="15sp"
android:orientation="vertical">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/Search_Btn"
android:layout_width="match_parent"
android:layout_height="50sp"
android:background="@drawable/custom_button_red"
android:text="검색하기"
android:textSize="20sp"
android:textColor="@color/white"
android:fontFamily="@font/main_font_b" />
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
3. Activity, Fragment 코드 작성하기
나는 Fragment에 있는 [사진 선택] 버튼을 누르면, Activity에 있는 함수를 호출해서 Fragment에 선택한 사진을 보여주게끔 코드를 작성했다.
# SearchBeforeFragment.kt
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageButton
import androidx.appcompat.widget.AppCompatButton
import com.zonebug.debugging.R
class SearchBeforeFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
val view = inflater.inflate(R.layout.fragment_search_before, container, false)
val selectBtn : ImageButton = view.findViewById(R.id.Search_Before_Btn_Select)
val editBtn : AppCompatButton = view.findViewById(R.id.Search_Before_Btn_Edit)
// 이미지 선택하기
selectBtn.setOnClickListener {
(activity as SearchActivity).selectImage()
}
// 이미지 다시 선택하기
editBtn.setOnClickListener {
(activity as SearchActivity).selectImage()
}
return view
}
}
# SearchActivity.kt
import android.app.ProgressDialog
import android.content.Intent
import android.net.Uri
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.Toast
import com.google.firebase.storage.FirebaseStorage
import com.zonebug.debugging.R
import com.zonebug.debugging.databinding.ActivitySearchBinding
import java.text.SimpleDateFormat
import java.util.*
class SearchActivity : AppCompatActivity() {
private lateinit var binding : ActivitySearchBinding
var isBefore : Boolean = true
lateinit var imageURI : Uri
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivitySearchBinding.inflate(layoutInflater)
setContentView(binding.root)
val transaction = supportFragmentManager.beginTransaction()
transaction.add(R.id.Search_MainView, SearchBeforeFragment()).commit()
// 하단 버튼 클릭
binding.SearchBtn.setOnClickListener {
if(isBefore) uploadImage()
isBefore = !isBefore
switchFragment()
}
}
private fun switchFragment() {
val transaction = supportFragmentManager.beginTransaction()
if(isBefore) {
transaction.replace(R.id.Search_MainView, SearchBeforeFragment())
.addToBackStack(null)
.commit()
binding.SearchBtn.text = "검색하기"
} else {
transaction.replace(R.id.Search_MainView, SearchAfterFragment())
.addToBackStack(null)
.commit()
binding.SearchBtn.text = "다른 벌레 검색하기"
}
}
fun selectImage() {
val intent = Intent()
intent.type = "image/*"
intent.action = Intent.ACTION_PICK
startActivityForResult(intent, 100)
}
override fun onActivityResult(requestCode : Int, resultCode : Int, data : Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val selectContainer : LinearLayout = this.findViewById(R.id.Search_Before_Container_Select)
val editContainer : LinearLayout = this.findViewById(R.id.Search_Before_Container_Edit)
val imageView : ImageView = this.findViewById(R.id.Search_Before_Image)
if(requestCode == 100 && resultCode == RESULT_OK) {
imageURI = data?.data!!
imageView.setImageURI(imageURI)
imageView.visibility = View.VISIBLE
editContainer.visibility = View.VISIBLE
selectContainer.visibility = View.GONE
}
}
private fun uploadImage() {
val progressDialog = ProgressDialog(this)
progressDialog.setMessage("업로드중입니다")
progressDialog.setCancelable(false)
progressDialog.show()
val formatter = SimpleDateFormat("yyyy_MM_dd_HH_mm_ss", Locale.getDefault())
val now = Date()
val fileName = formatter.format(now)
val storageReference = FirebaseStorage.getInstance().getReference("Search/$fileName")
val imageView : ImageView = this.findViewById(R.id.Search_Before_Image)
storageReference.putFile(imageURI).addOnSuccessListener {
imageView.setImageURI(null)
Toast.makeText(this@SearchActivity, "업로드 성공", Toast.LENGTH_SHORT).show()
if(progressDialog.isShowing) progressDialog.dismiss()
}.addOnFailureListener {
if(progressDialog.isShowing) progressDialog.dismiss()
Toast.makeText(this@SearchActivity, "업로드 실패", Toast.LENGTH_SHORT).show()
}
}
override fun onBackPressed() {
finish()
}
}
중요한 흐름은 selectImage( ) → onActivityResult( ) → uploadImage( ) 이다.
'활동 일지 > 캡스톤디자인프로젝트' 카테고리의 다른 글
[캡스톤디자인프로젝트] YOLOv5 & Flask (0) | 2023.04.19 |
---|---|
[캡스톤디자인프로젝트 | 그로쓰] Tencent Cloud GPU 서버 접속 (0) | 2023.04.11 |
[캡스톤디자인프로젝트 | 스타트] 라즈베리파이 & 카메라 모듈 개발 환경 설정 (1) | 2022.11.22 |
[캡스톤디자인프로젝트 | 스타트] Chrome 이미지 크롤링 (0) | 2022.11.11 |
[캡스톤디자인프로젝트 | 스타트] Yolo 사용해보기 (0) | 2022.10.11 |