前几天我们做完layout後,我们发现如果透过让view显示or不显示的方式,会让我们的整个布局稍显难看! 那怎麽办呢? 我们可以选择用其它Layout例如LinearLayout,但是我们就是任性,已经写完的code,我们就不要改 (打自己嘴吧,前面狂改超多),那我们这边就可以用动态的方式修改我们的layout啦
依旧使用昨天的imagePicker!
我们要把画面修得更漂亮,当今天使用者从Gallery选择照片後,我们就要给它先显示在我们的画面上,让使用者确定要传的照片是否正确,不然原本要传狗狗的照片,结果传到你在吃的咖哩饭就尴尬了~
我们把原本在这边上传到storage的步骤,改到後面使用者按上送出後,我们才上传~ 稍微有点不同的是,我们因为file型态的Uri,每次从getFileExtension回传的值都是null,所以我们这边要用抓字串的方式,来拿到我们的type
原本的getType
fun getFileExtension(activity: Activity, uri: Uri): String?{
return MimeTypeMap.getSingleton().getExtensionFromMimeType(activity.contentResolver.getType(uri))
}
改成透过String来抓後面的字串
private var selectedUri: Uri? = null
private lateinit var type: String
private val resultLauncher = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { uri ->
if (uri.resultCode == Activity.RESULT_OK) {
selectedUri = uri.data?.data
selectedUri?.let { it ->
Constant.loadPetImage(it, binding.ivChatRoomSelectedPhoto)
val filePath = selectedUri?.path
filePath?.substring(filePath?.lastIndexOf(".") + 1) ?.let {
type = it
}
//把我们的照片隐藏
binding.btnCamera.visibility = View.GONE
//显示照片的imageView
binding.ivChatRoomSelectedPhoto.visibility = View.VISIBLE
//在有照片的情形,我们不让使用者打字
binding.edChatRoomInputMessage.apply {
setText(resources.getString(R.string.already_selected_photo))
isEnabled = false
}
}
}
}
★这边我们的imageview,ivChatRoomSelectedPhoto等等会新增
再过来我们要开启imagePicker
这边基本上跟昨天一样,我们要压缩+裁减
binding.btnCamera.setOnClickListener{
ImagePicker.with(this)
.crop()
.compress(1024)
.createIntent { intent ->
resultLauncher.launch(intent)
}
}
我们这边在Fragment拿到资料後,我们来判断是否 selectedUri为null,如果是null,就代表我们使用者是传message,那我们就直接传啦~ 那如果我们的selectedUri不是null,那我们就要上传照片,并且把message的资讯跟uri都传进去saveImageToFireStorage()
private fun sendMessageAndSaveLastMessage() {
var message :Message
if (chatViewModel.fromDetail.value == true){
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,
)
}else{
message = Message(
user_name = accountViewModel.userDetail.value!!.name,
message = binding.edChatRoomInputMessage.text.toString().trim(),
send_user_id = accountViewModel.userDetail.value!!.id,
send_user_image = accountViewModel.userDetail.value!!.image,
send_user_name = accountViewModel.userDetail.value!!.name,
accept_user_name = accountViewModel.selectedUserDetail.value!!.name,
accept_user_image = accountViewModel.selectedUserDetail.value!!.image,
accept_user_id = accountViewModel.selectedUserDetail.value!!.id,
time = ServerValue.TIMESTAMP,
)
}
if (selectedUri == null){
chatViewModel.sendMessage(message)
chatViewModel.saveLastMessage(message)
}else{
chatViewModel.saveImageToFireStorage(type, selectedUri!!,message)
//别忘了要把uri改成null喔,不然下次又回传送一样的照片
selectedUri = null
//回覆原本的UI
binding.btnCamera.visibility = View.VISIBLE
binding.ivChatRoomSelectedPhoto.visibility = View.GONE
binding.edChatRoomInputMessage.isEnabled = true
}
binding.edChatRoomInputMessage.setText("")
}
跟之前一样,当我们照片传送成功的时候,我们就呼叫 sendMessage()跟saveLastMessage()这两个funtion,也别忘记,我们拿到的Uri要转成String喔
private val _imageFail = MutableLiveData<String>()
val imageFail: LiveData<String>
get() = _imageFail
fun saveImageToFireStorage(type: String, uri: Uri,message: Message) {
val sdf: StorageReference = FirebaseStorage.getInstance().reference.child(
Constant.CHAT_IMAGE + "_" + System.currentTimeMillis() + "_" + type
)
sdf.putFile(uri)
.addOnSuccessListener { it ->
it.metadata?.reference?.downloadUrl
?.addOnSuccessListener { Uri ->
val newMessage = Message(
user_name = message.user_name,
message = message.message,
send_user_id = message.send_user_id,
send_user_image = message.send_user_image,
send_user_name = message.send_user_name,
accept_user_name = message.accept_user_name,
accept_user_image = message.accept_user_image,
accept_user_id = message.accept_user_id,
time = ServerValue.TIMESTAMP,
image = Uri.toString()
)
sendMessage(newMessage)
saveLastMessage(newMessage)
}
?.addOnFailureListener {
_image_state.postValue(it.toString())
}
}
.addOnFailureListener {
_image_state.postValue(it.toString())
}
}
fun resetImageFail(){
_imageFail.postValue(null)
}
然後,我们还要去UI来观测它
chatViewModel.imageFail.observe(viewLifecycleOwner, Observer {
showSnackBar(it,true)
chatViewModel.resetImageFail()
})
显示AlertDialog,并且若取消,则回覆原本的UI
private fun setAlertDialog(){
val builder = AlertDialog.Builder(requireContext())
builder.apply {
setTitle("取消选取")
setPositiveButton("取消选取",object : DialogInterface.OnClickListener{
override fun onClick(dialog: DialogInterface?, which: Int) {
binding.btnCamera.visibility = View.VISIBLE
binding.ivChatRoomSelectedPhoto.visibility = View.GONE
binding.edChatRoomInputMessage.isEnabled = true
binding.edChatRoomInputMessage.setText("")
selectedUri = null
dialog?.dismiss()
}
})
setNegativeButton("让我再想一下",object : DialogInterface.OnClickListener{
override fun onClick(dialog: DialogInterface?, which: Int) {
dialog?.dismiss()
}
})
}
val alertDialog = builder.create()
alertDialog.show()
}
然後在onClick呼叫它即可!
binding.ivChatRoomSelectedPhoto.setOnClickListener {
setAlertDialog()
}
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/chat_room_background"
tools:context=".ui.fragment.ChatRoomFragment">
<androidx.appcompat.widget.Toolbar
android:id="@+id/toolbar_chat_room_fragment"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="@color/light_pewter_blue"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<TextView
android:id="@+id/tv_chat_room_accept_user_name"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:textColor="@color/white"
android:textSize="@dimen/toolbar_textSize"
android:textStyle="bold">
</TextView>
</androidx.appcompat.widget.Toolbar>
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_chat_room"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintBottom_toTopOf="@id/ll_chat_room_ed_word"
app:layout_constraintTop_toBottomOf="@id/toolbar_chat_room_fragment">
</androidx.recyclerview.widget.RecyclerView>
<LinearLayout
android:id="@+id/ll_chat_room_ed_word"
android:layout_width="match_parent"
android:layout_height="45dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
android:orientation="horizontal"
android:gravity="start"
android:background="@color/white">
<com.google.android.material.textfield.TextInputLayout
android:layout_width="0dp"
android:layout_weight="7"
android:layout_gravity="bottom"
android:layout_height="wrap_content">
<com.example.petsmatchingapp.utils.JFEditText
android:id="@+id/ed_chat_room_input_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:paddingTop="3dp"
android:background="@drawable/chat_edit_background">
</com.example.petsmatchingapp.utils.JFEditText>
</com.google.android.material.textfield.TextInputLayout>
<ImageButton
android:id="@+id/btn_camera"
android:layout_weight="1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:backgroundTint="@color/white"
android:src="@drawable/ic_baseline_insert_photo_24">
</ImageButton>
<ImageView
android:id="@+id/iv_chat_room_selected_photo"
android:layout_weight="1"
android:visibility="gone"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<ImageButton
android:id="@+id/btn_send"
android:layout_weight="1"
android:layout_width="wrap_content"
android:backgroundTint="@color/white"
android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_send_24">
</ImageButton>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="message"
type="com.example.petsmatchingapp.model.Message" />
</data>
<RelativeLayout
android:id="@+id/rl_message_item_list_layout"
android:padding="5dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_item_message_me_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toStartOf="@id/tv_item_message_me_message"
android:layout_alignBottom="@id/tv_item_message_me_message"
android:layout_alignWithParentIfMissing="false"
android:layout_marginEnd="5dp"
android:textSize="12sp"
tools:text = "13:00">
</com.example.petsmatchingapp.utils.JFTextView>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/tv_item_message_me_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{message.message}"
android:layout_alignParentEnd="true"
android:padding="5dp"
android:textSize="16sp"
android:maxWidth="250dp"
android:background="@drawable/message_background_me"
tools:text = "你今天吃饱了吗?">
</com.example.petsmatchingapp.utils.JFTextView>
<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>
</RelativeLayout>
</layout>
<?xml version="1.0" encoding="utf-8"?>
<layout 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">
<data>
<variable
name="message"
type="com.example.petsmatchingapp.model.Message" />
</data>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dp">
<ImageView
android:id="@+id/iv_item_message_other_image"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentStart="true"
tools:src = "@drawable/icon_dog"/>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/item_message_other_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="5dp"
android:text="@{message.message}"
android:padding="5dp"
android:maxWidth="250dp"
android:textSize="16sp"
android:layout_toEndOf="@id/iv_item_message_other_image"
android:layout_alignTop="@id/iv_item_message_other_image"
android:background="@drawable/message_backgorund_other"
tools:text = "今天我吃饱了喔,我想应该也还没吃饱啦"/>
<ImageView
android:id="@+id/iv_list_item_other_image"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_marginStart="5dp"
android:padding="5dp"
android:visibility="gone"
android:layout_toEndOf="@id/iv_item_message_other_image"
android:layout_alignTop="@id/iv_item_message_other_image"/>
<com.example.petsmatchingapp.utils.JFTextView
android:id="@+id/item_message_other_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@id/item_message_other_message"
android:layout_alignBottom="@id/item_message_other_message"
android:layout_marginStart="5dp"
tools:text="03:05 pm"
android:textSize="12sp"
app:layout_constraintStart_toEndOf="@id/item_message_other_message">
</com.example.petsmatchingapp.utils.JFTextView>
</RelativeLayout>
</layout>
这时候,机智的朋友们就发现啦,如果今天我们的message的textView我们让它gone的话,那原本依靠它的time的textView不就没办法抓相对位置了吗!!
这时候,我们就可以透过动态的方式来修改啦!
我们直接在adapter来修改布局,因为我们可以在adapter的viewHolder来判断我们的资料是否有包含image,有的话就修改布局
来到 ChatRoomAdapter的MyMessageViewHolder,我们直接新增
if (item.image != null) {
//先拿到view的布局
val time = RelativeLayout.LayoutParams(binding.tvItemMessageMeTime.layoutParams)
//把原本的start_of的属性拿掉
time.removeRule(RelativeLayout.START_OF)
//新增属性,第一个参数是属性,第二个填要对齐的Id
time.addRule(RelativeLayout.START_OF, binding.ivItemListMe.id)
time.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, binding.ivItemListMe.id)
//在把布局赋予给view即可
binding.tvItemMessageMeTime.layoutParams = time
}
这样就可以啦!! 简单粗暴
而Other的一样是
if (item.image != null){
val time = RelativeLayout.LayoutParams(binding.itemMessageOtherTime.layoutParams)
time.removeRule(RelativeLayout.END_OF)
time.addRule(RelativeLayout.END_OF,binding.ivListItemOtherImage.id)
time.addRule(RelativeLayout.ALIGN_BOTTOM,binding.ivListItemOtherImage.id)
binding.itemMessageOtherTime.layoutParams = time
}
成品如下!!
太棒啦!!!!!!!!
我们的30天挑战赛完成了,跟到这一步的您,应该对於Firebase资料库的使用有一点点的收获了吧! 而接下来的时间内,我也会来修正它,修正完就会上架它啦!! 也希望有看到这篇文章的小夥伴们,能在这条路上顺顺利利,工作/家庭/爱情三得意~
隐私三宝包含了隐私条款、服务条款、Cookie policy,其中隐私条款若要自己从无到有生出来,似...
将 Arduino Uno 插上电脑後,如果顺利的话作业系统会自动安装「USB 转 COM 晶片」之...
tags: 铁人赛 AWS Outposts ECS 参考资料 ECS on Outposts 限制...
MyBatis 可以简单的使用注解或XML 的方式进行配置和对映,通过将引数对映到配置的SQL 形成...
前面提到了物件、阵列、DOM元素的选取、事件监听,以及最後的localStorage。这些足够我们...