连假结束啦,我们目前聊天主要都是以对话方式呈现,那为了让我们的使用者可以同时聊天,也可以传送可爱的照片,那我们就会需要把照片给传出去,跟大家一起互相分享!
既然我们都要传照片了,那我们当然要新增照片的栏位啦
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一般,当今天有新的照片的时候,就直接说传送一张照片即可~
先在我们的layout新增以下
<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>
我们这边就做的比较简单一点,我们只一次最多就只能传送一笔图片,那如果要传送多张图片的话,就把我们的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
}
}
}
}
好的,这样就大功告成啦!
成果如下,虽然画面有点丑,之後有空再修!
<<: TypeScript | nerver 型别 心得纪录
目的 不再限制一个工厂(物件)建立许多产品(物件),而是转变成一个产品(物件)对应一个工厂(物件),...
font-style属性设定HTML元素的文字样式 normal : 正常(预设值) italic ...
PHP 干古 最早於 1994 由 Rasmus Lerdorf 用 C 语言开发的 CGI 程序,...
甚麽是套件? 所谓的套件(package)其实就是由一堆模组(module)所组成,里面有各种已...
现在已经有许多科幻片出现许多人工智慧,像是《机械公敌》(I,Robot)或是《钢铁人》(Iron M...