ViewModel_뷰 이미지 저장

쿵ㅇ양·2024년 1월 21일
0

Android

목록 보기
6/30

->내가 만들어야하는 화면 화면

myprofilefragment에서 공유버튼 클릭
-> 공유하기 다이얼로그프래그먼트 생성되고 이미지로 저장 버튼 클릭
-> frontprofilefragment의 레이아웃 뷰 이미지로 저장

세개의 프래그먼트간의 통신을 해야하기 때문에 인터페이스를 이용하기에는 너무 복잡함
따라서 ViewModel을 사용해서 프래그먼트 간의 데이터 전달을 하기로 함

ViewModel

사용법

1.SharedViewModel 클래스 파일 생성

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
    }
}

2. MyProfileFragment.kt

공유하기 버튼 클릭 -> 공유하기 다이얼로그


	//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)
        }

    }

3. FrontProfileFragment.kt

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()
        }
    }

4. BottomSheet2.kt 다이얼로그

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()
    }
}
profile
개발을 공부하고 있는 대학생

0개의 댓글

관련 채용 정보