【day30】chatRoom修改画面 X 动态修正layout

前几天我们做完layout後,我们发现如果透过让view显示or不显示的方式,会让我们的整个布局稍显难看! 那怎麽办呢? 我们可以选择用其它Layout例如LinearLayout,但是我们就是任性,已经写完的code,我们就不要改 (打自己嘴吧,前面狂改超多),那我们这边就可以用动态的方式修改我们的layout啦

一、修改上传流程!

依旧使用昨天的imagePicker!
我们要把画面修得更漂亮,当今天使用者从Gallery选择照片後,我们就要给它先显示在我们的画面上,让使用者确定要传的照片是否正确,不然原本要传狗狗的照片,结果传到你在吃的咖哩饭就尴尬了~

1.修改resultLauncher

我们把原本在这边上传到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)
                }
        }

2.修改sendMessageAndSaveLastMessage()

我们这边在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("")

    }

3.修改saveImageToFireStorage

跟之前一样,当我们照片传送成功的时候,我们就呼叫 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()
        })

4.选完照片後,我们要产生预览

显示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()
        }

二、修改chatRoom的UI

1.修改fragment_chat_room.xml,我们直接贴code,主要是新增预览照片的imageView,并且初始设定是隐藏的

<?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>

2.修改message_item_list_me.xml

<?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>

3.修改message_list_item_other

<?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不就没办法抓相对位置了吗!!

这时候,我们就可以透过动态的方式来修改啦!

4.动态修改布局

我们直接在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

                }

成品如下!!
day30.finish

太棒啦!!!!!!!!
我们的30天挑战赛完成了,跟到这一步的您,应该对於Firebase资料库的使用有一点点的收获了吧! 而接下来的时间内,我也会来修正它,修正完就会上架它啦!! 也希望有看到这篇文章的小夥伴们,能在这条路上顺顺利利,工作/家庭/爱情三得意~


<<:  LeetCode 刷题的只是写好程序的第一哩路

>>:  Day 30 完赛了...然後呢?

Day 4 如何规划拟定隐私三宝

隐私三宝包含了隐私条款、服务条款、Cookie policy,其中隐私条款若要自己从无到有生出来,似...

D03 - Hello Firmata

将 Arduino Uno 插上电脑後,如果顺利的话作业系统会自动安装「USB 转 COM 晶片」之...

【Day 20】ECS on Outposts 的限制

tags: 铁人赛 AWS Outposts ECS 参考资料 ECS on Outposts 限制...

Day 12 - Spring Boot & MyBatis

MyBatis 可以简单的使用注解或XML 的方式进行配置和对映,通过将引数对映到配置的SQL 形成...

最终章:Todo List实作

前面提到了物件、阵列、DOM元素的选取、事件监听,以及最後的localStorage。这些足够我们...