iT邦幫忙

2021 iThome 鐵人賽

DAY 27
0
Mobile Development

30天建立寵物約散App-Android新手篇系列 第 27

【day27】聊天室傳送照片

連假結束啦,我們目前聊天主要都是以對話方式呈現,那為了讓我們的使用者可以同時聊天,也可以傳送可愛的照片,那我們就會需要把照片給傳出去,跟大家一起互相分享!

一、修改我們的dataClass

既然我們都要傳照片了,那我們當然要新增照片的欄位啦

data class Message(
    val user_name: String? = null,
    val message: String? = null,
    val time: Any? = null,
    val send_user_id: String? = null,
    val send_user_name: String? = null,
    val accept_user_id: String? = null,
    val accept_user_name: String? = null,
    val accept_user_image: String? = null,
    val send_user_image: String? = null,
	//新增這一行
    val image: String? = null

)

然後我們的LastMessage這邊就不做修改囉,我們就仿照Line一般,當今天有新的照片的時候,就直接說傳送一張照片即可~

二、新增ImageButton讓我們觸發到相簿

先在我們的layout新增以下

https://ithelp.ithome.com.tw/upload/images/20211012/201380170YRayjJMiL.png

<ImageButton
            android:id="@+id/btn_camera"
            android:layout_weight="1"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:src="@drawable/ic_baseline_photo_camera_24">

        </ImageButton>

三、拿到相簿的result

我們這邊就做的比較簡單一點,我們只一次最多就只能傳送一筆圖片,那如果要傳送多張圖片的話,就把我們的dataClass改成list,就可以啦!

我們首先也要在上面聲明啦
我們把拿到副檔名的funtion在fragment就先叫出來,並且丟入viewmodel

private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()){ uri ->
        if (uri.resultCode == Activity.RESULT_OK){
            val selectedUri = uri.data?.data
                selectedUri?.let {
                    Constant.getFileExtension(requireActivity(),it)?.let { it1 ->
                        chatViewModel.saveImageToFireStorage(
                            it1,it)
                    }
                }

            }
        }

這時候我們發現紅字,所以我們就需要來到我們的ChatViewModel,新增啦

這邊我們修改一下寫法,因為之前我們竟然把Activity跟Fragment當成參數傳進去!!,原本想說這樣簡單方便,但是後來經過大大的解說,說有可能因為畫面旋轉,導致fragment的實例是不同的(雖然我們現在在manifest設定不能旋轉),但仍有這種風險,所以我們要把它改成透過livedata的方式,讓fragment來觀察!!

//我們把可下載Uri存在這邊,並且讓fragment來觀察
private val _image = MutableLiveData<Uri>()
val image: LiveData<Uri>
get() = _image

//這邊則是存進去storage失敗的時候,我們也透過livedata的方式存進去資訊
private val _image_state = MutableLiveData<String>()
val image_state: LiveData<String>
get() = _image_state
fun saveImageToFireStorage(type: String, uri: Uri) {


            val sdf: StorageReference = FirebaseStorage.getInstance().reference.child(
                Constant.CHAT_IMAGE + "_" + System.currentTimeMillis() + "_" + type
                )

            sdf.putFile(uri)
                .addOnSuccessListener { it ->
                    it.metadata?.reference?.downloadUrl
                        ?.addOnSuccessListener { Uri ->
                            _image.postValue(Uri)
                        }
                        ?.addOnFailureListener {
                            _image_state.postValue(it.toString())
                        }


                }
                .addOnFailureListener {
                    _image_state.postValue(it.toString())
                }
        }

好的!! 那接下來就是回到fragment來觀察啦!
我們觀察當今天我們觀察到LiveData有資料的時候,我們就要把顯示"已選取一張照片",並且把livedata的值賦予給selectedUri

chatViewModel.image.observe(viewLifecycleOwner, Observer {
            if (it != null){
            binding.edChatRoomInputMessage.setText("已選取一張照片")
             = it.toString()
            }

        })

★基本上我們不用去管使用者會不會再去選擇其他照片,因為只要他再次選擇其它相簿,他就會重新賦予livedata值,我們只要處理當今天使用者按完送出之後,要把livedata改成null,否則他會一直永遠記得之前的照片,直到關掉App或是換新的照片~

好的,接下來就是我們要傳送訊息啦!

我們只要把在從 DetailFrgament跟不是從DetailFragment傳進來的訊息,都加上image,這樣就好啦!
這邊就先貼chatViewModel.fromDetail.value == true 的message

message = Message(
                user_name = accountViewModel.userDetail.value!!.name,
                message = binding.edChatRoomInputMessage.text.toString().trim(),
                send_user_id = accountViewModel.userDetail.value!!.id,
                accept_user_id = matchingViewModel.selectedInvitation.value!!.user_id,
                send_user_image = accountViewModel.userDetail.value!!.image,
                send_user_name = accountViewModel.userDetail.value!!.name,
                time = ServerValue.TIMESTAMP,
                accept_user_image = matchingViewModel.selectedInvitation.value?.user_image,
                accept_user_name = matchingViewModel.selectedInvitation.value?.user_name,
                image = selectedUri
            )

好的! 那接下來,我們還需要做一件事,各位夥伴有沒有想法阿!!?

那就是~~

我們需要reset我們的livedata,否則它就會一直存著之前的照片,好的我們直接在viewmodel新增這這個funtion就好啦!

fun resetImage(){
        _image.postValue(null)
    }

但是.. 這邊有報錯,原本預設是不能賦予livedata null的阿

所以我們這邊要在

gradle的app層級新增以下,這樣就可以啦!

android {

lintOptions {
        disable 'NullSafeMutableLiveData'
    }


}

# 四、顯示畫面
接下來我們需要來修改我們的item_list_layout,並且還有adapter

我們首先都在我們的message_item_list_me 跟message_item_list_other 這兩個xml裡面新增 imageView,並且預設是不顯示的,這邊就給 message_item_list_me 的範例

						<ImageView
                android:id="@+id/iv_item_list_me"
                android:layout_width="80dp"
                android:layout_height="80dp"
                android:layout_alignParentEnd="true"
                android:visibility="gone"
                android:padding="5dp">

            </ImageView>

好的,那接下來就回到adapter,我們需要當今天 image這個欄位不是null的時候,我們要把message的view隱藏,並且叫出imageView

所以我們在onBindViewHolder 修改成以下

override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val model = getItem(position)
        when(holder){
            is  MyMessageViewHolder ->{
                holder.bind(model)
                if(model.image != null){
                    Constant.loadPetImage(model.image,holder.binding.ivItemListMe)
                    holder.itemView.iv_item_list_me.visibility = View.VISIBLE
                    holder.itemView.tv_item_message_me_message.visibility = View.GONE
                }


            }
            is  OtherMessageViewHolder -> {
                holder.bind(model)
                if(model.image != null){
                    Constant.loadPetImage(model.image,holder.binding.ivItemMessageOtherImage)
                    holder.itemView.iv_item_list_me.visibility = View.VISIBLE
                    holder.itemView.item_message_other_message.visibility = View.GONE
                }
            }
        }
    }

好的,這樣就大功告成啦!

成果如下,雖然畫面有點醜,之後有空再修!

day27.finish


上一篇
【day26】Span翻轉TextView
下一篇
【day28】寵物邀約上傳流程修改 X ViewPager2 with indicator
系列文
30天建立寵物約散App-Android新手篇30

尚未有邦友留言

立即登入留言