->내가 만들어야하는 화면 화면
myprofilefragment에서 공유버튼 클릭
-> 공유하기 다이얼로그프래그먼트 생성되고 이미지로 저장 버튼 클릭
-> frontprofilefragment의 레이아웃 뷰 이미지로 저장
세개의 프래그먼트간의 통신을 해야하기 때문에 인터페이스를 이용하기에는 너무 복잡함
따라서 ViewModel을 사용해서 프래그먼트 간의 데이터 전달을 하기로 함
ViewModel
SharedViewModel은 여러 컴포넌트 간 데이터를 공유하기 위한 ViewModel
이 ViewModel은 뷰 간에 데이터를 전달하고 저장하는 데 사용
//뷰모델 상속 받음-> 뷰모델 속성 이용 가능
class SharedViewModel : ViewModel(){
// 프로필 레이아웃을 저장하기 위한 MutableLiveData
//Mutable : 변경가능
val profileLayoutLiveData = MutableLiveData<View>()
// 비트맵 이미지를 저장할지 여부를 결정하기 위한 MutableLiveData
val storeBitmap = MutableLiveData<Boolean>()
// 저장된 이미지의 Uri를 저장하기 위한 MutableLiveData
val savedImageUri = MutableLiveData<Uri?>()
//프로필 레이아웃을 저장하기 위한 메서드.
// @param profileLayout 저장할 프로필 레이아웃(View)
fun storeProfileLayout(profileLayout: View) {
profileLayoutLiveData.value = profileLayout
setStoreBitmap(true)
}
// storeBitmap 값을 설정하기 위한 메서드.
// @param value 저장 여부를 나타내는 Boolean 값
fun setStoreBitmap(value: Boolean) {
storeBitmap.value = value
}
// 저장된 이미지의 Uri를 설정하기 위한 메서드.
// @param uri 저장된 이미지의 Uri
fun setSavedImageUri(uri: Uri) {
savedImageUri.value = uri
}
// 저장된 이미지의 Uri를 가져오기 위한 메서드.
// @return 저장된 이미지의 Uri
fun getSavedImageUri(): Uri? {
return savedImageUri.value
}
}
공유하기 버튼 클릭 -> 공유하기 다이얼로그
//sharedViewModel을 나주에 초기화
private lateinit var sharedViewModel: SharedViewModel
//onViewCreated에서 뷰를 생성하고 실행되는 onViewCreated함수
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//SharedViewModel을 초기화하고 앞에서 만든 SharedViewModel클래스 얻어옴
sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
//공유 버튼 클릭 시 이벤트 발생
binding.myprofileShareBtn.setOnClickListener {
//다이얼로그 생성
val bottomSheet2 = BottomSheet2()
//다이얼로그 화면에 표시
bottomSheet2.show(childFragmentManager, bottomSheet2.tag)
}
}
profileLayout을 가져와 비트맵으로 저장하는 코드
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//SharedViewModel 초기화
sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
// SharedViewModel에 프로필 레이아웃 저장
sharedViewModel.storeProfileLayout(binding.profileLayout)
// 프로필 레이아웃의 변경을 감지(observe)하여 비트맵으로 저장하는 로직 수행
sharedViewModel.profileLayoutLiveData.observe(viewLifecycleOwner) {
// storeBitmap 값이 true인 경우에만 로직 실행
if (sharedViewModel.storeBitmap.value == true) {
// 프로필 레이아웃을 비트맵으로 저장하는 로직 수행
val bitmap = getViewBitmap(it)
//파일 경로 생성
val filePath = getSaveFilePathName()
//비트맵을 파일로 저장
bitmapFileSave(bitmap, filePath)
Log.d("bitmap", "success")
}
}
}
//view를 비트맵으로 변환
private fun getViewBitmap(view: View): Bitmap {
// View의 크기를 측정
view.measure(
View.MeasureSpec.makeMeasureSpec(view.width, View.MeasureSpec.EXACTLY),
View.MeasureSpec.makeMeasureSpec(view.height, View.MeasureSpec.EXACTLY)
)
val width = view.measuredWidth
val height = view.measuredHeight
// 측정된 폭과 높이가 0일 경우 처리
if (width <= 0 || height <= 0) {
Log.e("FrontProfileFragment", "Invalid view size: width=$width, height=$height")
// 너비와 높이가 0일 경우에 대한 예외 처리를 추가하거나 기본값으로 처리
// 예: width = defaultWidth, height = defaultHeight
return Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888)
}
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val canvas = Canvas(bitmap)
view.draw(canvas)
Log.d("FrontProfileFragment", "Bitmap created successfully: width=$width, height=$height")
//생성된 비트맵 반환
return bitmap
}
//파일 저장 경로 생성
// 그냥 파일을 저장하는 경로만 생성하는 것이기 때문에 매개변수를 받지 않음
private fun getSaveFilePathName() : String{
val folder = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.toString()
val fileName = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
return "$folder/$fileName.jpg"
}
//비트맵을 파일로 저장(JPEG파일로)
//매개변수로 비트맵과 파일경로를 받음
private fun bitmapFileSave(bitmap: Bitmap, path: String) {
//FileOutputStream : 파일에 바이트를 쓰기 위한 스트림을 제공하는 Java 클래스
//FileOutputStream 인스턴스 생성
val fos: FileOutputStream
try {
// 파일에 대한 FileOutputStream 열기
fos = FileOutputStream(File(path))
//bitmap.compress 메서드를 사용하여 비트맵을 압축된 JPEG 형식으로 스트림에 쓰기
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
//스트림 닫기
fos.close()
} catch (e: IOException) {
// IOException이 발생한 경우 예외 처리
e.printStackTrace()
}
}
BottomSheet2 프래그먼트가 표시될 때, 이미지를 저장하고 해당 이미지의 파일 경로를 savedImageUri에 저장
private lateinit var sharedViewModel: SharedViewModel
//
private var savedImageUri: Uri? = null
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// SharedViewModel 초기화
sharedViewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
// 이미지 저장 버튼 클릭 이벤트 처리
binding.shareBottomSheet2ImageBtn.setOnClickListener {
// profileLayoutLiveData에서 현재 프로필 레이아웃을 가져옴
//profileLayout이 널이 아닌 경우 viewSave()실행, 널인 경우 로그 실행
//profileLayout = sharedViewModel.profileLayoutLiveData.value
sharedViewModel.profileLayoutLiveData.value?.let { profileLayout ->
// 현재 프로필 레이아웃을 이미지로 저장하고, 저장된 이미지의 Uri를 얻어옴
savedImageUri = viewSave(profileLayout)
Log.d("bottomclick", "Image saved successfully")
} ?: run {
Log.d("bottomclick", "profileLayout is null")
}
dismiss() // BottomSheet2를 닫음
}
}
// 뷰를 비트맵 이미지로 변환
private fun viewSave(view: View): Uri {
// 뷰에서 비트맵 이미지 생성
val bitmap = getViewBitmap(view)
// 이미지 파일 경로 생성
val filePath = getSaveFilePathName()
// 비트맵 이미지를 파일로 저장하고, 저장된 파일의 경로를 반환
bitmapFileSave(bitmap, filePath)
return Uri.fromFile(File(filePath))
}
// 뷰를 비트맵 이미지로 변환하는 메서드
private fun getViewBitmap(view: View): Bitmap {
//createBitmap : 비트맵 생성
val bitmap = Bitmap.createBitmap(
view.measuredWidth, view.measuredHeight, Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
view.draw(canvas)
//비트맵으로 변환된 이미지 반환
return bitmap
}
// 이미지 파일 저장 경로 생성
private fun getSaveFilePathName(): String {
val folder =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
.toString()
val fileName = SimpleDateFormat("yyyyMMddHHmmss").format(Date())
return "$folder/$fileName.jpg"
}
// 비트맵 이미지를 파일로 저장하는 메서드
private fun bitmapFileSave(bitmap: Bitmap, path: String) {
val fos: FileOutputStream
try {
// 파일 출력 스트림 열기
fos = FileOutputStream(File(path))
// 비트맵 이미지를 JPEG 형식으로 압축하여 파일에 쓰기
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos)
// 파일 출력 스트림 닫기
fos.close()
} catch (e: IOException) {
// IOException이 발생하면 스택 트레이스 출력
e.printStackTrace()
}
}