본문 바로가기
Android

Kotlin, FireBase 채팅어플 만들기 -7- [프로필 변경_fragment]

by LasBe 2021. 9. 9.
반응형

Kotlin, FireBase 채팅어플 만들기 -1- [Splash] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -2- [Login] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -3- [Sign Up] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -4- [Bottom Navigation] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -5- [친구창_fragment] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -6- [채팅 리스트_fragment] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -7- [프로필 변경_fragment] (tistory.com)

Kotlin, FireBase 채팅어플 만들기 -8- [채팅창_activity] (tistory.com)

 

[전체 코드 깃허브 주소]

LasBe-code/LasbeTalk (github.com)

 

[참고]

하울의 코딩 채널 - YouTube

 

GitHub - LasBe-code/LasbeTalk

Contribute to LasBe-code/LasbeTalk development by creating an account on GitHub.

github.com


[XML]


<fragment_profile.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"
    >

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <ImageView
            android:id="@+id/profile_imageview"
            android:layout_width="200dp"
            android:layout_height="200dp"
            android:layout_marginBottom="8dp"
            app:layout_constraintBottom_toTopOf="@+id/profile_textview_email"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <TextView
            android:id="@+id/profile_textview_email"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="8dp"
            android:text="TextView"
            android:textColor="#505050"
            app:layout_constraintBottom_toTopOf="@+id/profile_textview_name"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/profile_imageview" />

        <EditText
            android:id="@+id/profile_textview_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="32dp"
            android:gravity="center"
            android:inputType="textPersonName"
            android:text="Name"
            android:textSize="30sp"
            android:textStyle="bold"
            app:layout_constraintBottom_toTopOf="@+id/profile_button"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/profile_textview_email" />

        <Button
            android:id="@+id/profile_button"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="16dp"
            android:text="Change"
            android:textColor="#FFFFFF"
            app:backgroundTint="#FFFF9800"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.501"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toBottomOf="@+id/profile_textview_name" />
    </androidx.constraintlayout.widget.ConstraintLayout>

</androidx.constraintlayout.widget.ConstraintLayout>

 

비율을 위해 레이아웃으로 한번 묶어준 후 heigth를 wrap_content로 설정해주었다

 

ImageView는 변경 가능한 프로필사진

TextView는 변경 불가능한 이메일

EditText는 변경 가능한 이름

 


반응형

 

[액티비티 or 프래그먼트]


class ProfileFragment : Fragment() {
    companion object{
        private var imageUri : Uri? = null
        private val fireStorage = FirebaseStorage.getInstance().reference
        private val fireDatabase = FirebaseDatabase.getInstance().reference
        private val user = Firebase.auth.currentUser
        private val uid = user?.uid.toString()
                fun newInstance() : ProfileFragment {
            return ProfileFragment()
        }
    }

    //메모리에 올라갔을 때
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
    }

    //프레그먼트를 포함하고 있는 액티비티에 붙었을 때
    override fun onAttach(context: Context) {
        super.onAttach(context)
    }
    private val getContent =
            registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
                if(result.resultCode == AppCompatActivity.RESULT_OK) {
                    imageUri = result.data?.data //이미지 경로 원본
                    profile_imageview.setImageURI(imageUri) //이미지 뷰를 바꿈

                    //기존 사진을 삭제 후 새로운 사진을 등록
                    fireStorage.child("userImages/$uid/photo").delete().addOnSuccessListener {
                        fireStorage.child("userImages/$uid/photo").putFile(imageUri!!).addOnSuccessListener {
                            fireStorage.child("userImages/$uid/photo").downloadUrl.addOnSuccessListener {
                                val photoUri : Uri = it
                                println("$photoUri")
                                fireDatabase.child("users/$uid/profileImageUrl").setValue(photoUri.toString())
                                Toast.makeText(requireContext(), "프로필사진이 변경되었습니다.", Toast.LENGTH_SHORT).show()
                            }
                        }
                    }
                    Log.d("이미지", "성공")
                }
                else{
                    Log.d("이미지", "실패")
                }
            }
    //뷰가 생성되었을 때
    //프레그먼트와 레이아웃을 연결시켜주는 부분
    override fun onCreateView(

            inflater: LayoutInflater,
            container: ViewGroup?,
            savedInstanceState: Bundle?
    ): View? {
        //view 선언을 안하고 return에 바로 적용시키면 glide가 작동을 안함
        val view = inflater.inflate(R.layout.fragment_profile, container, false)
        val photo = view?.findViewById<ImageView>(R.id.profile_imageview)

        val email = view?.findViewById<TextView>(R.id.profile_textview_email)
        val name = view?.findViewById<TextView>(R.id.profile_textview_name)
        val button = view?.findViewById<Button>(R.id.profile_button)

        //프로필 구현
        fireDatabase.child("users").child(uid).addListenerForSingleValueEvent(object : ValueEventListener {
            override fun onCancelled(error: DatabaseError) {
            }
            override fun onDataChange(snapshot: DataSnapshot) {
                val userProfile = snapshot.getValue<Friend>()
                println(userProfile)
                Glide.with(requireContext()).load(userProfile?.profileImageUrl)
                        .apply(RequestOptions().circleCrop())
                        .into(photo!!)
                email?.text = userProfile?.email
                name?.text = userProfile?.name
            }
        })
        //프로필사진 바꾸기
        photo?.setOnClickListener{
            val intentImage = Intent(Intent.ACTION_PICK)
            intentImage.type = MediaStore.Images.Media.CONTENT_TYPE
            getContent.launch(intentImage)
        }
        button?.setOnClickListener{
            if(name?.text!!.isNotEmpty()) {
                fireDatabase.child("users/$uid/name").setValue(name.text.toString())
                name.clearFocus()
                Toast.makeText(requireContext(), "이름이 변경되었습니다.", Toast.LENGTH_SHORT).show()
            }
        }
        return view
    }
}

