Basics of Android Development - UI Practice

Write a chat interface here.

Make a 9-Patch image

A 9-Patch image is a specially processed png image that can specify which areas can be stretched and which areas cannot.

 For example, if the image above is directly set as the background image:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="50dp"
    android:background="@drawable/message_left_original">

</LinearLayout>

The result of running the program is: 

 It can be seen that since the width of the picture is not enough to fill the width of the entire screen, the whole picture is stretched. This kind of display effect is definitely not possible. At this time, a 9-Patch picture is needed.

Right-click on the picture, select Create 9-Patch file, and save it. After saving, double-click the picture and the following editing interface will appear:

 At this time, you can draw small black dots on the 4 borders of the picture. The part drawn on the upper border and the left border indicates that when the picture needs to be stretched, the area marked by the black dot will be stretched. The part drawn on the lower border and the right border Indicates the area where the content is allowed to be placed. Use the mouse to drag the edge of the picture to draw, and hold down the shift key to erase. After drawing is completed:

 The result of rerunning the program is:

 Write the chat interface

First add dependencies in app/build.gradle:

dependencies {

    implementation 'androidx.core:core-ktx:1.3.2'
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.3.0'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
    implementation 'androidx.recyclerview:recyclerview:1.2.0'
    testImplementation 'junit:junit:4.+'
    androidTestImplementation 'androidx.test.ext:junit:1.1.2'
    androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}

Then modify activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#d8e0e8">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/inputText"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_weight="1"
            android:hint="Type something here"
            android:maxLines="2"
            tools:ignore="Suspicious0dp" />

        <Button
            android:id="@+id/send"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="Send"/>

    </LinearLayout>
</LinearLayout>

In the above code, RecyclerView is used to display chat content, EditText is used to input messages, and Button is used to send messages.

Then define the message entity class and create a new Msg:

class Msg(val content:String, val type:Int) {
    companion object {
        const val TYPE_RECEIVED = 0
        const val TYPE_SENT = 1
    }
}

There are only two fields in the above Msg class, content indicates the message content, type indicates the message type, and the message type has two values: TYPE_RECEIVED indicates the received message, and TYPE_SENT indicates the sent message.

Then write the RecyclerView subitem layout and create a new msg_left_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">
    
    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="left"
        android:background="@drawable/message_left">
    
        <TextView
            android:id="@+id/leftMsg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#fff"/>
        
    </LinearLayout>
</FrameLayout>

Received messages are left-aligned. Similarly, write a subitem layout for sending messages, and create a new msg_right_item.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="10dp">

    <LinearLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="right"
        android:background="@drawable/message_right">

        <TextView
            android:id="@+id/rightMsg"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_gravity="center"
            android:layout_margin="10dp"
            android:textColor="#000"/>

    </LinearLayout>
</FrameLayout>

Sent messages are right-aligned. Then create an adapter class for RecyclerView and create a new class MsgAdapter:

class MsgAdapter(val msgList: List<Msg>):
    RecyclerView.Adapter<RecyclerView.ViewHolder>(){

    inner class LeftViewHolder(view:View):RecyclerView.ViewHolder(view) {
        val leftMsg: TextView = view.findViewById(R.id.leftMsg)
    }

    inner class RightViewHolder(view:View):RecyclerView.ViewHolder(view) {
        val rightMsg: TextView = view.findViewById(R.id.rightMsg)
    }

    override fun getItemViewType(position: Int): Int {
        val msg = msgList[position]
        return msg.type
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = if (viewType == Msg.TYPE_RECEIVED) {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.msg_left_item, parent, false)
        LeftViewHolder(view)
    } else {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.msg_right_item, parent, false)
        RightViewHolder(view)
    }

    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        val msg = msgList[position]
        when (holder) {
            is LeftViewHolder -> holder.leftMsg.text = msg.content
            is RightViewHolder -> holder.rightMsg.text = msg.content
            else -> throw IllegalArgumentException()
        }
    }
    override fun getItemCount() = msgList.size
}

In the above code, different interfaces are created according to different viewTypes. First, two ViewHolders, LeftViewHolder and RightViewHolder, are defined, which are used to cache the controls in the msg_left_item.xml and msg_right_item.xml layouts respectively. Then rewrite the getItemViewType method and return the message type corresponding to the current position.

Then in the onCreateViewHolder method, load different layouts and create different ViewHolders according to different viewTypes, and then judge the type of ViewHolder in the onBindViewHolder method. If it is LeftViewHolder, display the content to the message layout on the left. If it is RightViewHolder, it will The content is displayed to the right of the message layout.

Then modify Activity, initialize data for RecyclerView, and add button click event:

class MainActivity : AppCompatActivity(), View.OnClickListener {

    private val msgList = ArrayList<Msg>()

    private var adapter:MsgAdapter ?= null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        initMsg()
        val layoutManager = LinearLayoutManager(this)
        recyclerView.layoutManager = layoutManager
        adapter = MsgAdapter(msgList)
        recyclerView.adapter = adapter
        send.setOnClickListener(this)
    }

    override fun onClick(v: View?) {
        when (v) {
            send -> {
                val content = inputText.text.toString()
                if (content.isNotEmpty()) {
                    val msg = Msg(content, Msg.TYPE_SENT)
                    msgList.add(msg)
                    adapter?.notifyItemInserted(msgList.size - 1)
                    recyclerView.scrollToPosition(msgList.size - 1)
                    inputText.setText("")
                }
            }
        }
    }

    private fun initMsg() {
        val msg1 = Msg("Hello,guy:", Msg.TYPE_RECEIVED)
        msgList.add(msg1)
        val msg2 = Msg("Hello,who is that?", Msg.TYPE_SENT)
        msgList.add(msg2)
        val msg3 = Msg("This is Tom. Nice to meet you.", Msg.TYPE_RECEIVED)
        msgList.add(msg3)
    }
}

In the above code, several pieces of data are now initialized in the initMsg method for display in RecyclerView, and then RecyclerView is constructed in a standard way, and LayoutManager and adapter are specified for it.

Then the content in the EditText is obtained in the click event of the sending button. If the content is not empty, a new Msg object is created and added to the msgList list, and then the notifyItemInserted method of the adapter is called to notify the list that new data is inserted. Only in this way can the newly added messages be displayed in the RecyclerView. Or you can use the notifyDataSetChanged method to refresh all visible elements, but this method is less efficient. Then call the scrollToPosition method to position the displayed data to the last line, so as to ensure that the last message sent can be seen. Finally, clear the contents of the EditText.

The result after running the program is:

Guess you like

Origin blog.csdn.net/SAKURASANN/article/details/126922921
Recommended