【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


<<:  TypeScript | nerver 型别 心得纪录

>>:  Day28_CSS语法11

Day 06: Creational patterns - Factory Method

目的 不再限制一个工厂(物件)建立许多产品(物件),而是转变成一个产品(物件)对应一个工厂(物件),...

Day22_CSS语法5

font-style属性设定HTML元素的文字样式 normal : 正常(预设值) italic ...

来认识 PHP 与 Laravel

PHP 干古 最早於 1994 由 Rasmus Lerdorf 用 C 语言开发的 CGI 程序,...

[day-27] Python-使用套件快速设计程序

甚麽是套件?   所谓的套件(package)其实就是由一堆模组(module)所组成,里面有各种已...

[Day 29] 毁灭人类的人工智慧

现在已经有许多科幻片出现许多人工智慧,像是《机械公敌》(I,Robot)或是《钢铁人》(Iron M...