우선 프로필사진은 갤러리에서 선택하면 바로 변경되도록 적용했다.

이름은 버튼을 누르면 변경이 적용된다.

 

 

회원가입 액티비티와 같이 getContent를 사용하여 이미지 뷰를 클릭했을 때

MediaStore를 이용하여 갤러리를 띄우고

선택한 사진의 ImageUri를 registerForAvtivityResult로 받아온다.

 

회원가입과 다른 점은 getContent 내에서

delete()를 통해 기존에 있던 프로필 사진을 삭제 후 콜백을 받아

선택한 사진을 storage에 올리고 또 콜백을 받아

database에 이미지 url을 변경해준다.

private val getContent =
        registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result: ActivityResult ->
            if(result.resultCode == AppCompatActivity.RESULT_OK) {
                imageUri = result.data?.data //이미지 경로 원본
                profile_imageview.setImageURI(imageUri) //이미지 뷰를 바꿈

                //기존 사진을 삭제 후 새로운 사진을 등록
                fireStorage.child("userImages/$uid/photo").delete().addOnSuccessListener {
                    fireStorage.child("userImages/$uid/photo").putFile(imageUri!!).addOnSuccessListener {
                        fireStorage.child("userImages/$uid/photo").downloadUrl.addOnSuccessListener {
                            val photoUri : Uri = it
                            println("$photoUri")
                            fireDatabase.child("users/$uid/profileImageUrl").setValue(photoUri.toString())
                            Toast.makeText(requireContext(), "프로필사진이 변경되었습니다.", Toast.LENGTH_SHORT).show()
                        }
                    }
                }
                Log.d("이미지", "성공")
            }
            else{
                Log.d("이미지", "실패")
            }
        }
.
.
///onCreateView///

photo?.setOnClickListener{
            val intentImage = Intent(Intent.ACTION_PICK)
            intentImage.type = MediaStore.Images.Media.CONTENT_TYPE
            getContent.launch(intentImage)
        }

 

이름 변경하는 부분은 쉬워서 패스

 

 

가장 애먹었던 부분은 프래그먼트 내에서 glide로 이미지를 불러오는 것이었다.

어댑터에서는 holder.itemView.context에서

액티비티에서는 this로

프래그먼트에서는 requireContext()로 context를 불러올 수 있었다.

 

그리고 view를 직접 선언하지 않고 return문에 inflater를 넣어주면 프로필 프래그먼트를 열 때

glide에서 오류가 나와서 팅기는 문제가 있었다.

 

view를 선언해서 문제는 해결했지만 이유는 잘 모르겠다.

//// 리사이클러뷰 어댑터 ////
Glide.with(holder.itemView.context).load(friend?.profileImageUrl)
        .apply(RequestOptions().circleCrop())
        .into(holder.imageView)
        
        
//// 프래그먼트 ////
Glide.with(requireContext()).load(userProfile?.profileImageUrl)
        .apply(RequestOptions().circleCrop())
        .into(photo!!)
        
        
//// return문에 inflater == glide 오류 ////
return inflater.inflate(R.layout.fragment_profile, container, false)

//// view 선언 == glide 성공 ////
val view = inflater.inflate(R.layout.fragment_profile, container, false)

[작동]


기존
storage에 변경이 잘 적용되었다.

 


[아쉬운 점]


버튼을 눌렀을 때 프로필사진과 이름이 같이 바뀌도록 수정해야겠다.

반응형

댓글


오픈 채팅